文章目录
- 图书管理系统
- 1.前言
- 2.项目剖析
- 3.总结
- 4.致谢
图书管理系统
1.前言
为了解决广大用户喜欢在网上看书的问题,本人写了一套图书管理系统,来帮助大家在网络上看书,但是这套系统并不完善,目前还只是一个小程序,等本人的知识积累到一定程度,就可以把这个图书管理系统,写成网页的形式来供大家使用了。
大家先看下面这两个图,来了解一下整个系统功能的使用:
下面这是管理员的权限:
下面这是普通用户的权限:
大家看完这两个图之后,我们具体分析一下,整个项目是怎么实现的。
2.项目剖析
整个项目可以拆分成两个对象,哪两个对象呢?一个就是用户,一个图书,说白了就是对用户的权限加以限制,不同权限的用户,对图书的操作是不同的,比如说管理员他可能就需要,添加,删除一些图书,而普通用户只需要去选择要不要借阅这些图书来看看。下面就是我具体实现的步骤了:
- 那我们第一步其实定义一些实体,来把用户还有图书这些东西给抽象出来,Java是面向对象的,你得把对象给我弄出来是吧!那我们的代码就可以这么写:
创建一个User对象:
//代码就是想到什么,写什么,现在觉得user就只需要个名字,我就写了个名字放在这里
public class User {
protected String name;
public User(String name) {
this.name = name;
}
}
创建一个Book对象:
/**
* 创建一个图书对象,图书应该有书名,作者,价格,类型以及有没有被借出这些属性吧!
* 你看我都写成了private,证明我不想直接让别人访问,我得给一些接口吧!得让别人访问
* 到阿!然后就给了个构造方法,还有get和set方法,还有重写toString方法。
*/
public class Book {
private String name; //书名
private String author; //作者
private int price; // 价格
private String type; //类型
private boolean isBorrowed; // 是否被借出
public Book() {
}
public Book(String name, String author, int price, String type) {
this.name = name;
this.author = author;
this.price = price;
this.type = type;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public boolean isBorrowed() {
return isBorrowed;
}
public void setBorrowed(boolean borrowed) {
isBorrowed = borrowed;
}
@Override
public String toString() {
return "Book{" +
"name='" + name + '\'' +
", author='" + author + '\'' +
", price=" + price +
", type='" + type + '\'' +
", isBorrowed=" + (isBorrowed ? "借出" : "未借出") +
'}';
}
}
- 既然两个实体都被我们建立出来了,那我们就应该得细分一下了,分什么呢?用户有哪些用户呢?管理员算一个吧!普通用户也算一个吧!那就有两个用户了,那我们是不是也得把这两个用户给建立出来,他们既然都属于用户这一块,我刚好是不是可以直接用到继承的知识,让这两个用户继承我的User,就没有必要再定义name属性了,直接用我父类的就好了。
创建一个AdminUser对象:
/**
* 创建了一个AdminUser来继承User,是不是就可以复用User里面的属性了
*/
public class AdminUser extends User {
//子类创建了构造方法,是不是先要帮父类进行初始化阿!
public AdminUser(String name) {
super(name);
}
}
创建一个NormalUser对象:
/**
* 创建了一个NormalUser来继承User,是不是也可以复用User里面的属性了
*/
public class NormalUser extends User {
//子类创建了构造方法,是不是先要帮父类进行初始化阿!
public NormalUser(String name) {
super(name);
}
}
- 书这个对象已经建立出来了,但是仔细思考,我创建好了一本书,是不是没有地方放呀!那没有地方怎么办?创建一个书架对象,专门用来存放书这个对象,那说到存放,第一个是不是想到就是数组阿!
创建一个BookList对象:
import java.util.Arrays;
public class BookList {
//数组的默认大小
private static final int DEFAULT_SIZE = 10;
//创建一个book类型的数组,数组默认大小是10
private Book[] books = new Book[DEFAULT_SIZE];
//数组用了几个元素
private int usedSize;
//在构造方法里,默认初始化了三本书,也就是说程序一执行,就会有三本书在里面
public BookList() {
books[0] = new Book("三国演义", "罗贯中", 19, "历史");
books[1] = new Book("水浒传", "施耐庵", 21, "历史");
books[2] = new Book("红楼梦", "曹雪芹", 31, "历史");
//把usedSize改为3
this.usedSize = 3;
}
public Book getBook(int pos) {
return books[pos];
}
public void setBooks(Book book) {
books[usedSize] = book;
}
public void setBooks(Book book, int pos) {
books[pos] = book;
}
public int getUsedSize() {
return usedSize;
}
public void setUsedSize(int usedSize) {
this.usedSize = usedSize;
}
public static int getDefaultSize() {
return DEFAULT_SIZE;
}
//如果数组内容满了,还想插入,那就得扩容了,借助copyOf这个方法,就可以进行扩容
public void dilatation() {
this.books = Arrays.copyOf(books, 2 * DEFAULT_SIZE);
}
}
- 到这里实体的部分我们都建立完了,剩下的就是每个用户对书的操作是不一样的,也就是说我们得把那些操作也得给实现实现吧!那我们对书的操作有哪些呢?有添加图书,借阅图书,删除图书,查找图书,归还图书,显示所有图书以及退出系统,一共七个操作,我想的是,如果我定义一个接口,到时候方法的调用者,他传哪个参数,我就调用哪个方法,这样我就实现了接口的统一,并且能实现你传入什么参数,我就调用什么功能,也就是说我要定义一个接口,让这些功能去实现我的接口,最后就能展现出,你传的参数不一样,我就能给你展示出不同的功能。
定义一个IOpreationBook接口:
//里面就一个方法,到时候大家都得实现我这个方法,我就可以用多态的知识,来调用不同的方法
public interface IOpreationBook {
void work(BookList bookList);
}
定义一个AddBookOpreation类:
//添加一本图书
//实现IOpreationBook接口,并且重写work方法
public class AddBookOpreation implements IOpreationBook {
@Override
public void work(BookList bookList) {
//如果数组满了,就进行扩容
if (bookList.getUsedSize() == BookList.getDefaultSize()) {
bookList.dilatation();
}
Scanner scan = new Scanner(System.in);
System.out.println("添加图书");
System.out.println("请输入书名:");
String name = scan.nextLine();
System.out.println("请输入作者:");
String author = scan.nextLine();
System.out.println("请输入类型:");
String type = scan.nextLine();
System.out.println("请输入价格:");
int price = scan.nextInt();
//如果数组里面已经有这本输入了,这不允许插入
if (findBook(bookList, name) != null) {
System.out.println("不允许重复插入");
return;
}
Book book = new Book(name, author, price, type);
bookList.setBooks(book);
//插入成功不要忘记将usedSize+1
bookList.setUsedSize(bookList.getUsedSize() + 1);
System.out.println("添加成功");
}
}
定义一个BorrowBookOpreation类:
//借阅一本图书
//实现IOpreationBook接口,并且重写work方法
public class BorrowBookOpreation implements IOpreationBook {
@Override
public void work(BookList bookList) {
System.out.println("借阅图书");
Scanner scan = new Scanner(System.in);
System.out.println("请输入要借阅图书的名字:");
String name = scan.nextLine();
//借书的前提是要有这本书,所以要去数组里找一找,看看有没有这本书
Book book = findBook(bookList, name);
if (book == null) {
System.out.println("找不到这本书");
return;
}
//找到这本书之后呢?看看有没有被借出去,没有被借出去,就可以借走了,否则借阅失败
if (book.isBorrowed() == false) {
book.setBorrowed(true);
System.out.println("借阅成功");
} else {
System.out.println("借阅失败");
}
}
}
定义一个DelBookOpreation类:
//删除一本图书
//实现IOpreationBook接口,并且重写work方法
public class DelBookOpreation implements IOpreationBook {
@Override
public void work(BookList bookList) {
Scanner scan = new Scanner(System.in);
System.out.println("删除一本图书");
System.out.println("请输入图书的名字:");
String name = scan.nextLine();
//删除图书,得先看看有没有这本书,没有则结束方法,有则返回下标的位置
int pos = findBook(name, bookList);
if (pos == -1) {
System.out.println("找不到这本书");
return;
}
//确定下标的位置了,那就让后面的书覆盖掉前面的书,就可以实现删除了
int currentLen = bookList.getUsedSize();
for (int i = pos; i < currentLen - 1; i++) {
Book book = bookList.getBook(i + 1);
bookList.setBooks(book, i);
}
/**
* 这里为什么要置为null呢?我们仔细分析的话,后面的书虽然把前面的书覆盖了,但是
* 后面那本书还存在着,引用还是指向着他,所以我们最好的方法就是把他置为null,
* 这样就不会指向他了。
*/
bookList.setBooks(null, currentLen - 1);
//删完之后,别忘记让usedSize-1
bookList.setUsedSize(currentLen - 1);
System.out.println("删除成功");
}
}
定义一个FindBookOpreation类:
//查找一本图书
//实现IOpreationBook接口,并且重写work方法
public class FindBookOpreation implements IOpreationBook {
@Override
public void work(BookList bookList) {
System.out.println("查找一本书");
Scanner scan = new Scanner(System.in);
System.out.println("请输入图书的名字:");
String name = scan.nextLine();
int currentLen = bookList.getUsedSize();
/**
* 这里就是给数组遍历了,名字一样则返回,不一样就接着循环下去,如果循环都走完了,还没有
* 找到的话,就说明没有这本书。
*/
for (int i = 0; i < currentLen; i++) {
/**
* 这里别看写的很长,其实就是获取数组里面的一个对象,然后通过对象获取他的名字
* 然后进行比较就完了
*/
if (bookList.getBook(i).getName().equals(name)) {
System.out.println(bookList.getBook(i));
return;
}
}
System.out.println("找不到这本书");
}
}
定义一个ReturnBookOpreation类:
//归还一本图书
//实现IOpreationBook接口,并且重写work方法
public class ReturnBookOpreation implements IOpreationBook {
@Override
public void work(BookList bookList) {
System.out.println("归还图书");
Scanner scan = new Scanner(System.in);
System.out.println("请输入图书的名字:");
String name = scan.nextLine();
//归还图书也是要先判断有没有这本书
Book book = findBook(bookList, name);
if (book == null) {
System.out.println("找不到这本书");
return;
}
//有这本书再判断是否已经被借走了,被借走了再归还就没毛病
if (book.isBorrowed() == true) {
book.setBorrowed(false);
System.out.println("归还成功");
} else {
System.out.println("归还失败");
}
}
}
定义一个ShowBookOpreation类:
//展示所有图书
//实现IOpreationBook接口,并且重写work方法
public class ShowBookOpreation implements IOpreationBook {
@Override
public void work(BookList bookList) {
System.out.println("显示所有图书");
int currentLen = bookList.getUsedSize();
//这里一个for循环就能显示出所有图书
for (int i = 0; i < currentLen; i++) {
System.out.println(bookList.getBook(i));
}
}
}
定义一个ExitBookOpreation类:
//退出系统
//实现IOpreationBook接口,并且重写work方法
public class ExitBookOpreation implements IOpreationBook {
@Override
public void work(BookList bookList) {
System.out.println("退出系统成功");
/**
* 在java里面,正常退出的结束码是0,所以我们只要调用exit这个方法,并赋值一个0,
* 就可以退出这个程序
*/
System.exit(0);
}
}
- 到这里对书的操作我们就已经全部实现好了,现在就剩下一个问题了,不同权限的用户,该怎么调用不同的方法呢?我想到的方法是对User这个对象,添加一些东西,添加什么呢?添加一个接口数组,再添加一个对接口数组做操作的方法,为什么要这样设计呢?首先我加了个接口数组,那我的子类一定也继承了这个接口数组,那我就可以在子类为父类初始化的时候,往这个接口数组存放东西,比如说管理员有查找图书,添加图书的这几个功能,我就可以在初始化的时候,为这个数组添加对应功能的实例,然后再通过对接口数组操作的方法,去调用我想用的那几个功能就可以了,说的很抽象,我们具体看看代码就明白了。
为User对象添加属性和方法:
public class User {
protected String name;
//添加了一个接口数组
protected IOpreationBook[] iOpreationBook;
public User(String name) {
this.name = name;
}
//添加了一个对接口数组操作的方法
public void doWork(int choice, BookList bookList) {
//里面调用了接口数组里的对象里面的方法
this.iOpreationBook[choice].work(bookList);
}
}
让AdminUser这个对象为父类的属性进行初始化:
public class AdminUser extends User {
public AdminUser(String name) {
//初始化了父类的name属性
super(name);
//初始化了父类里的接口数组,给这个数组里面添加对应的操作
/**
* 看到这里,有些同学就会问,为什么你可以这么赋值呢?你们忘记这些操作都实现了
* IOpreationBook这个接口了吗?那这个接口数组不就是IOpreationBook这个类型吗?
* 那把我这些操作放进去,不就刚好构成多态,然后发生向上转型吗?不就可以通过父类
* 来调用我子类重写的方法吗?所以大家还是要把知识点记牢阿!
*/
this.iOpreationBook = new IOpreationBook[]{
new ExitBookOpreation(),
new FindBookOpreation(),
new AddBookOpreation(),
new DelBookOpreation(),
new ShowBookOpreation()
};
}
}
让NormalUser这个对象为父类的属性进行初始化:
public class NormalUser extends User {
public NormalUser(String name) {
//初始化了父类的name属性
super(name);
//初始化了父类里的接口数组,给这个数组里面添加对应的操作
this.iOpreationBook = new IOpreationBook[]{
new ExitBookOpreation(),
new FindBookOpreation(),
new BorrowBookOpreation(),
new ReturnBookOpreation()
};
}
}
- 看到这里,我们已经把整体的构架搭建完了,写到这里的时候,发现少了点什么,好像没有写菜单阿!那我们把User改为抽象类,然后添加一个抽象方法叫做menu(),然后让子类去重写这个方法,菜单不就出来了吗?
把User变为抽象类,并添加一个抽象方法
//我把User改为抽象类,因为这样才能添加抽象方法,我的子类才能重写我的抽象方法
public abstract class User {
protected String name;
protected IOpreationBook[] iOpreationBook;
public User(String name) {
this.name = name;
}
//添加抽象方法
//为什么这个菜单还有返回值呢?我得知道用户输入了点什么呀!我才好去调用对应的方法呀!
public abstract int menu();
public void doWork(int choice, BookList bookList) {
this.iOpreationBook[choice].work(bookList);
}
}
让AdminUser去实现父类的抽象方法:
public class AdminUser extends User {
public AdminUser(String name) {
super(name);
this.iOpreationBook = new IOpreationBook[]{
new ExitBookOpreation(),
new FindBookOpreation(),
new AddBookOpreation(),
new DelBookOpreation(),
new ShowBookOpreation()
};
}
//实现抽象方法,定义自己的菜单
@Override
public int menu() {
System.out.println("*****************************");
System.out.println("hello " + name + "欢迎来到图书小练习!");
System.out.println("1.查找图书");
System.out.println("2.新增图书");
System.out.println("3.删除图书");
System.out.println("4.显示图书");
System.out.println("0.退出系统");
System.out.println("*****************************");
System.out.println("请输入你的操作:");
Scanner scan = new Scanner(System.in);
int choice = scan.nextInt();
return choice;
}
}
让NormalUser去实现父类的抽象方法:
public class NormalUser extends User {
public NormalUser(String name) {
super(name);
this.iOpreationBook = new IOpreationBook[]{
new ExitBookOpreation(),
new FindBookOpreation(),
new BorrowBookOpreation(),
new ReturnBookOpreation()
};
}
//实现抽象方法,定义自己的菜单
@Override
public int menu() {
System.out.println("*****************************");
System.out.println("hello " + name + "欢迎来到图书小练习!");
System.out.println("1.查找图书");
System.out.println("2.借阅图书");
System.out.println("3.归还图书");
System.out.println("0.退出系统");
System.out.println("*****************************");
System.out.println("请输入你的操作:");
Scanner scan = new Scanner(System.in);
int choice = scan.nextInt();
return choice;
}
}
- 到这里我们整体项目就写完了,剩下就是运行我们刚刚写的方法了。
public class Main {
//写一个登录方法,来获取当前用户是哪个身份
public static User Login() {
Scanner scan = new Scanner(System.in);
System.out.println("请输入你的姓名:");
String name = scan.nextLine();
System.out.println("请输入你的身份:1-》管理员,0-》普通用户");
int choice = scan.nextInt();
if (choice == 1) {
return new AdminUser(name);
} else {
return new NormalUser(name);
}
}
public static void main(String[] args) {
//知道是哪个身份之后,调用那个用户对应的方法
User user = Login();
BookList bookList = new BookList();
while (true) {
/**
* 通过用户调用menu方法,就能知道用户输入了那个数字,然后去调用对应数字的那个方法
* 有人说用户乱输入怎么办?没关系阿!我这个是死循环,你不输入0,这个程序就结束不了
* 你乱输入,找不到对应方法,写的好一点的,就提示你输入错误,让你重新输入,写的不好
* 的,程序就直接报错了,所以呢?不要乱输入哈!
*/
int choice = user.menu();
user.doWork(choice, bookList);
}
}
}
- 一起看看整体的效果吧!
3.总结
当项目写出来的那一刻,挺开心的,虽然这个项目很小,也有很多不足的地方,但是这些都是我们学习的动力,就是在不断受挫,再不断爬起来,那个时候你会发现,你比昨天的自己更棒了。说说这个项目,这个项目用了哪些语法呢?用了变量+各种数据类型+if+for+数组+方法+继承+封装+多态+抽象类+接口,就是把前面所学的知识,进行了个整合,自己把这个系统写完了,对你的帮助,你的理解真的很大,也希望各位同学能继续坚持下去,继续在编程的这条路越走越远。