硅基计划2.0 学习总结 玖 图书管理系统 2.0复盘版(文字末尾源码可复制)

文章目录
- 一、单例模式
- 1. 饿汉式
- 2. 懒汉式
- 3. 懒汉式例子——公司CEO
- 二、工厂模式
- 三、代理模式
- 四、jar包的使用
- 五、项目模块
- 1. User模块
- 2. 常量模块
- 3. Book模块&Util模块
- 4. 图书馆系统模块`LibrarySystem`
- 5. 继续完善`User`模块中的代理类
- 6. 回到`Book`模块完善`Library`类中不同用户类型共同方法
- 7. `User`模块的管理员类
- 8. `Book`模块
- 9. `Uitl`模块
- 10 目前的`User`模块的普通用户类
- 11. 继续实现`User`模块的管理员类
- 12. 继续实现`User`模块的普通用户类
- 13. 完善最后的`Library`类模块中的共通方法
- 六、总结以及源码展示
- 1. Book模块
- 2. Constant模块
- 3. lib读写流模块(此代码非我原创!!!)
- 4. User模块
- 工厂模块
- 5. `Util`模块
- 6. `LibrarySystem`模块(项目启动模块)
- 7. 文件模块
- `allBooks.txt`模块测试数据
- `borrow.txt`模块测试数据
- 七、想说的话
一、单例模式
定义:控制类只能有一个实例,并且能够提供全局访问点,用于严格资源控制
1. 饿汉式
定义:在程序启动的时候就让这单个例子初始化,好处是安全和不用额外开销,坏处就是如果你不去使用这个实例,就会造成资源的浪费
那如何去控制实例的个数呢,很简单,且看演示
//创建一个User类
public class User {private static User user = new User();//static说明静态成员只有一份,不管new了多少对象都只有一份private User(){}public static User get(){//提供外部调用的唯一接口return user;}
}
//创建Test类以实例化类
public class Test {public static void main(String[] args) {User user1 = User.get();User user2 = User.get();System.out.println(user1 == user2);//true}
}
我们可以看到我们提供类一个对外接口,这个接口就是为构造方法服务的,这是外部实例化类的唯一方式
在User类中我们令其构造方法为private修饰,就是保证了不能通过User user = new User();来创建对象
只能User user1 = User.get();通过类去调用get()方法,从而去实例化类
通过打印结果我们看到我们不论创建了多少实例它们本质上都是一样的(因static修饰了)
2. 懒汉式
定义:顾名思义就是你要用的时候才去实例化,不用就放在那,首次访问才初始化
其优点就是不占用资源,缺点就是多线程不安全,待会演示
//创建一个Person类
public class Person {private static Person person;private Person(){}public static Person get(){if(person == null){//首次调用实例化person = new Person();}//如果是第二次及以上就不用实例化了return person;}
}
//在Test类中调用
public static void main(String[] args) {Person person = Person.get();Person person1 = Person.get();System.out.println(person1 == person);//true}
现在来回答为什么多线程不安全,注意看get()方法
假设我A用户进来了(此时还没有实例化),实例化好了正准备出去,B用户又进来了(因A用户还未完全实例化好)
明明只能实例化一个,但是现在B用于也实例化了一个,就会造成混乱,但是这个后续我们可以优化
3. 懒汉式例子——公司CEO
//创建一个CEO类
public class CEO {private static CEO ceo;private String name;private CEO(String name){this.name = name;}public static CEO get(String name){if(ceo == null){ceo = new CEO(name);}return ceo;}public void manage(){System.out.println(name+"CEO正在管理公司事务");}public void development(){System.out.println(name+"CEO正在统筹公司战略");}
}
//创建一个Company类供调用
public class Company {public static void main(String[] args) {CEO ceo = CEO.get("张三");ceo.development();ceo.manage();}
}
张三CEO正在统筹公司战略 张三CEO正在管理公司事务
二、工厂模式
定义:创建对象过程与使用对象过程进行分离,达到解耦目的
用一个专门的类对创建对象进行封装
分为简单工厂、工厂方法、抽象工厂三大模式
在非工厂模式中,加入你的类内部变量发生了变动,很多地方都受到牵连,都要改
我们先演示非工厂模式弊端,我们创建一个抽象类User,定义一些变量
再创建两个类ManagePerson和OrdinaryPerson去继承User类,再定义个Test去实例化对象
public abstract class User {protected String name;protected int id;public User(String name, int id) {this.name = name;this.id = id;}
}
public class ManagePerson extends User{public ManagePerson(String name, int id) {super(name, id);}
}
public class OrdinaryPerson extends User{public OrdinaryPerson(String name, int id) {super(name, id);}
}
public class Test {public static void main(String[] args) {ManagePerson managePerson = new ManagePerson("张三",10010);OrdinaryPerson ordinaryPerson = new OrdinaryPerson("李四",10086);}
}
当我们在抽象类User中去添加新的用户类型的时候,就要重新写构造方法,传参之类的,很麻烦
因此,如果我们使用工厂模式,我们先想,工厂是怎么运作的,是不是要做出产品,而这个产品正好对于我们的对象
因此我们可以知道:每个用户类型(管理员ManagePerson和普通用户OrdinaryPerson)都分别可以做成一个工厂,想要添加新的用户类型,只需要添加新的工厂而无需修改原本代码
我们创建一个抽象工厂类接口PersonFactory,只写User用户类的对象构造User user(String name,int id);
我们再创建两个工厂类ManagePersonFactory类和OrdinaryPersonFactory类中实现PersonFactory接口
return返回值就返回创建当前类的对象,即return new 类(参数)
并且工厂类中不能出现构造方法,这个是在对应的用户类中才有的
//User用户抽象类
public abstract class User {protected String name;protected int id;public User(String name, int id) {this.name = name;this.id = id;}
}
//管理员类ManagePerson
public class ManagePerson extends User{public ManagePerson(String name, int id) {super(name, id);}
}
//普通用户类OrdinaryPerson
public class OrdinaryPerson extends User {public OrdinaryPerson(String name, int id) {super(name, id);}
}
//用户工厂类接口PersonFactory,声明`User`方法
public interface PersonFactory {User user(String name,int id);
}
//管理员工厂类ManagePersonFactory,实现用户类接口,重写方法
public class ManagePersonFactory implements PersonFactory {@Overridepublic User user(String name, int id) {return new ManagePerson(name,id);}
}
//普通用户工厂类OrdinaryPersonFactory,实现用户类接口,重写方法
public class OrdinaryPersonFactory implements PersonFactory {@Overridepublic User user(String name, int id) {return new OrdinaryPerson(name,id);}
}
//定义测试类用于实例化对象
public class Test {public static void main(String[] args) {PersonFactory person1 = new ManagePersonFactory();person1.user("张三",10010);PersonFactory person2 = new OrdinaryPersonFactory();person2.user("李四",10086);PersonFactory person3 = new OrdinaryPersonFactory();person3.user("王五",10000);}
}
此时,如果还有其他用户类型,直接写新的用户类和用户类工厂就好了,无需修改原代码,是不是很方便?
三、代理模式
定义:通俗理解成中介,这个类可以去代理另一个类的功能,分为静态、动态以及CGLIB
我们本次以静态为例
想想我们刚刚写的代码,我们不同类型的用户是不是有不同的菜单呢
因此我们在用户类中加入打印菜单方法public abstract int display();,注意返回类型是int
再在普通用户类和管理员类中分别重写方法和定义一个专属方法(比如普通用户借书,管理员上架书等等)
此时我们就达成了普通用户和管理员之间的差异化
再定义个代理类Broker,定义一个私有变量realName用于表示用户真实姓名,再写个构造方法,用于向上转型指向相对应的用户类型
再在代理类中定义所有用户类型的所有可能方法,每个方法再判断用户类型权限
在测试类的main方法中注意我们还要利用向上转型和通过工厂对象调用user方法完成对象创建
此时,我们再创建代理类对象,传参传对应的用户类型的对象,完整代码如下
仅展示改动的代码,其他代码与之前代码一致
//新建一个代理类Broker
public class Broker {private User realName;//用来表示真正的用户名字public Broker(User user){realName = user;}//运用到了向上转型,因为User是所有用户类的父类//再判断对象实例化过程中用户的类型,即检查对应权限//要把所有用户类型的所有可能的方法都写这public void borrowBook(){if(realName instanceof OrdinaryPerson) {((OrdinaryPerson)realName).borrowBook();//可以强转,因为类型以及判断,就是把realName转成对应类型}else if(realName instanceof ManagePerson){System.out.println("管理员不用借书");}else{System.out.println("错误");}}public void helvesBook(){if(realName instanceof ManagePerson) {((ManagePerson)realName).shelvesBook();//可以强转,因为类型以及判断,就是把realName转成对应类型}else if(realName instanceof OrdinaryPerson){System.out.println("普通用户不能上架图书");}else{System.out.println("错误");}}
}
//在User类中加入展示菜单`dispaly()`方法
public abstract class User {protected String name;protected int id;public User(String name, int id) {this.name = name;this.id = id;}public abstract int display();
}
//其他两个用户类再重写
public class ManagePerson extends User{public ManagePerson(String name, int id) {super(name, id);}@Overridepublic int display() {return 0;}public void shelvesBook(){System.out.println(name+"正在上架图书");}
}
public class OrdinaryPerson extends User {public OrdinaryPerson(String name, int id) {super(name, id);}@Overridepublic int display() {return 0;}public void borrowBook(){System.out.println(name+"在借书");}
}
//再在测试类中的main方法中调用
public class Test {public static void main(String[] args) {PersonFactory personFactory = new ManagePersonFactory();User managePerson = personFactory.user ("张三",10010);PersonFactory person2 = new OrdinaryPersonFactory();User ordinaryPerson = person2.user("李四",10086);System.out.println("===以下是代理模式===");Broker brokerOrdinary = new Broker(ordinaryPerson);brokerOrdinary.borrowBook();brokerOrdinary.helvesBook();//权限测试System.out.println("------------");Broker brokerManage = new Broker(managePerson);brokerManage.borrowBook();//权限测试brokerManage.helvesBook();}
}
运行结果:
=以下是代理模式=
李四在借书
普通用户不能上架图书
------------
管理员不用借书
张三正在上架图书
四、jar包的使用
先打开需要导入jar包的项目,先创建一个名为lib的文件

再找到想导入的jar包,放在lib目录下
导入成功后再点击上方文件-项目结构

再点击库,加号导入,从Java,导入刚刚jar包,确定->应用再确定即可

虽然jar内部是字节码文件,但是Idea会反编译成Java文件,从此我们直接调用这个jar包即可(类实例化对象)
五、项目模块
我们先来介绍我们项目的五大模块,他们分别是
图书模块Book、用户模块User、常量模块Constant、工具模块Util、读写流模块lib、图书馆启动类LibrarySystem
我们接下来按照各个模块的各个步骤(中间会几个模块之间穿插),工具模块默认导入,结尾会放出
1. User模块
我们对各个用户类型模块的属性以及各个方法都定义好
public abstract class User {protected String name;protected String role;protected int userID;public User(String name, int id,String role) {this.name = name;this.userID = id;this.role = role;}public String getName() {return name;}public String getRole() {return role;}public int getUserID() {return userID;}//功能菜单打印public abstract int display();
}
public class AdminUser extends User {public AdminUser(String name, int userID) {super(name, userID, "管理员");library = Library.getLibrary();}@Overridepublic int display() {System.out.println("管理员[" + name + "]的操作菜单:");System.out.println("1. 查找图书");System.out.println("2. 打印所有的图书");System.out.println("3. 退出系统");System.out.println("4. 上架图书");System.out.println("5. 修改图书");System.out.println("6. 下架图书");System.out.println("7. 统计借阅次数");System.out.println("8. 查看最后欢迎的前K本书");System.out.println("9. 查看库存状态");System.out.println("10. 检查超过⼀年未下架的图书");System.out.println("请选择你的操作:");return scanner.nextInt();}//上架图书public void addBook(){}//图书信息修改public void updateBook(){}//删除图书public void removeBook(){}//统计每本书的借阅次数public void borrowCount(){}//查看最受欢迎的前k本书public void generateBook(){}//查看库存状态public void checkInventoryStatus(){}//移除上架超过一年的图书public void checkAndRemoveOldBook(){}
}
public class NormalUser extends User {public NormalUser(String name, int useId) {super(name, useId, "普通用户");}@Overridepublic int display() {System.out.println("普通用户[" + name + "]的操作菜单:");System.out.println("1. 查找图书");System.out.println("2. 打印所有的图书");System.out.println("3. 退出系统");System.out.println("4. 借阅图书");System.out.println("5. 归还图书");System.out.println("6. 查看当前个⼈借阅情况");System.out.println("请选择你的操作:");return scanner.nextInt();}//加载所有借阅书籍private void loadBorrowedBook(){}//存储所有借阅的书籍private void storeBorrowedBook(){}//借书public void borrowBook(){}//还书public void returnBook(){}//查看个人借阅情况public void viewBorrowBooks(){}
}
我们接着再引入工厂模式对各个用户类型进行初始化,这个Factory定义在User包底下
public interface IPersonFactory {User createUser(String name, int useId);
}
public class AdminUserFactory implements IPersonFactory{@Overridepublic User createUser(String name, int useId) {return new AdminUser(name,useId);}
}
public class NormalUserFactory implements IPersonFactory{@Overridepublic User createUser(String name, int useId) {return new NormalUser(name,useId);}
}
因为我们之前实例化对象的时候,就用到了向上转型,因此此时工厂模式会自动匹配到底实例化的是哪个用户类型
我们在LibrarySystem类中创建main方法,我们使用工厂模式实例化管理员对象和【普通用户对象
public class LibrarySystem {public static void main(String[] args) {IPersonFactory normalUser = new NormalUserFactory();User normalUser1 = normalUser.createUser("王晓明",1);User normalUser2 = normalUser.createUser("李东来",2);IPersonFactory adminUser = new AdminUserFactory();User adminUser1 = adminUser.createUser("胡志云",3);}}
}
我们再引入代理模式,把所有用户类型的方法都拿到代理类中来,以及真实代理的对象
public class ProxyUser {//被代理的是哪个用户private User realUser;public ProxyUser(User user) {this.realUser = user;}public User getRealUser() {return realUser;}public String getRealUserName(){return realUser.name;}private void checkRealUserWhetherNormal(String exceptionMessage){}//借书public void borrowBook(){}//还书public void returnBook(){}//查看个人借阅情况public void viewBorrowBooks(){}//-----------------------------------------------////管理员用户方法//检查管理员权限问题private void checkUserWhetherAdminUser(String exceptionMessage){}//上架图书public void addBook(){}//图书信息修改public void updateBook(){}//删除图书public void removeBook(){}//统计每本书的借阅次数public void borrowCount(){}//查看最受欢迎的前k本书public void generateBook(){}//查看库存状态public void checkInventoryStatus(){}//移除上架超过一年的图书public void checkAndRemoveOldBook(){}//退出系统public void exit(){}public void handleOperation(int choice) {}
}
我们再使用代理类在LibrarySystem类的main方法中代理对象
public class LibrarySystem {public static void main(String[] args) {IPersonFactory normalUser = new NormalUserFactory();User normalUser1 = normalUser.createUser("王晓明",1);User normalUser2 = normalUser.createUser("李东来",2);IPersonFactory adminUser = new AdminUserFactory();User adminUser1 = adminUser.createUser("胡志云",3);//使用代理类ProxyUser proxyUserNormalUser1 = new ProxyUser(normalUser1);ProxyUser proxyUserNormalUser2 = new ProxyUser(normalUser2);ProxyUser proxyUserAdminUser = new ProxyUser(adminUser1);}
}
2. 常量模块
public class Constant {//内存中的书籍数组初识容量public static final int CAPACITY = 5;//存储所有图书的⽂件public static final String ALL_BOOK_FILE_NAME = "allBooks.txt";//存储借阅信息的文件public static final String ALL_BOOK_BORROW = "borrow.txt";//-------------------管理员相关操作管理----------------//查找图书public static final int SEARCH_BOOK = 1;//显⽰图书public static final int DISPLAY_BOOK = 2;//退出系统public static final int EXIT = 3;//上架图书public static final int ADD_BOOK = 4;//更新图书public static final int UPDATE_BOOK = 5;//删除图书public static final int REMOVE_BOOK = 6;//查看图书的借阅次数public static final int BORROWED_BOOK_COUNT = 7;//查看受欢迎的图书public static final int GENERATE_BOOK = 8;//查看库存状态public static final int CHECK_INVENTORY_STATUS = 9;//移除上架超过1年的书籍public static final int CHECK_AND_REMOVE_OLD_BOOK = 10;//-------------------普通用户相关操作管理----------------//借阅图书public static final int BORROWED_BOOK = 4;//归还图书public static final int RETURN_BOOK = 5;//查看个⼈借阅情况public static final int VIEW_BORROW_HISTORY_BOOK = 6;
}
3. Book模块&Util模块
我们在Book包底下定义Book类,表示一本本图书,实现Comparable接口便于后续查找最受欢迎的前k本书
public class Book implements Comparable<Book>{private int bookId; //书idprivate String title; // 书名private String author; // 作者private String category; // 类别private int publishYear; // 出版年份private boolean isBorrowed; // 借阅状态private int borrowCount; // 借阅次数private LocalDate shelfDate; // 上架时间public Book(String title, String author, String category, int publishYear, LocalDate shelfDate) {this.title = title;this.author = author;this.category = category;this.publishYear = publishYear;this.shelfDate = shelfDate;}public int getBookId() {return bookId;}public void setBookId(int bookId) {this.bookId = bookId;}public String getTitle() {return title;}public void setTitle(String title) {this.title = title;}public String getAuthor() {return author;}public void setAuthor(String author) {this.author = author;}public String getCategory() {return category;}public void setCategory(String category) {this.category = category;}public int getPublishYear() {return publishYear;}public void setPublishYear(int publishYear) {this.publishYear = publishYear;}public boolean isBorrowed() {return isBorrowed;}public void setBorrowed(boolean borrowed) {isBorrowed = borrowed;}public int getBorrowCount() {return borrowCount;}public void setBorrowCount(int borrowCount) {this.borrowCount = borrowCount;}public LocalDate getShelfDate() {return shelfDate;}public void setShelfDate(LocalDate shelfDate) {this.shelfDate = shelfDate;}@Overridepublic String toString() {return "Book{" +"书籍id:" + bookId +", 书名:" + title + '\'' +", 作者:" + author + '\'' +", 种类:" + category + '\'' +", 发行年份:" + publishYear +", 是否被借阅:" + (isBorrowed ? "已被借阅" : "未被借阅") +", 借阅次数:" + borrowCount +", 上架日期:" + shelfDate +'}';}//针对一本书的信息进行整合,变成一个字符串public String toJson(){StringBuilder json = new StringBuilder();json.append(bookId).append(",");json.append(title).append(",");json.append(author).append(",");json.append(category).append(",");json.append(publishYear).append(",");json.append(isBorrowed).append(",");json.append(borrowCount).append(",");json.append(shelfDate != null ? shelfDate.format(DateTimeFormatter.ISO_LOCAL_DATE) : "null");return json.toString();}//为最受欢迎的前k本书做准备,从大到小排序@Overridepublic int compareTo(Book o) {return o.getBorrowCount()-this.getBorrowCount();}public void incrementBorrowCount() {borrowCount++;}public void decreaseBorrowCount() {borrowCount--;}
}
- 工具模块
util中的AnalyzingBook实现
现在我们想要存储书籍数据,那么我们就需要在工具包util下定义一个AnalyzingBook,专门用于书籍文件的读取和写入
每个方法我都有注释,大家都可以看得懂的
public class AnalyzingBook {//存书public void storeObject(Book[] books, String filename) {//为什么我们不可以直接books.length统计书籍数量呢//这就相当于书架,可能有的地方有空缺并没有放置书//因此我们先要统计有效书籍的数目int bookUseLength = 0;for (int i = 0; i < books.length; i++) {if(books[i] != null){bookUseLength++;}}//现在我们需要吧书进行字符串化StringBuilder jsonArray = new StringBuilder();//我们根据刚刚统计到的有效书籍个数进行字符串化//我们现在针对的是每一本书for (int i = 0; i < bookUseLength; i++) {if(books[i] != null){//这里的toJSON是我们要实现的,即把一本书的所有信息整合成一个字符串jsonArray.append(books[i].toJson());}//因为我们每一本书在文件中是以换行符相隔的,因此最后一本书无需换行符if(i < bookUseLength-1){jsonArray.append("\n");}}//此时我们的几本书合在一起就是一个这样的字符串//1,西游记,......\n2,水浒传,....... .... 5,红楼梦//写回文件FileUtils.writeFile(jsonArray.toString(),Constant.ALL_BOOK_FILE_NAME);}//从文件读取数据public Book[] loadObject(String filename) {//读取所有内容,再根据\n分隔封装成为一个个书籍对象String content = FileUtils.readFile(Constant.ALL_BOOK_FILE_NAME);if(content == null || content.isEmpty()){System.out.println("文件不存在或文件为空:"+Constant.ALL_BOOK_FILE_NAME);return null;}//根据\n分隔字符串String[] bookJsonStrings = content.split("\n");//组成成对象Book[] booksList = new Book[bookJsonStrings.length];for(int i = 0;i < bookJsonStrings.length;i++){//这里涉及到把字符串变成一个书籍的对象,我们具体来实现Book book = parseBookJson(bookJsonStrings[i]);booksList[i] = book;}return booksList;}//书籍字符串解析private Book parseBookJson(String json) {//判空if(json.isEmpty()) {return null;}//使用逗号分割字符串String [] pairs = json.split(",");//针对每一个数学,赋予对象的值int bookId = Integer.parseInt(pairs[0]);String title = pairs[1];String author = pairs[2];String category = pairs[3];int publishYear = Integer.parseInt(pairs[4]);boolean isBorrowed = Boolean.parseBoolean(pairs[5]);int borrowCount = Integer.parseInt(pairs[6]);LocalDate shelfDate = LocalDate.parse(pairs[7]);//根据内容去构造对象,要判断内容都要存在,其实可以不用判断//我们之前已经筛选过了,但是为了保险起见,我们还是要进行筛选if (title != null && author != null && category != null && shelfDate != null) {Book book = new Book(title, author, category, publishYear, shelfDate);book.setBorrowed(isBorrowed);book.setBorrowCount(borrowCount);book.setBookId(bookId);return book;}return null;}
}
现在我们回到Book类中,写好我们的toJson方法
//针对一本书的信息进行整合,变成一个字符串public String toJson(){StringBuilder json = new StringBuilder();json.append(bookId).append(",");json.append(title).append(",");json.append(author).append(",");json.append(category).append(",");json.append(publishYear).append(",");json.append(isBorrowed).append(",");json.append(borrowCount).append(",");json.append(shelfDate != null ? shelfDate.format(DateTimeFormatter.ISO_LOCAL_DATE) : "null");return json.toString();}
好,现在书有了,我们是不是还要有图书馆类Library去存放我们的书籍呢
public class Library {private Book[] books;//当前图书数组private int bookCount;//实际存储的图书数量private final AnalyzingBook analyzingBook = new AnalyzingBook();public Library() {//当调⽤该构造⽅法的时候,要加载⽂件当中的数据进⾏到books数组当中//books数组的大小我们已经默认定义在常量中了}
}
好,我们继续在图书馆类Library类中实现我们的方法
首先就是加载所有的书籍
private void loadAllBooks() {//先读取文件的内容Book[] allBook = analyzingBook.loadObject(Constant.ALL_BOOK_FILE_NAME);books = new Book[Constant.CAPACITY];//判断有效数据个数if(allBook == null){bookCount = 0;}else{//查案实际书籍的长度,即看书架是否满了int allBookLength = allBook.length;if(allBookLength > books.length){//大于实际长度,说明当前的书架放不下那么多书,因此我们就根据实际有多少本就找多大的书架books = new Book[allBookLength];}for (int i = 0; i < allBookLength; i++) {//将读取到的元素进行赋值,以返回实时的图书信息books[i] = allBook[i];}//修改实际的有效数据个数bookCount = allBookLength;}}
接着,我们还需要一个把书籍写入文件的方法
private void storeBook(){analyzingBook.storeObject(books,Constant.ALL_BOOK_FILE_NAME);}
因此我们最后Library类整合如下,在实例化图书馆类的时候就去加载书籍
public class Library {private Book[] books;//当前图书数组private int bookCount;//实际存储的图书数量private final AnalyzingBook analyzingBook = new AnalyzingBook();public Library() {//当调⽤该构造⽅法的时候,每次要加载⽂件当中的数据进⾏到books数组当中loadAllBooks();}private void loadAllBooks() {//先读取文件的内容Book[] allBook = analyzingBook.loadObject(Constant.ALL_BOOK_FILE_NAME);books = new Book[Constant.CAPACITY];//判断有效数据个数if(allBook == null){bookCount = 0;}else{//查案实际书籍的长度,即看书架是否满了int allBookLength = allBook.length;if(allBookLength > books.length){//大于实际长度,说明当前的书架放不下那么多书,因此我们就根据实际有多少本就找多大的书架books = new Book[allBookLength];}for (int i = 0; i < allBookLength; i++) {//将读取到的元素进行赋值,以返回实时的图书信息books[i] = allBook[i];}//修改实际的有效数据个数bookCount = allBookLength;}}private void storeBook(){analyzingBook.storeObject(books,Constant.ALL_BOOK_FILE_NAME);}
}
4. 图书馆系统模块LibrarySystem
我们要实现一个选择用户类型的模块
//选择对应的代理类角色进行登录操作public static ProxyUser selectProxyRole(ProxyUser proxyUserNormalUser1, ProxyUser proxyUserNormalUser2,ProxyUser proxyUserAdminUser){Scanner scanner = new Scanner(System.in);System.out.println("请选择对应的用户类型进行登录");System.out.println("1.管理员["+proxyUserAdminUser.getRealUserName()+"]\n" +"2.普通用户["+proxyUserNormalUser1.getRealUserName()+"]\n3.普通用户["+proxyUserNormalUser2.getRealUserName()+"]\n4.退出系统");ProxyUser currentUser = null;int choice = scanner.nextInt();switch (choice) {case 1:currentUser = proxyUserAdminUser;break;case 2:currentUser = proxyUserNormalUser1;break;case 3:currentUser = proxyUserNormalUser2;break;case 4:System.exit(0);System.out.println("系统已退出..");break;default:break;}return currentUser;}
因此我们现在的main方法如下
public static void main(String[] args) {IPersonFactory normalUser = new NormalUserFactory();User normalUser1 = normalUser.createUser("王晓明",1);User normalUser2 = normalUser.createUser("李东来",2);IPersonFactory adminUser = new AdminUserFactory();User adminUser1 = adminUser.createUser("胡志云",3);//使用代理类ProxyUser proxyUserNormalUser1 = new ProxyUser(normalUser1);ProxyUser proxyUserNormalUser2 = new ProxyUser(normalUser2);ProxyUser proxyUserAdminUser = new ProxyUser(adminUser1);//操作界面测试LibrarySystem librarySystem = new LibrarySystem();ProxyUser currentUser = selectProxyRole(proxyUserNormalUser1,proxyUserNormalUser2,proxyUserAdminUser);while(true){int choice = currentUser.getRealUser().display();//此时⽆需关系是 管理员还是普通用户,代理类会做权限判断currentUser.handleOperation(choice);}}
5. 继续完善User模块中的代理类
还记得我们刚刚的handleOperation吗,我们来实现它
这个方法就是每个用户类型的功能菜单
public void handleOperation(int choice) {if (realUser instanceof AdminUser) {// 管理员操作switch (choice) {case Constant.SEARCH_BOOK:library.searchBook();break;case Constant.DISPLAY_BOOK:library.displayBook();break;case Constant.EXIT:library.exit();break;case Constant.ADD_BOOK:addBook();break;case Constant.UPDATE_BOOK:updateBook();break;case Constant.REMOVE_BOOK:removeBook();break;case Constant.BORROWED_BOOK_COUNT:borrowCount();break;case Constant.GENERATE_BOOK:generateBook();break;case Constant.CHECK_INVENTORY_STATUS:checkInventoryStatus();break;case Constant.CHECK_AND_REMOVE_OLD_BOOK:checkAndRemoveOldBook();break;default:System.out.println("⽆效的操作。");}} else if (realUser instanceof NormalUser) {// 普通⽤⼾操作switch (choice) {case Constant.SEARCH_BOOK:library.searchBook();break;case Constant.DISPLAY_BOOK:library.displayBook();break;case Constant.EXIT:library.exit();case Constant.BORROWED_BOOK:borrowBook();break;case Constant.RETURN_BOOK:returnBook();break;case Constant.VIEW_BORROW_HISTORY_BOOK:viewBorrowBooks();break;default:System.out.println("⽆效的操作。");}}}
同时,我们添加权限判断,判断是否是管理员或者是普通用户
//检查管理员权限问题private void checkUserWhetherAdminUser(String exceptionMessage){if(!(realUser instanceof AdminUser)){throw new PermissionException(exceptionMessage);}}
private void checkRealUserWhetherNormal(String exceptionMessage){if(!(realUser instanceof NormalUser)){throw new PermissionException(exceptionMessage);}}
同时我们在当前用户模块包User底下定义一个自定义异常
public class PermissionException extends RuntimeException{public PermissionException(String message) {super(message);}
}
6. 回到Book模块完善Library类中不同用户类型共同方法
//下面三个方法是不同用户类型的共同方法public void searchBook(){System.out.println("查找图书中......");System.out.println("请输入你想要查找的图书名称");String title = scanner.nextLine();Book book = searchBookBTitle(title);if(book == null){System.out.println("并未找到指定书籍");return;}System.out.println("找到了你想找的书籍,信息如下");System.out.println("-----------------------------------------------------------------------------------------------------------------------------");System.out.println(book);System.out.println("-----------------------------------------------------------------------------------------------------------------------------");}public void displayBook(){System.out.println("图书馆书籍信息如下");loadAllBooks();System.out.println("-----------------------------------------------------------------------------------------------------------------------------");for (int i = 0; i < bookCount; i++) {System.out.println(books[i]);}System.out.println("-----------------------------------------------------------------------------------------------------------------------------");}public void exit(){System.out.println("退出");System.exit(0);}
7. User模块的管理员类
我们现在要为添加图书做好准备
//上架图书public void addBook(){scanner.nextLine();System.out.println("请输⼊书名:");String title = scanner.nextLine(); // 输⼊书名System.out.println("请输⼊作者:");String author = scanner.nextLine(); // 输⼊作者System.out.println("请输⼊类别:");String category = scanner.nextLine(); // 输⼊图书类别System.out.println("请输⼊出版年份:");int year = scanner.nextInt(); // 输⼊出版年份scanner.nextLine(); // 吞掉换⾏符LocalDate shelfDate = LocalDate.now(); // 当前时间作为上架时间Book newBook = new Book(title, author, category, year, shelfDate);// 创建新书对象//调⽤图书类 添加图书library.addBook(newBook);}
但是到了这里,你是否发现,如果我们几个用户类型,每一次使用图书馆类都要new,是不是会导致有多个图书馆,因此我们把Library设置为单例模式
因此我们回到Library类中修改部分代码如下,添加一个对外获取对象的方法
private Book[] books;//当前图书数组private int bookCount;//实际存储的图书数量private static Library library;//保证只有一个图书馆,即单例模式Scanner scanner = new Scanner(System.in);private final AnalyzingBook analyzingBook = new AnalyzingBook();public Library() {//当调⽤该构造⽅法的时候,每次要加载⽂件当中的数据进⾏到books数组当中loadAllBooks();}public static Library getLibrary(){if(library == null){library = new Library();}return library;}.........}
因此我们的两个用户类型中实例化图书馆类的代码修改如下
public class AdminUser extends User {Scanner scanner = new Scanner(System.in);private Library library = null;//创建管理员对象的时候才实例化图书馆public AdminUser(String name, int userID) {super(name, userID, "管理员");library = Library.getLibrary();}......}
public class NormalUser extends User {Scanner scanner = new Scanner(System.in);private Library library;//用户借阅图书的相关信息private PairOfUidAndBookId[] pairOfUidAndBookIds;//当前书籍的借阅量private int borrowedCount;//最多借阅书籍数目private static final int BORROW_BOOK_MAX_NUM = 5;//借阅信息分析工具类加载private final AnalyzingBorrowedBook analyzingBorrowedBook = new AnalyzingBorrowedBook();public NormalUser(String name, int useId) {super(name, useId, "普通用户");loadBorrowedBook();library = Library.getLibrary();}......}
8. Book模块
我们现在有存储借阅信息的文件,我们要通过这个文件,分析一些借阅信息,因此我们在Book包下定义一个PairOfUidAndBookId类来专门分析这个文件
public class PairOfUidAndBookId {//专门分析借阅信息private int userId;private int bookId;public PairOfUidAndBookId() {}public PairOfUidAndBookId(int userId, int bookId) {this.userId = userId;this.bookId = bookId;}public int getUserId() {return userId;}public void setUserId(int userId) {this.userId = userId;}public int getBookId() {return bookId;}public void setBookId(int bookId) {this.bookId = bookId;}//把对象序列化为JSON字符串的形式public String toJson() {StringBuilder json = new StringBuilder();json.append(userId).append(",");json.append(bookId);return json.toString();}
}
9. Uitl模块
我们有了之前的AnalyzingBook类,同样也要有分析借阅信息的工具类AnalyzingBorrowedBook
public class AnalyzingBorrowedBook {//这个就是分析借阅信息,跟分析书籍信息类似的逻辑//存储借阅信息public void storeObject(PairOfUidAndBookId[] pairOfUidAndBookIds, String filename){//先遍历pairOfUidAndBookIds数组当中不为空的数据多少个?int booksUseLen = 0;for (int i = 0; i < pairOfUidAndBookIds.length; i++) {if(pairOfUidAndBookIds[i] != null) {booksUseLen++;}}StringBuilder jsonArray = new StringBuilder();for (int i = 0; i < booksUseLen; i++) {if(pairOfUidAndBookIds[i] != null) {jsonArray.append(pairOfUidAndBookIds[i].toJson());if (i != booksUseLen-1) {jsonArray.append("\n");}}}FileUtils.writeFile(jsonArray.toString(),filename);/* */}//读取借阅信息public PairOfUidAndBookId[] loadObject(String filename){//从⽂件读取数据String content = FileUtils.readFile(filename);if (content == null || content.isEmpty()) {System.out.println("已借阅书籍列表⽆数据,表⽰没有⽤⼾借阅过书籍");return null;}String[] JsonStrings = content.split("\n");PairOfUidAndBookId[] pairOfUidAndBookIds = new PairOfUidAndBookId[JsonStrings.length];for (int i = 0; i < JsonStrings.length; i++) {PairOfUidAndBookId pairOfUidAndBookId = new PairOfUidAndBookId();String[] uidAndBookIds = JsonStrings[i].split(",");pairOfUidAndBookId.setUserId(Integer.parseInt(uidAndBookIds[0]));pairOfUidAndBookId.setBookId(Integer.parseInt(uidAndBookIds[1]));pairOfUidAndBookIds[i] = pairOfUidAndBookId;}return pairOfUidAndBookIds;}
}
10 目前的User模块的普通用户类
public class NormalUser extends User {Scanner scanner = new Scanner(System.in);private Library library;//用户借阅图书的相关信息private PairOfUidAndBookId[] pairOfUidAndBookIds;//当前书籍的借阅量private int borrowedCount;//最多借阅书籍数目private static final int BORROW_BOOK_MAX_NUM = 5;//借阅信息分析工具类加载private final AnalyzingBorrowedBook analyzingBorrowedBook = new AnalyzingBorrowedBook();public NormalUser(String name, int useId) {super(name, useId, "普通用户");loadBorrowedBook();library = Library.getLibrary();}@Overridepublic int display() {System.out.println("普通用户[" + name + "]的操作菜单:");System.out.println("1. 查找图书");System.out.println("2. 打印所有的图书");System.out.println("3. 退出系统");System.out.println("4. 借阅图书");System.out.println("5. 归还图书");System.out.println("6. 查看当前个⼈借阅情况");System.out.println("请选择你的操作:");return scanner.nextInt();}//加载所有借阅书籍private void loadBorrowedBook(){PairOfUidAndBookId[] allBorrowedBook;//1.先加载⽂件当中的借阅信息allBorrowedBook = analyzingBorrowedBook.loadObject(Constant.ALL_BOOK_BORROW);//2. 默认已借阅的图书数组⼤⼩为BORROW_BOOK_MAX_NUM,这⾥也可以定义到常量类pairOfUidAndBookIds = new PairOfUidAndBookId[BORROW_BOOK_MAX_NUM];//3.没有读取到已借阅的图书信息if (allBorrowedBook == null) {borrowedCount = 0;} else {//4. 查看实际读取到的数组⻓度是多少?int allBorrowedBookLen = allBorrowedBook.length;//5. 如果读取到了10本书被借阅 但是当前borrowedBooks数组⻓度⼩于10if (allBorrowedBookLen > pairOfUidAndBookIds.length) {//6. 按照实际情况进⾏分配数组内存pairOfUidAndBookIds = new PairOfUidAndBookId[allBorrowedBookLen];}//7. 把数据拷⻉回到 已借阅图书信息的数组当中for (int i = 0; i < allBorrowedBookLen; i++) {pairOfUidAndBookIds[i] = allBorrowedBook[i];}//8. 更新当前实际借阅书籍的书籍数量borrowedCount = allBorrowedBookLen;}}//存储所有借阅的书籍private void storeBorrowedBook(){analyzingBorrowedBook.storeObject(pairOfUidAndBookIds, Constant.ALL_BOOK_BORROW);}//借书public void borrowBook(){}//还书// 还书public void returnBook(){}//查看个人借阅情况public void viewBorrowBooks(){}
}
11. 继续实现User模块的管理员类
我们的模式就是代理类检查权限并调用管理类方法--->管理类做好准备工作-->Library类实现
- 添加书籍
public class ProxyUser {
//上架图书,管理员类中public void addBook(){System.out.println("上架图书中......");//首先检查权限checkUserWhetherAdminUser("普通用户没有权限去上架图书");//权限检查通过((AdminUser)realUser).addBook();}
}
//上架图书
public class AdminUser extends User {public void addBook(){scanner.nextLine();System.out.println("请输⼊书名:");String title = scanner.nextLine(); // 输⼊书名System.out.println("请输⼊作者:");String author = scanner.nextLine(); // 输⼊作者System.out.println("请输⼊类别:");String category = scanner.nextLine(); // 输⼊图书类别System.out.println("请输⼊出版年份:");int year = scanner.nextInt(); // 输⼊出版年份scanner.nextLine(); // 吞掉换⾏符LocalDate shelfDate = LocalDate.now(); // 当前时间作为上架时间Book newBook = new Book(title, author, category, year, shelfDate);// 创建新书对象//调⽤图书类 添加图书library.addBook(newBook);}
}
public class Library {public void addBook(Book book) {System.out.println("进行书籍上架中......");if(bookCount >= books.length){//说明满了System.out.println("书架满了,不能存放");/*这里我们以后进行扩容操作*/return;}//把书放进来books[bookCount] = book;//现在来撰写书籍id的自增逻辑//每一次根据最后一本书的id后加加//如果是书架的第一本书,我们就把这本书id置为1if(bookCount == 0){book.setBookId(1);}else{Book lastBook = books[bookCount-1];book.setBookId(lastBook.getBookId()+1);}bookCount++;//写回文件storeBook();System.out.println("图书上架成功!书名是:"+book.getTitle());}
}
- 更新书籍
//图书信息修改
public class ProxyUser {public void updateBook(){//老样子,进行权限检查System.out.println("更新图书中......");//首先检查权限checkUserWhetherAdminUser("普通用户没有权限去更新图书");//权限检查通过((AdminUser)realUser).updateBook();}
}
//图书信息修改
public class AdminUser extends User {public void updateBook(){System.out.println("目前图书的信息如下");library.displayBook();System.out.println("请输入你想要修改的图书id");int bookId = scanner.nextInt();//吞掉换行符scanner.nextLine();Book book = library.searchBookById(bookId);if(book == null){System.out.println("当前书籍id不存在");return;}System.out.println("当前书名:" + book.getTitle());System.out.println("请输⼊新的书名:");String newTitle = scanner.nextLine(); // 输⼊新的书名System.out.println("当前作者:" + book.getAuthor());System.out.println("请输⼊新的作者:");String newAuthor = scanner.nextLine(); // 输⼊新的作者System.out.println("当前类别:" + book.getCategory());System.out.println("请输⼊新的类别:");String newCategory = scanner.nextLine(); // 输⼊新的类别//更新对应书籍的信息book.setTitle(newTitle);book.setAuthor(newAuthor);book.setCategory(newCategory);//转到图书馆类实现具体细节library.updateBook(book);//将书籍的信息进行再次的打印System.out.println(book);}
}
public class Library {
//我们在管理类中就已经对书籍信息进行了修改,只需要写入文件就好public void updateBook(Book book) {//⼀定要进⾏存储storeBook();}
}
- 下架图书
//删除图书
public class ProxyUser {public void removeBook(){//老样子,进行权限检查System.out.println("删除图书中......");//首先检查权限checkUserWhetherAdminUser("普通用户没有权限去更新图书");//权限检查通过((AdminUser)realUser).removeBook();}
}
//删除图书
public class AdminUser extends User {public void removeBook(){library.displayBook();System.out.println("请输入书籍id");int bookId = scanner.nextInt();//吞掉换行符scanner.nextLine();//现在删除书籍有个问题//1,....... 2,....... 3,.......//假设删除2号书籍,则会变成//1,....... 3,.......//而我们期望变成//1,....... 2,.......//但是目前文件读写做不到,后续使用MySQLBook removeBook = library.searchBookById(bookId);if(removeBook == null){System.out.println("并无该书籍信息");return;}library.removeBook(removeBook.getBookId());}
}
public class Library {public void removeBook(int bookId) {//根据书籍找到其下标int index = searchBookByIdReturnIndex(bookId);if(index == -1){System.out.println("并无该书籍信息,无法删除");return;}//检查书籍是否被借阅Book bookToRemove = books[index];if(bookToRemove.isBorrowed()) {System.out.println("[" + bookToRemove.getTitle() + "]当前已被借阅,无法删除!");System.out.println("-----------------------------------------------------------------------------------------------------------------------------");return;}//为什么这么设计,我们是让后面的书往前覆盖,然后把最后一本书的地方设置为null//为社么循环的终止条件是bookCount-1呢,因为我们删除一本书后,bookCount就会少一个//如果不写-1会导致null的异常for (int i = index; i < bookCount-1; i++) {books[i] = books[i+1];}books[bookCount-1] = null;bookCount--;//再把信息写回文件System.out.println("该书籍已成功删除");storeBook();}
}
- 查看借阅次数
public class ProxyUser {
//统计每本书的借阅次数public void borrowCount(){System.out.println("正在查看借阅的次数中");//首先检查权限checkUserWhetherAdminUser("普通用户没有权限去查看借阅次数");//权限检查通过((AdminUser)realUser).borrowCount();}
}
public class AdminUser extends User {
//统计每本书的借阅次数public void borrowCount(){library.borrowCount();}
}
public class Library {public void borrowCount() {loadAllBooks();System.out.println("-----------------------------------------------------------------------------------------------------------------------------");for (int i = 0; i < bookCount; i++) {Book book = books[i];System.out.println("书名是:"+book.getTitle()+" 的借阅次数为["+book.getBorrowCount()+"]次");}System.out.println("-----------------------------------------------------------------------------------------------------------------------------");}
}
- 查看最受欢迎的前
k本书
public class ProxyUser {
//查看最受欢迎的前k本书public void generateBook(){//首先检查权限checkUserWhetherAdminUser("普通用户没有权限去查看最受欢迎的前k本书");//权限检查通过((AdminUser)realUser).generateBook();}
}
//查看最受欢迎的前k本书
public class AdminUser extends User {public void generateBook(){System.out.println("最受欢迎的前k本书,输入不可以超过"+library.getBookCount()+"本");int k = scanner.nextInt();if(k <= 0 || k > library.getBookCount()){System.out.println("非法输入或k值过大");return;}library.generateBook(k);}
}
public class Library {public void generateBook(int k) {//1. 加载已有的全部的书籍loadAllBooks();//2.把所有书籍放在 临时数据 进⾏排序Book[] tmp = new Book[getBookCount()];for (int i = 0; i < getBookCount(); i++) {tmp[i] = books[i];}//2.1 开始排序Arrays.sort(tmp);//3. 把前k本书拷⻉到新数组 可以不定义临时数组,直接输出前K个就⾏Book[] generateBooks = new Book[k];for (int i = 0; i < k; i++) {generateBooks[i] = tmp[i];}//4.打印新数组System.out.println("-----------------------------------------------------------------------------------------------------------------------------");System.out.println("最受欢迎书籍如下:");for (int i = 0; i < generateBooks.length; i++) {Book book = generateBooks[i];System.out.println("索引: "+i+" 书名:"+ book.getTitle()+" 作者:"+book.getTitle()+" 借阅次数:"+book.getBorrowCount());}System.out.println("-----------------------------------------------------------------------------------------------------------------------------");}
}
- 查看库存状态
//查看库存状态
public class ProxyUser {public void checkInventoryStatus(){System.out.println("查案书籍库存");//首先检查权限checkUserWhetherAdminUser("普通用户没有权限去查看书籍库存");//权限检查通过((AdminUser)realUser).checkInventoryStatus();}
}
public class AdminUser extends User {
//查看库存状态public void checkInventoryStatus(){library.checkInventoryStatus();}
}
public class Library {
public void checkInventoryStatus() {loadAllBooks();System.out.println("-----------------------------------------------------------------------------------------------------------------------------");for (int i = 0; i < bookCount; i++) {Book book = books[i];//把内容信息中的true和false转换成文字化形式String status = book.isBorrowed() ? "已借出" : "在馆";System.out.println("书名:"+book.getTitle()+" || 目前状态:"+status);}System.out.println("-----------------------------------------------------------------------------------------------------------------------------");}
}
- 移除上架超过一年的书籍
public class ProxyUser {
//移除上架超过一年的图书public void checkAndRemoveOldBook(){System.out.println("移除上架超过一年的书籍");//首先检查权限checkUserWhetherAdminUser("普通用户没有权限去移除上架超过一年的书籍");//权限检查通过((AdminUser)realUser).checkAndRemoveOldBook();}
}
public class AdminUser extends User {public void checkAndRemoveOldBook(){library.checkAndRemoveOldBook();}
}
public class Library {public void checkAndRemoveOldBook() {//我们还是采用后面的书往前覆盖的方式删除,但是为什么我们代码中有一个i--呢//因为我们删除书籍后,每一本未被删除的书籍下标会变化,如果不减减,会导致遗漏//书籍一,...... 书籍二,...... 书籍三,......//删除书籍二,之后i下标的位置是书籍二,但是如果i不减减,上面循环的i++会导致此时i在书籍三//就会造成书籍二的遗漏判断System.out.println("-----------------------------------------------------------------------------------------------------------------------------");loadAllBooks();// 获取当前时间戳long currentTimestamp = System.currentTimeMillis();// 将当前时间戳转换为 LocalDateLocalDate currentDate = Instant.ofEpochMilli(currentTimestamp).atZone(ZoneId.systemDefault()).toLocalDate();boolean flg = false;for (int i = 0; i < getBookCount(); i++) {Book book = books[i];if (book == null){// 添加空指针检查continue;}//获取当前书籍的上架时间LocalDate specifiedDate = book.getShelfDate();// 计算两个⽇期之间的差值(以年为单位)long yearsBetween = ChronoUnit.YEARS.between(specifiedDate, currentDate);if(yearsBetween >= 1) {System.out.print("图书 " + book.getTitle() + " 已经上架超过⼀年,是否移除? (y/n):");String response = scanner.nextLine();if (response.equalsIgnoreCase("y")) {//确认删除调⽤remove⽅法进⾏删除//removeBook需要的是bookId,因此我们不能传入索引代表iremoveBook(book.getBookId());i--; // 因为后⾯的书已经向前移动,所以要重新检查当前索引位置}flg = true;}}if(!flg) {System.out.println("没有上架超过⼀年的图书!");}System.out.println("-----------------------------------------------------------------------------------------------------------------------------");}
}
12. 继续实现User模块的普通用户类
- 借阅图书
//借书
public class ProxyUser {public void borrowBook(){checkRealUserWhetherNormal("管理员请以普通用户的⽅式借阅图书");((NormalUser) realUser).borrowBook();}
}
public class NormalUser extends User {
//借书public void borrowBook(){//我们的借阅文件是这么存储信息的//用户id 书籍id//2 1library.displayBook();System.out.println("请输入你想借阅的书籍的id");int bookId = scanner.nextInt();//吞掉换行符scanner.nextLine();if(library.getBookCount() == 0){System.out.println("书架内并无书籍,无法借阅");return;}//加载所有书籍信息loadBorrowedBook();Book book = library.searchBookById(bookId);if(book == null){System.out.println("并无此书");return;}//检查通过loadBorrowedBook()加载到pairOfUidAndBookIds数组当中//是否有bookId == 当前需要借阅的图书ID && UID也是⼀样的 说明当前用户借阅过//否则就是其他用户借阅过for (int i = 0; i < borrowedCount; i++) {PairOfUidAndBookId pairOfUidAndBookId = pairOfUidAndBookIds[i];//找到了对应的书籍if (pairOfUidAndBookId.getBookId() == book.getBookId()) {if (getUserID() == pairOfUidAndBookId.getUserId()) {System.out.println("该书已经被你借阅过了,你的ID是:" +getUserID());return;} else {System.out.println("该书已经被其他⼈借阅过了,他的ID是:" +pairOfUidAndBookId.getUserId());return;}}}//此时这本书没有被你或者是其他人借阅过library.borrowBook(bookId);//封装对象写到 借阅表当中PairOfUidAndBookId pairOfUidAndBookId = new PairOfUidAndBookId(userID, book.getBookId());pairOfUidAndBookIds[borrowedCount] = pairOfUidAndBookId;borrowedCount++;//存储借阅图书storeBorrowedBook();System.out.println("借阅成功!");}
}
public class Library {public void borrowBook(int bookId) {loadAllBooks();for (int i = 0; i < getBookCount(); i++) {Book book = books[i];if(book.getBookId()== bookId) {//修改借阅信息book.setBorrowed(true);//自增book.incrementBorrowCount();}}storeBook();}
}
- 还书
public class ProxyUser {
//还书public void returnBook(){checkRealUserWhetherNormal("管理员请以普通用户的方式还书");((NormalUser) realUser).returnBook();}
}
public class NormalUser extends User {
//还书public void returnBook(){loadBorrowedBook();if (borrowedCount == 0) {System.out.println("目前没有用户借阅过书籍");return;}// 先打印当前用户的借阅情况System.out.println("-----------------------------------------------------------------------------------------------------------------------------");System.out.println("您的当前借阅情况:");boolean hasBorrowedBooks = false;for (int i = 0; i < borrowedCount; i++) {PairOfUidAndBookId pair = pairOfUidAndBookIds[i];if (pair.getUserId() == userID) {Book book = library.searchBookById(pair.getBookId());if (book != null) {System.out.println("书籍ID: " + book.getBookId() + " | 书名: " + book.getTitle() +" | 作者: " + book.getAuthor());hasBorrowedBooks = true;}}}if (!hasBorrowedBooks) {System.out.println("您当前没有借阅任何书籍。");System.out.println("-----------------------------------------------------------------------------------------------------------------------------");return;}System.out.println("-----------------------------------------------------------------------------------------------------------------------------");System.out.println("请输入你要归还的书籍ID:");int bookId = scanner.nextInt();scanner.nextLine();// 判断要归还的书是否已经被自己借阅过Book book = library.searchBookById(bookId);if(book == null) {System.out.println("没有该ID的相关书籍:" + bookId);return;}// 还书逻辑保持不变for (int i = 0; i < borrowedCount; i++) {if (pairOfUidAndBookIds[i].getBookId() == book.getBookId()) {if (getUserID() == pairOfUidAndBookIds[i].getUserId()) {library.returnBook(bookId);System.out.println("图书 '" + book.getTitle() + "' 已成功归还。");// 用最后一本替换归还的书pairOfUidAndBookIds[i] = pairOfUidAndBookIds[borrowedCount - 1];pairOfUidAndBookIds[borrowedCount - 1] = null;borrowedCount--;storeBorrowedBook();} else {System.out.println("该书籍不是你借阅的书籍,不能归还:" + book.getTitle());}System.out.println("-----------------------------------------------------------------------------------------------------------------------------");return;}}System.out.println("你没有借阅该书籍,不需要归还,书籍ID为:" + bookId);System.out.println("-----------------------------------------------------------------------------------------------------------------------------");}
}
public class Library {public void returnBook(int bookId) {//就是把存储图书的文件信息的借阅状态设置为falseloadAllBooks();for (int i = 0; i < getBookCount(); i++) {Book book = books[i];if(book.getBookId()==bookId) {book.setBorrowed(false);book.decreaseBorrowCount();}}storeBook();}
}
- 查看个人的借阅情况
public class ProxyUser {
//查看个人借阅情况public void viewBorrowBooks(){checkRealUserWhetherNormal("管理员请以普通用户的⽅式查看借阅情况");((NormalUser) realUser).viewBorrowBooks();}
}
public class NormalUser extends User {
//查看个人借阅情况public void viewBorrowBooks(){//因为我们借阅信息的表中存的只有用户id和书籍id,也就是说我们要根据书籍id反向找到书籍//根据用户id判断,即使borrowCount不为0也还是要判断,如果这本书是别人借的呢?因此我们定义flagloadBorrowedBook();System.out.println("-----------------------------------------------------------------------------------------------------------------------------");System.out.println("您的借阅情况如下:");if (borrowedCount == 0) {System.out.println("⽬前没有借阅记录.....");} else {boolean flg = false;for (int i = 0; i < borrowedCount; i++) {//这⾥只能查看属于⾃⼰借阅的情况// ⽤⼾ID相同的情况下,使⽤书籍ID查询书籍if(pairOfUidAndBookIds[i].getUserId() == userID) {flg = true;Book book = library.searchBookById(pairOfUidAndBookIds[i].getBookId());System.out.println(book);}}if(!flg) {System.out.println("你没有借阅过书籍!");}System.out.println("-----------------------------------------------------------------------------------------------------------------------------");}}
}
13. 完善最后的Library类模块中的共通方法
public void searchBook(){System.out.println("查找图书中......");System.out.println("请输入你想要查找的图书名称");String title = scanner.nextLine();Book book = searchBookBTitle(title);if(book == null){System.out.println("并未找到指定书籍");return;}System.out.println("找到了你想找的书籍,信息如下");System.out.println("-----------------------------------------------------------------------------------------------------------------------------");System.out.println(book);System.out.println("-----------------------------------------------------------------------------------------------------------------------------");}
其他的比如退出系统,或者是打印所有图书我们都已经写过了
六、总结以及源码展示
现在我是第二次复盘这个项目,明白了很多东西,其实这么多类调来调去,就是代理-->具体用户类型-->图书馆类之间倒腾
现在我已经充分认识到了单例模式、工厂模式、代理模式的厉害之处了
可以直接看我的仓库,克隆代码到本地仓库
下面我给出源码,以及lib读写流的源码
请务必按照我图片的方式,创建不同的类以及包

1. Book模块
package Book;import java.time.LocalDate;
import java.time.format.DateTimeFormatter;/*** @author pluchon* @create 2025-10-26-12:22* 作者代码水平一般,难免难看,请见谅*/
public class Book implements Comparable<Book>{private int bookId; //书idprivate String title; // 书名private String author; // 作者private String category; // 类别private int publishYear; // 出版年份private boolean isBorrowed; // 借阅状态private int borrowCount; // 借阅次数private LocalDate shelfDate; // 上架时间public Book(String title, String author, String category, int publishYear, LocalDate shelfDate) {this.title = title;this.author = author;this.category = category;this.publishYear = publishYear;this.shelfDate = shelfDate;}public int getBookId() {return bookId;}public void setBookId(int bookId) {this.bookId = bookId;}public String getTitle() {return title;}public void setTitle(String title) {this.title = title;}public String getAuthor() {return author;}public void setAuthor(String author) {this.author = author;}public String getCategory() {return category;}public void setCategory(String category) {this.category = category;}public int getPublishYear() {return publishYear;}public void setPublishYear(int publishYear) {this.publishYear = publishYear;}public boolean isBorrowed() {return isBorrowed;}public void setBorrowed(boolean borrowed) {isBorrowed = borrowed;}public int getBorrowCount() {return borrowCount;}public void setBorrowCount(int borrowCount) {this.borrowCount = borrowCount;}public LocalDate getShelfDate() {return shelfDate;}public void setShelfDate(LocalDate shelfDate) {this.shelfDate = shelfDate;}@Overridepublic String toString() {return "Book{" +"书籍id:" + bookId +", 书名:" + title + '\'' +", 作者:" + author + '\'' +", 种类:" + category + '\'' +", 发行年份:" + publishYear +", 是否被借阅:" + (isBorrowed ? "已被借阅" : "未被借阅") +", 借阅次数:" + borrowCount +", 上架日期:" + shelfDate +'}';}//针对一本书的信息进行整合,变成一个字符串public String toJson(){StringBuilder json = new StringBuilder();json.append(bookId).append(",");json.append(title).append(",");json.append(author).append(",");json.append(category).append(",");json.append(publishYear).append(",");json.append(isBorrowed).append(",");json.append(borrowCount).append(",");json.append(shelfDate != null ? shelfDate.format(DateTimeFormatter.ISO_LOCAL_DATE) : "null");return json.toString();}//为最受欢迎的前k本书做准备,从大到小排序@Overridepublic int compareTo(Book o) {return o.getBorrowCount()-this.getBorrowCount();}public void incrementBorrowCount() {borrowCount++;}public void decreaseBorrowCount() {borrowCount--;}
}
package Book;
import Book.Library;
import Constant.Constant;
import Util.AnalyzingBook;import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.Scanner;/*** @author pluchon* @create 2025-10-26-12:26* 作者代码水平一般,难免难看,请见谅*/
public class Library {private Book[] books;//当前图书数组private int bookCount;//实际存储的图书数量private static Library library;//保证只有一个图书馆,即单例模式Scanner scanner = new Scanner(System.in);private final AnalyzingBook analyzingBook = new AnalyzingBook();public Library() {//当调⽤该构造⽅法的时候,每次要加载⽂件当中的数据进⾏到books数组当中loadAllBooks();}public static Library getLibrary(){if(library == null){library = new Library();}return library;}private void loadAllBooks() {//先读取文件的内容Book[] allBook = analyzingBook.loadObject(Constant.ALL_BOOK_FILE_NAME);books = new Book[Constant.CAPACITY];//判断有效数据个数if(allBook == null){bookCount = 0;}else{//查案实际书籍的长度,即看书架是否满了int allBookLength = allBook.length;if(allBookLength > books.length){//大于实际长度,说明当前的书架放不下那么多书,因此我们就根据实际有多少本就找多大的书架books = new Book[allBookLength];}for (int i = 0; i < allBookLength; i++) {//将读取到的元素进行赋值,以返回实时的图书信息books[i] = allBook[i];}//修改实际的有效数据个数bookCount = allBookLength;}}private void storeBook(){analyzingBook.storeObject(books,Constant.ALL_BOOK_FILE_NAME);}//下面三个方法是不同用户类型的共同方法public void searchBook(){System.out.println("查找图书中......");System.out.println("请输入你想要查找的图书名称");String title = scanner.nextLine();Book book = searchBookBTitle(title);if(book == null){System.out.println("并未找到指定书籍");return;}System.out.println("找到了你想找的书籍,信息如下");System.out.println("-----------------------------------------------------------------------------------------------------------------------------");System.out.println(book);System.out.println("-----------------------------------------------------------------------------------------------------------------------------");}private Book searchBookBTitle(String title) {for (int i = 0; i < bookCount; i++) {Book book = books[i];if(book.getTitle().equals(title)){return book;}}return null;}public void displayBook(){System.out.println("图书馆书籍信息如下");loadAllBooks();System.out.println("-----------------------------------------------------------------------------------------------------------------------------");for (int i = 0; i < bookCount; i++) {System.out.println(books[i]);}System.out.println("-----------------------------------------------------------------------------------------------------------------------------");}public void exit(){System.out.println("退出");System.exit(0);}public void addBook(Book book) {System.out.println("进行书籍上架中......");if(bookCount >= books.length){//说明满了System.out.println("书架满了,不能存放");/*这里我们以后进行扩容操作*/return;}//把书放进来books[bookCount] = book;//现在来撰写书籍id的自增逻辑//每一次根据最后一本书的id后加加//如果是书架的第一本书,我们就把这本书id置为1if(bookCount == 0){book.setBookId(1);}else{Book lastBook = books[bookCount-1];book.setBookId(lastBook.getBookId()+1);}bookCount++;//写回文件storeBook();System.out.println("图书上架成功!书名是:"+book.getTitle());}public Book searchBookById(int bookId) {for (int i = 0; i < bookCount; i++) {if(books[i].getBookId() == bookId){return books[i];}}return null;}//根据书籍的id找到其下标位置private int searchBookByIdReturnIndex(int bookId){for (int i = 0; i < bookCount; i++) {Book book = books[i];if(book.getBookId() == bookId){return i;}}return -1;}//我们在管理类中就已经对书籍信息进行了修改,只需要写入文件就好public void updateBook(Book book) {//⼀定要进⾏存储storeBook();}public void removeBook(int bookId) {//根据书籍找到其下标int index = searchBookByIdReturnIndex(bookId);if(index == -1){System.out.println("并无该书籍信息,无法删除");return;}//检查书籍是否被借阅Book bookToRemove = books[index];if(bookToRemove.isBorrowed()) {System.out.println("[" + bookToRemove.getTitle() + "]当前已被借阅,无法删除!");System.out.println("-----------------------------------------------------------------------------------------------------------------------------");return;}//为什么这么设计,我们是让后面的书往前覆盖,然后把最后一本书的地方设置为null//为社么循环的终止条件是bookCount-1呢,因为我们删除一本书后,bookCount就会少一个//如果不写-1会导致null的异常for (int i = index; i < bookCount-1; i++) {books[i] = books[i+1];}books[bookCount-1] = null;bookCount--;//再把信息写回文件System.out.println("该书籍已成功删除");storeBook();}public void borrowCount() {loadAllBooks();System.out.println("-----------------------------------------------------------------------------------------------------------------------------");for (int i = 0; i < bookCount; i++) {Book book = books[i];System.out.println("书名是:"+book.getTitle()+" 的借阅次数为["+book.getBorrowCount()+"]次");}System.out.println("-----------------------------------------------------------------------------------------------------------------------------");}public int getBookCount() {return bookCount;}public void generateBook(int k) {//1. 加载已有的全部的书籍loadAllBooks();//2.把所有书籍放在 临时数据 进⾏排序Book[] tmp = new Book[getBookCount()];for (int i = 0; i < getBookCount(); i++) {tmp[i] = books[i];}//2.1 开始排序Arrays.sort(tmp);//3. 把前k本书拷⻉到新数组 可以不定义临时数组,直接输出前K个就⾏Book[] generateBooks = new Book[k];for (int i = 0; i < k; i++) {generateBooks[i] = tmp[i];}//4.打印新数组System.out.println("-----------------------------------------------------------------------------------------------------------------------------");System.out.println("最受欢迎书籍如下:");for (int i = 0; i < generateBooks.length; i++) {Book book = generateBooks[i];System.out.println("索引: "+i+" 书名:"+ book.getTitle()+" 作者:"+book.getTitle()+" 借阅次数:"+book.getBorrowCount());}System.out.println("-----------------------------------------------------------------------------------------------------------------------------");}public void checkInventoryStatus() {loadAllBooks();System.out.println("-----------------------------------------------------------------------------------------------------------------------------");for (int i = 0; i < bookCount; i++) {Book book = books[i];//把内容信息中的true和false转换成文字化形式String status = book.isBorrowed() ? "已借出" : "在馆";System.out.println("书名:"+book.getTitle()+" || 目前状态:"+status);}System.out.println("-----------------------------------------------------------------------------------------------------------------------------");}public void checkAndRemoveOldBook() {//我们还是采用后面的书往前覆盖的方式删除,但是为什么我们代码中有一个i--呢//因为我们删除书籍后,每一本未被删除的书籍下标会变化,如果不减减,会导致遗漏//书籍一,...... 书籍二,...... 书籍三,......//删除书籍二,之后i下标的位置是书籍二,但是如果i不减减,上面循环的i++会导致此时i在书籍三//就会造成书籍二的遗漏判断System.out.println("-----------------------------------------------------------------------------------------------------------------------------");loadAllBooks();// 获取当前时间戳long currentTimestamp = System.currentTimeMillis();// 将当前时间戳转换为 LocalDateLocalDate currentDate = Instant.ofEpochMilli(currentTimestamp).atZone(ZoneId.systemDefault()).toLocalDate();boolean flg = false;for (int i = 0; i < getBookCount(); i++) {Book book = books[i];if (book == null){// 添加空指针检查continue;}//获取当前书籍的上架时间LocalDate specifiedDate = book.getShelfDate();// 计算两个⽇期之间的差值(以年为单位)long yearsBetween = ChronoUnit.YEARS.between(specifiedDate, currentDate);if(yearsBetween >= 1) {System.out.print("图书 " + book.getTitle() + " 已经上架超过⼀年,是否移除? (y/n):");String response = scanner.nextLine();if (response.equalsIgnoreCase("y")) {//确认删除调⽤remove⽅法进⾏删除//removeBook需要的是bookId,因此我们不能传入索引代表iremoveBook(book.getBookId());i--; // 因为后⾯的书已经向前移动,所以要重新检查当前索引位置}flg = true;}}if(!flg) {System.out.println("没有上架超过⼀年的图书!");}System.out.println("-----------------------------------------------------------------------------------------------------------------------------");}public void borrowBook(int bookId) {loadAllBooks();for (int i = 0; i < getBookCount(); i++) {Book book = books[i];if(book.getBookId()== bookId) {//修改借阅信息book.setBorrowed(true);//自增book.incrementBorrowCount();}}storeBook();}public void returnBook(int bookId) {//就是把存储图书的文件信息的借阅状态设置为falseloadAllBooks();for (int i = 0; i < getBookCount(); i++) {Book book = books[i];if(book.getBookId()==bookId) {book.setBorrowed(false);book.decreaseBorrowCount();}}storeBook();}
}
package Book;/*** @author pluchon* @create 2025-10-26-13:47* 作者代码水平一般,难免难看,请见谅*/
public class PairOfUidAndBookId {//专门分析借阅信息private int userId;private int bookId;public PairOfUidAndBookId() {}public PairOfUidAndBookId(int userId, int bookId) {this.userId = userId;this.bookId = bookId;}public int getUserId() {return userId;}public void setUserId(int userId) {this.userId = userId;}public int getBookId() {return bookId;}public void setBookId(int bookId) {this.bookId = bookId;}//把对象序列化为JSON字符串的形式public String toJson() {StringBuilder json = new StringBuilder();json.append(userId).append(",");json.append(bookId);return json.toString();}
}
2. Constant模块
package Constant;/*** @author pluchon* @create 2025-10-26-12:56* 作者代码水平一般,难免难看,请见谅*/
public class Constant {//内存中的书籍数组初识容量public static final int CAPACITY = 5;//存储所有图书的⽂件public static final String ALL_BOOK_FILE_NAME = "allBooks.txt";//存储借阅信息的文件public static final String ALL_BOOK_BORROW = "borrow.txt";//-------------------管理员相关操作管理----------------
//查找图书public static final int SEARCH_BOOK = 1;//显⽰图书public static final int DISPLAY_BOOK = 2;//退出系统public static final int EXIT = 3;//上架图书public static final int ADD_BOOK = 4;//更新图书public static final int UPDATE_BOOK = 5;//删除图书public static final int REMOVE_BOOK = 6;//查看图书的借阅次数public static final int BORROWED_BOOK_COUNT = 7;//查看受欢迎的图书public static final int GENERATE_BOOK = 8;//查看库存状态public static final int CHECK_INVENTORY_STATUS = 9;//移除上架超过1年的书籍public static final int CHECK_AND_REMOVE_OLD_BOOK = 10;//-------------------普通⽤⼾相关操作管理----------------//借阅图书public static final int BORROWED_BOOK = 4;//归还图书public static final int RETURN_BOOK = 5;//查看个⼈借阅情况public static final int VIEW_BORROW_HISTORY_BOOK = 6;
}
3. lib读写流模块(此代码非我原创!!!)
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//package com.bit.utils;import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;public class FileUtils {public static void writeFile(String jsonArray, String filename) {File file = new File(filename);if (!file.exists()) {try {File parent = file.getParentFile();if (parent != null && !parent.exists() && !parent.mkdirs()) {throw new IOException("无法创建目录: " + parent);}if (file.createNewFile()) {System.out.println("文件不存在,已创建新文件: " + filename);}} catch (IOException e) {System.out.println("创建文件失败: " + filename);throw new RuntimeException(e);}}try {try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(filename), StandardCharsets.UTF_8))) {writer.write(jsonArray);}} catch (IOException e) {System.out.println("写入文件错误...." + filename);throw new RuntimeException(e);}}public static String readFile(String filename) {File file = new File(filename);if (!file.exists()) {System.out.println("文件不存在: " + filename);return null;} else if (!file.isFile()) {System.out.println("指定路径不是一个文件: " + filename);return null;} else {StringBuilder content = new StringBuilder();String line;try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(filename), StandardCharsets.UTF_8))) {while((line = reader.readLine()) != null) {content.append(line);content.append("\n");}} catch (IOException e) {System.out.println("读取文件时发生错误: " + filename);e.printStackTrace();return null;}return content.toString();}}
}
4. User模块
package User;
import Book.Library;
import Book.Book;import java.time.LocalDate;
import java.util.Scanner;/*** @author pluchon* @create 2025-10-26-11:38* 作者代码水平一般,难免难看,请见谅*/
public class AdminUser extends User {Scanner scanner = new Scanner(System.in);private Library library = null;//创建管理员对象的时候才实例化图书馆public AdminUser(String name, int userID) {super(name, userID, "管理员");library = Library.getLibrary();}@Overridepublic int display() {System.out.println("管理员[" + name + "]的操作菜单:");System.out.println("1. 查找图书");System.out.println("2. 打印所有的图书");System.out.println("3. 退出系统");System.out.println("4. 上架图书");System.out.println("5. 修改图书");System.out.println("6. 下架图书");System.out.println("7. 统计借阅次数");System.out.println("8. 查看最后欢迎的前K本书");System.out.println("9. 查看库存状态");System.out.println("10. 检查超过⼀年未下架的图书");System.out.println("请选择你的操作:");return scanner.nextInt();}//上架图书public void addBook(){scanner.nextLine();System.out.println("请输⼊书名:");String title = scanner.nextLine(); // 输⼊书名System.out.println("请输⼊作者:");String author = scanner.nextLine(); // 输⼊作者System.out.println("请输⼊类别:");String category = scanner.nextLine(); // 输⼊图书类别System.out.println("请输⼊出版年份:");int year = scanner.nextInt(); // 输⼊出版年份scanner.nextLine(); // 吞掉换⾏符LocalDate shelfDate = LocalDate.now(); // 当前时间作为上架时间Book newBook = new Book(title, author, category, year, shelfDate);// 创建新书对象//调⽤图书类 添加图书library.addBook(newBook);}//图书信息修改public void updateBook(){System.out.println("目前图书的信息如下");library.displayBook();System.out.println("请输入你想要修改的图书id");int bookId = scanner.nextInt();//吞掉换行符scanner.nextLine();Book book = library.searchBookById(bookId);if(book == null){System.out.println("当前书籍id不存在");return;}System.out.println("当前书名:" + book.getTitle());System.out.println("请输⼊新的书名:");String newTitle = scanner.nextLine(); // 输⼊新的书名System.out.println("当前作者:" + book.getAuthor());System.out.println("请输⼊新的作者:");String newAuthor = scanner.nextLine(); // 输⼊新的作者System.out.println("当前类别:" + book.getCategory());System.out.println("请输⼊新的类别:");String newCategory = scanner.nextLine(); // 输⼊新的类别//更新对应书籍的信息book.setTitle(newTitle);book.setAuthor(newAuthor);book.setCategory(newCategory);//转到图书馆类实现具体细节library.updateBook(book);//将书籍的信息进行再次的打印System.out.println(book);}//删除图书public void removeBook(){library.displayBook();System.out.println("请输入书籍id");int bookId = scanner.nextInt();//吞掉换行符scanner.nextLine();//现在删除书籍有个问题//1,....... 2,....... 3,.......//假设删除2号书籍,则会变成//1,....... 3,.......//而我们期望变成//1,....... 2,.......//但是目前文件读写做不到,后续使用MySQLBook removeBook = library.searchBookById(bookId);if(removeBook == null){System.out.println("并无该书籍信息");return;}library.removeBook(removeBook.getBookId());}//统计每本书的借阅次数public void borrowCount(){library.borrowCount();}//查看最受欢迎的前k本书public void generateBook(){System.out.println("最受欢迎的前k本书,输入不可以超过"+library.getBookCount()+"本");int k = scanner.nextInt();if(k <= 0 || k > library.getBookCount()){System.out.println("非法输入或k值过大");return;}library.generateBook(k);}//查看库存状态public void checkInventoryStatus(){library.checkInventoryStatus();}//移除上架超过一年的图书public void checkAndRemoveOldBook(){library.checkAndRemoveOldBook();}
}
package User;import Book.Book;
import Book.Library;
import Book.PairOfUidAndBookId;
import Util.AnalyzingBook;
import Util.AnalyzingBorrowedBook;
import Constant.Constant;
import java.util.Scanner;/*** @author pluchon* @create 2025-10-26-11:40* 作者代码水平一般,难免难看,请见谅*/
public class NormalUser extends User {Scanner scanner = new Scanner(System.in);private Library library;//用户借阅图书的相关信息private PairOfUidAndBookId[] pairOfUidAndBookIds;//当前书籍的借阅量private int borrowedCount;//最多借阅书籍数目private static final int BORROW_BOOK_MAX_NUM = 5;//借阅信息分析工具类加载private final AnalyzingBorrowedBook analyzingBorrowedBook = new AnalyzingBorrowedBook();public NormalUser(String name, int useId) {super(name, useId, "普通用户");loadBorrowedBook();library = Library.getLibrary();}@Overridepublic int display() {System.out.println("普通用户[" + name + "]的操作菜单:");System.out.println("1. 查找图书");System.out.println("2. 打印所有的图书");System.out.println("3. 退出系统");System.out.println("4. 借阅图书");System.out.println("5. 归还图书");System.out.println("6. 查看当前个⼈借阅情况");System.out.println("请选择你的操作:");return scanner.nextInt();}//加载所有借阅书籍private void loadBorrowedBook(){PairOfUidAndBookId[] allBorrowedBook;//1.先加载⽂件当中的借阅信息allBorrowedBook = analyzingBorrowedBook.loadObject(Constant.ALL_BOOK_BORROW);//2. 默认已借阅的图书数组⼤⼩为BORROW_BOOK_MAX_NUM,这⾥也可以定义到常量类pairOfUidAndBookIds = new PairOfUidAndBookId[BORROW_BOOK_MAX_NUM];//3.没有读取到已借阅的图书信息if (allBorrowedBook == null) {borrowedCount = 0;} else {//4. 查看实际读取到的数组⻓度是多少?int allBorrowedBookLen = allBorrowedBook.length;//5. 如果读取到了10本书被借阅 但是当前borrowedBooks数组⻓度⼩于10if (allBorrowedBookLen > pairOfUidAndBookIds.length) {//6. 按照实际情况进⾏分配数组内存pairOfUidAndBookIds = new PairOfUidAndBookId[allBorrowedBookLen];}//7. 把数据拷⻉回到 已借阅图书信息的数组当中for (int i = 0; i < allBorrowedBookLen; i++) {pairOfUidAndBookIds[i] = allBorrowedBook[i];}//8. 更新当前实际借阅书籍的书籍数量borrowedCount = allBorrowedBookLen;}}//存储所有借阅的书籍private void storeBorrowedBook(){analyzingBorrowedBook.storeObject(pairOfUidAndBookIds, Constant.ALL_BOOK_BORROW);}//借书public void borrowBook(){//我们的借阅文件是这么存储信息的//用户id 书籍id//2 1library.displayBook();System.out.println("请输入你想借阅的书籍的id");int bookId = scanner.nextInt();//吞掉换行符scanner.nextLine();if(library.getBookCount() == 0){System.out.println("书架内并无书籍,无法借阅");return;}//加载所有书籍信息loadBorrowedBook();Book book = library.searchBookById(bookId);if(book == null){System.out.println("并无此书");return;}//检查通过loadBorrowedBook()加载到pairOfUidAndBookIds数组当中//是否有bookId == 当前需要借阅的图书ID && UID也是⼀样的 说明当前用户借阅过//否则就是其他用户借阅过for (int i = 0; i < borrowedCount; i++) {PairOfUidAndBookId pairOfUidAndBookId = pairOfUidAndBookIds[i];//找到了对应的书籍if (pairOfUidAndBookId.getBookId() == book.getBookId()) {if (getUserID() == pairOfUidAndBookId.getUserId()) {System.out.println("该书已经被你借阅过了,你的ID是:" +getUserID());return;} else {System.out.println("该书已经被其他⼈借阅过了,他的ID是:" +pairOfUidAndBookId.getUserId());return;}}}//此时这本书没有被你或者是其他人借阅过library.borrowBook(bookId);//封装对象写到 借阅表当中PairOfUidAndBookId pairOfUidAndBookId = new PairOfUidAndBookId(userID, book.getBookId());pairOfUidAndBookIds[borrowedCount] = pairOfUidAndBookId;borrowedCount++;//存储借阅图书storeBorrowedBook();System.out.println("借阅成功!");}//还书public void returnBook(){loadBorrowedBook();if (borrowedCount == 0) {System.out.println("目前没有用户借阅过书籍");return;}// 先打印当前用户的借阅情况System.out.println("-----------------------------------------------------------------------------------------------------------------------------");System.out.println("您的当前借阅情况:");boolean hasBorrowedBooks = false;for (int i = 0; i < borrowedCount; i++) {PairOfUidAndBookId pair = pairOfUidAndBookIds[i];if (pair.getUserId() == userID) {Book book = library.searchBookById(pair.getBookId());if (book != null) {System.out.println("书籍ID: " + book.getBookId() + " | 书名: " + book.getTitle() +" | 作者: " + book.getAuthor());hasBorrowedBooks = true;}}}if (!hasBorrowedBooks) {System.out.println("您当前没有借阅任何书籍。");System.out.println("-----------------------------------------------------------------------------------------------------------------------------");return;}System.out.println("-----------------------------------------------------------------------------------------------------------------------------");System.out.println("请输入你要归还的书籍ID:");int bookId = scanner.nextInt();scanner.nextLine();// 判断要归还的书是否已经被自己借阅过Book book = library.searchBookById(bookId);if(book == null) {System.out.println("没有该ID的相关书籍:" + bookId);return;}// 还书逻辑保持不变for (int i = 0; i < borrowedCount; i++) {if (pairOfUidAndBookIds[i].getBookId() == book.getBookId()) {if (getUserID() == pairOfUidAndBookIds[i].getUserId()) {library.returnBook(bookId);System.out.println("图书 '" + book.getTitle() + "' 已成功归还。");// 用最后一本替换归还的书pairOfUidAndBookIds[i] = pairOfUidAndBookIds[borrowedCount - 1];pairOfUidAndBookIds[borrowedCount - 1] = null;borrowedCount--;storeBorrowedBook();} else {System.out.println("该书籍不是你借阅的书籍,不能归还:" + book.getTitle());}System.out.println("-----------------------------------------------------------------------------------------------------------------------------");return;}}System.out.println("你没有借阅该书籍,不需要归还,书籍ID为:" + bookId);System.out.println("-----------------------------------------------------------------------------------------------------------------------------");}//查看个人借阅情况public void viewBorrowBooks(){//因为我们借阅信息的表中存的只有用户id和书籍id,也就是说我们要根据书籍id反向找到书籍//根据用户id判断,即使borrowCount不为0也还是要判断,如果这本书是别人借的呢?因此我们定义flagloadBorrowedBook();System.out.println("-----------------------------------------------------------------------------------------------------------------------------");System.out.println("您的借阅情况如下:");if (borrowedCount == 0) {System.out.println("⽬前没有借阅记录.....");} else {boolean flg = false;for (int i = 0; i < borrowedCount; i++) {//这⾥只能查看属于⾃⼰借阅的情况// ⽤⼾ID相同的情况下,使⽤书籍ID查询书籍if(pairOfUidAndBookIds[i].getUserId() == userID) {flg = true;Book book = library.searchBookById(pairOfUidAndBookIds[i].getBookId());System.out.println(book);}}if(!flg) {System.out.println("你没有借阅过书籍!");}System.out.println("-----------------------------------------------------------------------------------------------------------------------------");}}
}
package User;/*** @author pluchon* @create 2025-10-26-13:23* 作者代码水平一般,难免难看,请见谅*/
public class PermissionException extends RuntimeException{public PermissionException(String message) {super(message);}
}
package User;import Book.Library;import Constant.Constant;/*** @author pluchon* @create 2025-10-26-12:13* 作者代码水平一般,难免难看,请见谅*/
public class ProxyUser {//被代理的是哪个用户private User realUser;private Library library = Library.getLibrary();public ProxyUser(User user) {this.realUser = user;}public User getRealUser() {return realUser;}public String getRealUserName(){return realUser.name;}private void checkRealUserWhetherNormal(String exceptionMessage){if(!(realUser instanceof NormalUser)){throw new PermissionException(exceptionMessage);}}//借书public void borrowBook(){checkRealUserWhetherNormal("管理员请以普通用户的⽅式借阅图书");((NormalUser) realUser).borrowBook();}//还书public void returnBook(){checkRealUserWhetherNormal("管理员请以普通用户的方式还书");((NormalUser) realUser).returnBook();}//查看个人借阅情况public void viewBorrowBooks(){checkRealUserWhetherNormal("管理员请以普通用户的⽅式查看借阅情况");((NormalUser) realUser).viewBorrowBooks();}//-----------------------------------------------////管理员用户方法//检查管理员权限问题private void checkUserWhetherAdminUser(String exceptionMessage){if(!(realUser instanceof AdminUser)){throw new PermissionException(exceptionMessage);}}//上架图书public void addBook(){System.out.println("上架图书中......");//首先检查权限checkUserWhetherAdminUser("普通用户没有权限去上架图书");//权限检查通过((AdminUser)realUser).addBook();}//图书信息修改public void updateBook(){//老样子,进行权限检查System.out.println("更新图书中......");//首先检查权限checkUserWhetherAdminUser("普通用户没有权限去更新图书");//权限检查通过((AdminUser)realUser).updateBook();}//删除图书public void removeBook(){//老样子,进行权限检查System.out.println("删除图书中......");//首先检查权限checkUserWhetherAdminUser("普通用户没有权限去更新图书");//权限检查通过((AdminUser)realUser).removeBook();}//统计每本书的借阅次数public void borrowCount(){System.out.println("正在查看借阅的次数中");//首先检查权限checkUserWhetherAdminUser("普通用户没有权限去查看借阅次数");//权限检查通过((AdminUser)realUser).borrowCount();}//查看最受欢迎的前k本书public void generateBook(){//首先检查权限checkUserWhetherAdminUser("普通用户没有权限去查看最受欢迎的前k本书");//权限检查通过((AdminUser)realUser).generateBook();}//查看库存状态public void checkInventoryStatus(){System.out.println("查案书籍库存");//首先检查权限checkUserWhetherAdminUser("普通用户没有权限去查看书籍库存");//权限检查通过((AdminUser)realUser).checkInventoryStatus();}//移除上架超过一年的图书public void checkAndRemoveOldBook(){System.out.println("移除上架超过一年的书籍");//首先检查权限checkUserWhetherAdminUser("普通用户没有权限去移除上架超过一年的书籍");//权限检查通过((AdminUser)realUser).checkAndRemoveOldBook();}//退出系统public void exit(){System.exit(0);}public void handleOperation(int choice) {if (realUser instanceof AdminUser) {// 管理员操作switch (choice) {case Constant.SEARCH_BOOK:library.searchBook();break;case Constant.DISPLAY_BOOK:library.displayBook();break;case Constant.EXIT:library.exit();break;case Constant.ADD_BOOK:addBook();break;case Constant.UPDATE_BOOK:updateBook();break;case Constant.REMOVE_BOOK:removeBook();break;case Constant.BORROWED_BOOK_COUNT:borrowCount();break;case Constant.GENERATE_BOOK:generateBook();break;case Constant.CHECK_INVENTORY_STATUS:checkInventoryStatus();break;case Constant.CHECK_AND_REMOVE_OLD_BOOK:checkAndRemoveOldBook();break;default:System.out.println("⽆效的操作。");}} else if (realUser instanceof NormalUser) {// 普通⽤⼾操作switch (choice) {case Constant.SEARCH_BOOK:library.searchBook();break;case Constant.DISPLAY_BOOK:library.displayBook();break;case Constant.EXIT:library.exit();case Constant.BORROWED_BOOK:borrowBook();break;case Constant.RETURN_BOOK:returnBook();break;case Constant.VIEW_BORROW_HISTORY_BOOK:viewBorrowBooks();break;default:System.out.println("⽆效的操作。");}}}
}
package User;/*** @author pluchon* @create 2025-10-26-11:36* 作者代码水平一般,难免难看,请见谅*/
public abstract class User {protected String name;protected String role;protected int userID;public User(String name, int id,String role) {this.name = name;this.userID = id;this.role = role;}public String getName() {return name;}public String getRole() {return role;}public int getUserID() {return userID;}//功能菜单打印public abstract int display();
}
工厂模块
package User.Factory;import User.User;/*** @author pluchon* @create 2025-10-26-11:42* 作者代码水平一般,难免难看,请见谅*/
public interface IPersonFactory {User createUser(String name, int useId);
}
package User.Factory;import User.AdminUser;
import User.User;/*** @author pluchon* @create 2025-10-26-12:04* 作者代码水平一般,难免难看,请见谅*/
public class AdminUserFactory implements IPersonFactory{@Overridepublic User createUser(String name, int useId) {return new AdminUser(name,useId);}
}
package User.Factory;
import User.NormalUser;
import User.User;/*** @author pluchon* @create 2025-10-26-11:54* 作者代码水平一般,难免难看,请见谅*/
public class NormalUserFactory implements IPersonFactory{@Overridepublic User createUser(String name, int useId) {return new NormalUser(name,useId);}
}
5. Util模块
package Util;import Book.Book;
import Constant.Constant;
import com.bit.utils.FileUtils;import java.io.File;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;/*** @author pluchon* @create 2025-10-26-12:26* 作者代码水平一般,难免难看,请见谅*/
public class AnalyzingBook {//存书public void storeObject(Book[] books, String filename) {//为什么我们不可以直接books.length统计书籍数量呢//这就相当于书架,可能有的地方有空缺并没有放置书//因此我们先要统计有效书籍的数目int bookUseLength = 0;for (int i = 0; i < books.length; i++) {if(books[i] != null){bookUseLength++;}}//现在我们需要吧书进行字符串化StringBuilder jsonArray = new StringBuilder();//我们根据刚刚统计到的有效书籍个数进行字符串化//我们现在针对的是每一本书for (int i = 0; i < bookUseLength; i++) {if(books[i] != null){//这里的toJSON是我们要实现的,即把一本书的所有信息整合成一个字符串jsonArray.append(books[i].toJson());}//因为我们每一本书在文件中是以换行符相隔的,因此最后一本书无需换行符if(i < bookUseLength-1){jsonArray.append("\n");}}//此时我们的几本书合在一起就是一个这样的字符串//1,西游记,......\n2,水浒传,....... .... 5,红楼梦//写回文件FileUtils.writeFile(jsonArray.toString(),Constant.ALL_BOOK_FILE_NAME);}//从文件读取数据public Book[] loadObject(String filename) {//读取所有内容,再根据\n分隔封装成为一个个书籍对象String content = FileUtils.readFile(Constant.ALL_BOOK_FILE_NAME);if(content == null || content.isEmpty()){System.out.println("文件不存在或文件为空:"+Constant.ALL_BOOK_FILE_NAME);return null;}//根据\n分隔字符串String[] bookJsonStrings = content.split("\n");//组成成对象Book[] booksList = new Book[bookJsonStrings.length];for(int i = 0;i < bookJsonStrings.length;i++){//这里涉及到把字符串变成一个书籍的对象,我们具体来实现Book book = parseBookJson(bookJsonStrings[i]);booksList[i] = book;}return booksList;}//书籍字符串解析private Book parseBookJson(String json) {//判空if(json.isEmpty()) {return null;}//使用逗号分割字符串String [] pairs = json.split(",");//针对每一个数学,赋予对象的值int bookId = Integer.parseInt(pairs[0]);String title = pairs[1];String author = pairs[2];String category = pairs[3];int publishYear = Integer.parseInt(pairs[4]);boolean isBorrowed = Boolean.parseBoolean(pairs[5]);int borrowCount = Integer.parseInt(pairs[6]);LocalDate shelfDate = LocalDate.parse(pairs[7]);//根据内容去构造对象,要判断内容都要存在,其实可以不用判断//我们之前已经筛选过了,但是为了保险起见,我们还是要进行筛选if (title != null && author != null && category != null && shelfDate != null) {Book book = new Book(title, author, category, publishYear, shelfDate);book.setBorrowed(isBorrowed);book.setBorrowCount(borrowCount);book.setBookId(bookId);return book;}return null;}
}
package Util;import Book.PairOfUidAndBookId;
import Constant.Constant;
import com.bit.utils.FileUtils;/*** @author pluchon* @create 2025-10-26-13:50* 作者代码水平一般,难免难看,请见谅*/
public class AnalyzingBorrowedBook {//这个就是分析借阅信息,跟分析书籍信息类似的逻辑//存储借阅信息public void storeObject(PairOfUidAndBookId[] pairOfUidAndBookIds, String filename){//先遍历pairOfUidAndBookIds数组当中不为空的数据多少个?int booksUseLen = 0;for (int i = 0; i < pairOfUidAndBookIds.length; i++) {if(pairOfUidAndBookIds[i] != null) {booksUseLen++;}}StringBuilder jsonArray = new StringBuilder();for (int i = 0; i < booksUseLen; i++) {if(pairOfUidAndBookIds[i] != null) {jsonArray.append(pairOfUidAndBookIds[i].toJson());if (i != booksUseLen-1) {jsonArray.append("\n");}}}FileUtils.writeFile(jsonArray.toString(),filename);/* */}//读取借阅信息public PairOfUidAndBookId[] loadObject(String filename){//从⽂件读取数据String content = FileUtils.readFile(filename);if (content == null || content.isEmpty()) {System.out.println("已借阅书籍列表⽆数据,表⽰没有⽤⼾借阅过书籍");return null;}String[] JsonStrings = content.split("\n");PairOfUidAndBookId[] pairOfUidAndBookIds = new PairOfUidAndBookId[JsonStrings.length];for (int i = 0; i < JsonStrings.length; i++) {PairOfUidAndBookId pairOfUidAndBookId = new PairOfUidAndBookId();String[] uidAndBookIds = JsonStrings[i].split(",");pairOfUidAndBookId.setUserId(Integer.parseInt(uidAndBookIds[0]));pairOfUidAndBookId.setBookId(Integer.parseInt(uidAndBookIds[1]));pairOfUidAndBookIds[i] = pairOfUidAndBookId;}return pairOfUidAndBookIds;}
}
6. LibrarySystem模块(项目启动模块)
import Book.Library;
import User.Factory.AdminUserFactory;
import User.Factory.IPersonFactory;
import User.Factory.NormalUserFactory;
import User.User;
import User.ProxyUser;
import User.AdminUser;
import User.NormalUser;import java.util.Scanner;/*** @author pluchon* @create 2025-10-26-11:52* 作者代码水平一般,难免难看,请见谅*/
public class LibrarySystem {//选择对应的代理类角色进行登录操作public static ProxyUser selectProxyRole(ProxyUser proxyUserNormalUser1, ProxyUser proxyUserNormalUser2,ProxyUser proxyUserAdminUser){Scanner scanner = new Scanner(System.in);System.out.println("请选择对应的用户类型进行登录");System.out.println("1.管理员["+proxyUserAdminUser.getRealUserName()+"]\n" +"2.普通用户["+proxyUserNormalUser1.getRealUserName()+"]\n3.普通用户["+proxyUserNormalUser2.getRealUserName()+"]\n4.退出系统");ProxyUser currentUser = null;int choice = scanner.nextInt();switch (choice) {case 1:currentUser = proxyUserAdminUser;break;case 2:currentUser = proxyUserNormalUser1;break;case 3:currentUser = proxyUserNormalUser2;break;case 4:System.exit(0);System.out.println("系统已退出..");break;default:break;}return currentUser;}public static void main(String[] args) {IPersonFactory normalUser = new NormalUserFactory();User normalUser1 = normalUser.createUser("王晓明",1);User normalUser2 = normalUser.createUser("李东来",2);IPersonFactory adminUser = new AdminUserFactory();User adminUser1 = adminUser.createUser("胡志云",3);//使用代理类ProxyUser proxyUserNormalUser1 = new ProxyUser(normalUser1);ProxyUser proxyUserNormalUser2 = new ProxyUser(normalUser2);ProxyUser proxyUserAdminUser = new ProxyUser(adminUser1);//操作界面测试LibrarySystem librarySystem = new LibrarySystem();ProxyUser currentUser = selectProxyRole(proxyUserNormalUser1,proxyUserNormalUser2,proxyUserAdminUser);while(true){int choice = currentUser.getRealUser().display();//此时⽆需关系是 管理员还是普通⽤⼾,代理类会做权限判断currentUser.handleOperation(choice);}}
}
7. 文件模块
allBooks.txt模块测试数据
1,C,侠名,编程,1985,true,0,2025-10-26
2,Java,zlh,编程,2000,true,10,2024-11-10
3,Python,Guido,编程,1991,true,25,2025-09-15
4,JavaScript,Brendan,编程,1995,false,8,2025-08-20
5,数据结构,严蔚敏,计算机,1997,true,15,2025-07-10
6,算法导论,Thomas,计算机,2001,true,12,2025-06-05
7,数据库原理,王珊,计算机,2006,true,7,2025-05-18
8,计算机网络,谢希仁,网络,2010,false,9,2025-04-22
borrow.txt模块测试数据
1,1
1,2
2,3
2,5
3,1
3,4
3,6
4,2
4,7
5,3
5,8
6,1
6,4
7,5
7,6
8,7
8,8
七、想说的话
这个项目目前还不是最好的,我理想是以后使用Spring写出网站效果,以及连接Mysql数据库
其实这个项目我一直都想要复盘了,但是苦于困难一直没有行动,用了一个周末的时候,好好的复盘了项目,我也不敢肯定的说我全都会,但是大致逻辑我还是懂的
如果你也有好的项目,欢迎和我交流,感谢大家一直长期以来的支持
