这个系统将使用 控制台 作为交互界面,采用 MVC(Model-View-Controller)设计模式,并使用 文件存储 作为数据库,非常适合学习和练手。
系统概述
功能模块
- 图书管理:
- 添加新书
- 查看所有图书
- 根据书名或ID搜索图书
- 修改图书信息
- 删除图书
- 用户管理:
- 添加新用户(读者)
- 查看所有用户
- 根据用户名或ID搜索用户
- 修改用户信息
- 删除用户
- 借阅管理:
- 用户借书
- 用户还书
- 查看指定用户的借阅记录
- 数据持久化:
- 系统启动时自动从文件加载数据。
- 系统退出时自动保存数据到文件。
技术栈
- 语言: Java
- 设计模式: MVC (Model-View-Controller)
- 数据存储: 文本文件 (
.txt) - 核心库:
java.io,java.util,java.time(用于日期处理)
项目结构
为了遵循MVC模式,我们将代码组织成以下几个包:
BookManagementSystem/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ ├── model/
│ │ │ │ ├── Book.java // 图书实体类 (Model)
│ │ │ │ ├── User.java // 用户实体类 (Model)
│ │ │ │ └── BorrowRecord.java // 借阅记录类 (Model)
│ │ │ ├── controller/
│ │ │ │ ├── BookController.java // 图书业务逻辑控制器
│ │ │ │ ├── UserController.java // 用户业务逻辑控制器
│ │ │ │ └── BorrowController.java // 借阅业务逻辑控制器
│ │ │ ├── view/
│ │ │ │ ├── AdminView.java // 管理员主界面 (View)
│ │ │ │ └── MainView.java // 系统主界面 (View)
│ │ │ ├── utils/
│ │ │ │ ├── FileUtils.java // 文件读写工具类
│ │ │ │ └── DateUtils.java // 日期格式化工具类
│ │ │ └── Main.java // 程序入口
│ │ └── resources/
│ │ ├── books.txt // 图书数据文件
│ │ ├── users.txt // 用户数据文件
│ │ └── borrow_records.txt // 借阅记录数据文件
│ └── test/
│ └── ... (可选)
└── README.md
源代码实现
src/main/java/model/Book.java
package model;
import java.io.Serializable;
// 图书实体类 (Model)
public class Book implements Serializable {
private static final long serialVersionUID = 1L;
private String id; // 图书ID
private String title; // 书名
private String author; // 作者
private boolean isAvailable; // 是否可借
public Book() {}
public Book(String id, String title, String author) {
this.id = id;
this.title = title;
this.author = author;
this.isAvailable = true; // 新书默认可借
}
// Getters and Setters
public String getId() { return id; }
public void setId(String id) { this.id = id; }
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 boolean isAvailable() { return isAvailable; }
public void setAvailable(boolean available) { isAvailable = available; }
@Override
public String toString() {
return "Book{" +
"ID='" + id + '\'' +
", Title='" + title + '\'' +
", Author='" + author + '\'' +
", Available=" + (isAvailable ? "Yes" : "No") +
'}';
}
}
src/main/java/model/User.java
package model;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
// 用户实体类 (Model)
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private String id; // 用户ID
private String name; // 用户名
private List<String> borrowedBookIds; // 已借阅的图书ID列表
public User() {
this.borrowedBookIds = new ArrayList<>();
}
public User(String id, String name) {
this.id = id;
this.name = name;
this.borrowedBookIds = new ArrayList<>();
}
// Getters and Setters
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public List<String> getBorrowedBookIds() { return borrowedBookIds; }
public void setBorrowedBookIds(List<String> borrowedBookIds) { this.borrowedBookIds = borrowedBookIds; }
public void borrowBook(String bookId) {
if (!borrowedBookIds.contains(bookId)) {
borrowedBookIds.add(bookId);
}
}
public void returnBook(String bookId) {
borrowedBookIds.remove(bookId);
}
@Override
public String toString() {
return "User{" +
"ID='" + id + '\'' +
", Name='" + name + '\'' +
", BorrowedBooks=" + borrowedBookIds.size() +
'}';
}
}
src/main/java/model/BorrowRecord.java
package model;
import java.io.Serializable;
import java.time.LocalDate;
// 借阅记录类 (Model)
public class BorrowRecord implements Serializable {
private static final long serialVersionUID = 1L;
private String bookId;
private String userId;
private LocalDate borrowDate;
private LocalDate dueDate; // 应还日期
private LocalDate returnDate; // 实际归还日期
public BorrowRecord(String bookId, String userId) {
this.bookId = bookId;
this.userId = userId;
this.borrowDate = LocalDate.now();
this.dueDate = borrowDate.plusWeeks(2); // 借期两周
this.returnDate = null; // 初始未归还
}
// Getters and Setters
public String getBookId() { return bookId; }
public void setBookId(String bookId) { this.bookId = bookId; }
public String getUserId() { return userId; }
public void setUserId(String userId) { this.userId = userId; }
public LocalDate getBorrowDate() { return borrowDate; }
public void setBorrowDate(LocalDate borrowDate) { this.borrowDate = borrowDate; }
public LocalDate getDueDate() { return dueDate; }
public void setDueDate(LocalDate dueDate) { this.dueDate = dueDate; }
public LocalDate getReturnDate() { return returnDate; }
public void setReturnDate(LocalDate returnDate) { this.returnDate = returnDate; }
public boolean isReturned() {
return returnDate != null;
}
@Override
public String toString() {
return "BorrowRecord{" +
"BookID='" + bookId + '\'' +
", UserID='" + userId + '\'' +
", BorrowDate=" + borrowDate +
", DueDate=" + dueDate +
", ReturnDate=" + (returnDate != null ? returnDate : "Not Returned") +
'}';
}
}
src/main/java/utils/FileUtils.java
package utils;
import model.Book;
import model.BorrowRecord;
import model.User;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
public class FileUtils {
// 通用保存方法
public static <T> void saveToFile(List<T> list, String filename) {
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filename))) {
oos.writeObject(list);
} catch (IOException e) {
System.err.println("Error saving to file: " + filename);
e.printStackTrace();
}
}
// 通用读取方法
@SuppressWarnings("unchecked")
public static <T> List<T> loadFromFile(String filename, Class<T> type) {
File file = new File(filename);
if (!file.exists()) {
return new ArrayList<>();
}
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file))) {
return (List<T>) ois.readObject();
} catch (IOException | ClassNotFoundException e) {
System.err.println("Error loading from file: " + filename);
e.printStackTrace();
return new ArrayList<>();
}
}
}
src/main/java/utils/DateUtils.java
package utils;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
public class DateUtils {
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
public static String format(LocalDate date) {
return date.format(FORMATTER);
}
public static LocalDate parse(String dateStr) {
return LocalDate.parse(dateStr, FORMATTER);
}
}
src/main/java/controller/BookController.java
package controller;
import model.Book;
import utils.FileUtils;
import java.util.List;
import java.util.stream.Collectors;
public class BookController {
private List<Book> books;
public BookController() {
this.books = FileUtils.loadFromFile("resources/books.txt", Book.class);
}
public void addBook(Book book) {
books.add(book);
save();
}
public void deleteBook(String bookId) {
books.removeIf(book -> book.getId().equals(bookId));
save();
}
public void updateBook(Book book) {
for (int i = 0; i < books.size(); i++) {
if (books.get(i).getId().equals(book.getId())) {
books.set(i, book);
break;
}
}
save();
}
public Book findBookById(String bookId) {
return books.stream()
.filter(book -> book.getId().equals(bookId))
.findFirst()
.orElse(null);
}
public List<Book> findBooksByTitle(String title) {
return books.stream()
.filter(book -> book.getTitle().toLowerCase().contains(title.toLowerCase()))
.collect(Collectors.toList());
}
public List<Book> getAllBooks() {
return books;
}
public void borrowBook(String bookId) {
Book book = findBookById(bookId);
if (book != null && book.isAvailable()) {
book.setAvailable(false);
save();
}
}
public void returnBook(String bookId) {
Book book = findBookById(bookId);
if (book != null && !book.isAvailable()) {
book.setAvailable(true);
save();
}
}
private void save() {
FileUtils.saveToFile(books, "resources/books.txt");
}
}
src/main/java/controller/UserController.java
package controller;
import model.User;
import utils.FileUtils;
import java.util.List;
import java.util.stream.Collectors;
public class UserController {
private List<User> users;
public UserController() {
this.users = FileUtils.loadFromFile("resources/users.txt", User.class);
}
public void addUser(User user) {
users.add(user);
save();
}
public void deleteUser(String userId) {
users.removeIf(user -> user.getId().equals(userId));
save();
}
public void updateUser(User user) {
for (int i = 0; i < users.size(); i++) {
if (users.get(i).getId().equals(user.getId())) {
users.set(i, user);
break;
}
}
save();
}
public User findUserById(String userId) {
return users.stream()
.filter(user -> user.getId().equals(userId))
.findFirst()
.orElse(null);
}
public User findUserByName(String name) {
return users.stream()
.filter(user -> user.getName().toLowerCase().contains(name.toLowerCase()))
.findFirst()
.orElse(null);
}
public List<User> getAllUsers() {
return users;
}
private void save() {
FileUtils.saveToFile(users, "resources/users.txt");
}
}
src/main/java/controller/BorrowController.java
package controller;
import model.BorrowRecord;
import model.User;
import utils.DateUtils;
import utils.FileUtils;
import java.time.LocalDate;
import java.util.List;
import java.util.stream.Collectors;
public class BorrowController {
private List<BorrowRecord> borrowRecords;
public BorrowController() {
this.borrowRecords = FileUtils.loadFromFile("resources/borrow_records.txt", BorrowRecord.class);
}
public void borrowBook(String bookId, String userId) {
// 检查用户是否已借阅此书
boolean alreadyBorrowed = borrowRecords.stream()
.anyMatch(r -> r.getBookId().equals(bookId) && r.getUserId().equals(userId) && !r.isReturned());
if (!alreadyBorrowed) {
BorrowRecord record = new BorrowRecord(bookId, userId);
borrowRecords.add(record);
save();
}
}
public void returnBook(String bookId, String userId) {
for (BorrowRecord record : borrowRecords) {
if (record.getBookId().equals(bookId) && record.getUserId().equals(userId) && !record.isReturned()) {
record.setReturnDate(LocalDate.now());
save();
break;
}
}
}
public List<BorrowRecord> getBorrowRecordsByUserId(String userId) {
return borrowRecords.stream()
.filter(record -> record.getUserId().equals(userId))
.collect(Collectors.toList());
}
private void save() {
FileUtils.saveToFile(borrowRecords, "resources/borrow_records.txt");
}
}
src/main/java/view/MainView.java
package view;
import controller.BookController;
import controller.UserController;
import controller.BorrowController;
import java.util.Scanner;
public class MainView {
private Scanner scanner;
private BookController bookController;
private UserController userController;
private BorrowController borrowController;
public MainView() {
this.scanner = new Scanner(System.in);
this.bookController = new BookController();
this.userController = new UserController();
this.borrowController = new BorrowController();
}
public void show() {
while (true) {
System.out.println("\n===== 图书管理系统 =====");
System.out.println("1. 管理员入口");
System.out.println("2. 读者入口 (模拟)");
System.out.println("3. 退出系统");
System.out.print("请选择: ");
int choice;
try {
choice = Integer.parseInt(scanner.nextLine());
} catch (NumberFormatException e) {
System.out.println("无效输入,请输入数字。");
continue;
}
switch (choice) {
case 1:
new AdminView(bookController, userController, borrowController, scanner).show();
break;
case 2:
// 简单的读者模拟
simulateUser();
break;
case 3:
System.out.println("感谢使用,再见!");
return;
default:
System.out.println("无效选择,请重新输入。");
}
}
}
private void simulateUser() {
System.out.println("\n===== 读者模拟界面 =====");
System.out.print("请输入您的用户ID: ");
String userId = scanner.nextLine();
var user = userController.findUserById(userId);
if (user == null) {
System.out.println("用户不存在!");
return;
}
System.out.println("欢迎, " + user.getName() + "!");
// 查看该用户的借阅记录
System.out.println("\n您的借阅记录:");
var records = borrowController.getBorrowRecordsByUserId(userId);
if (records.isEmpty()) {
System.out.println("您当前没有借阅任何图书。");
} else {
records.forEach(System.out::println);
}
}
public static void main(String[] args) {
new MainView().show();
}
}
src/main/java/view/AdminView.java
package view;
import controller.BookController;
import controller.UserController;
import controller.BorrowController;
import model.Book;
import model.User;
import java.util.List;
import java.util.Scanner;
public class AdminView {
private Scanner scanner;
private BookController bookController;
private UserController userController;
private BorrowController borrowController;
public AdminView(BookController bookController, UserController userController, BorrowController borrowController, Scanner scanner) {
this.bookController = bookController;
this.userController = userController;
this.borrowController = borrowController;
this.scanner = scanner;
}
public void show() {
while (true) {
System.out.println("\n===== 管理员菜单 =====");
System.out.println("1. 图书管理");
System.out.println("2. 用户管理");
System.out.println("3. 借阅管理");
System.out.println("4. 返回主菜单");
System.out.print("请选择: ");
int choice;
try {
choice = Integer.parseInt(scanner.nextLine());
} catch (NumberFormatException e) {
System.out.println("无效输入,请输入数字。");
continue;
}
switch (choice) {
case 1:
manageBooks();
break;
case 2:
manageUsers();
break;
case 3:
manageBorrows();
break;
case 4:
return;
default:
System.out.println("无效选择,请重新输入。");
}
}
}
private void manageBooks() {
while (true) {
System.out.println("\n--- 图书管理 ---");
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.print("请选择: ");
int choice;
try {
choice = Integer.parseInt(scanner.nextLine());
} catch (NumberFormatException e) {
System.out.println("无效输入。");
continue;
}
switch (choice) {
case 1:
listAllBooks();
break;
case 2:
addNewBook();
break;
case 3:
searchBook();
break;
case 4:
updateBook();
break;
case 5:
deleteBook();
break;
case 6:
return;
default:
System.out.println("无效选择。");
}
}
}
private void manageUsers() {
while (true) {
System.out.println("\n--- 用户管理 ---");
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.print("请选择: ");
// ... (与manageBooks类似的结构)
// 为简洁起见,此处省略具体实现,逻辑与图书管理类似
System.out.println("用户管理功能待实现。");
return; // 暂时返回
}
}
private void manageBorrows() {
while (true) {
System.out.println("\n--- 借阅管理 ---");
System.out.println("1. 用户借书");
System.out.println("2. 用户还书");
System.out.println("3. 查看用户借阅记录");
System.out.println("4. 返回上级");
System.out.print("请选择: ");
int choice;
try {
choice = Integer.parseInt(scanner.nextLine());
} catch (NumberFormatException e) {
System.out.println("无效输入。");
continue;
}
switch (choice) {
case 1:
processBorrow();
break;
case 2:
processReturn();
break;
case 3:
viewBorrowRecords();
break;
case 4:
return;
default:
System.out.println("无效选择。");
}
}
}
// --- 图书管理辅助方法 ---
private void listAllBooks() {
List<Book> books = bookController.getAllBooks();
if (books.isEmpty()) {
System.out.println("图书馆暂无图书。");
} else {
System.out.println("\n所有图书列表:");
books.forEach(System.out::println);
}
}
private void addNewBook() {
System.out.print("输入图书ID: ");
String id = scanner.nextLine();
if (bookController.findBookById(id) != null) {
System.out.println("该ID已存在!");
return;
}
System.out.print("输入书名: ");
String title = scanner.nextLine();
System.out.print("输入作者: ");
String author = scanner.nextLine();
Book book = new Book(id, title, author);
bookController.addBook(book);
System.out.println("图书添加成功!");
}
private void searchBook() {
System.out.print("输入要搜索的书名关键词: ");
String keyword = scanner.nextLine();
List<Book> books = bookController.findBooksByTitle(keyword);
if (books.isEmpty()) {
System.out.println("未找到匹配的图书。");
} else {
System.out.println("\n搜索结果:");
books.forEach(System.out::println);
}
}
private void updateBook() {
System.out.print("输入要修改的图书ID: ");
String id = scanner.nextLine();
Book book = bookController.findBookById(id);
if (book == null) {
System.out.println("未找到该ID的图书。");
return;
}
System.out.println("找到图书: " + book);
System.out.print("输入新的书名 (留空则不修改): ");
String newTitle = scanner.nextLine();
System.out.print("输入新的作者 (留空则不修改): ");
String newAuthor = scanner.nextLine();
if (!newTitle.isEmpty()) book.setTitle(newTitle);
if (!newAuthor.isEmpty()) book.setAuthor(newAuthor);
bookController.updateBook(book);
System.out.println("图书信息更新成功!");
}
private void deleteBook() {
System.out.print("输入要删除的图书ID: ");
String id = scanner.nextLine();
if (bookController.findBookById(id) == null) {
System.out.println("未找到该ID的图书。");
return;
}
bookController.deleteBook(id);
System.out.println("图书删除成功!");
}
// --- 借阅管理辅助方法 ---
private void processBorrow() {
System.out.print("输入用户ID: ");
String userId = scanner.nextLine();
System.out.print("输入要借的图书ID: ");
String bookId = scanner.nextLine();
User user = userController.findUserById(userId);
Book book = bookController.findBookById(bookId);
if (user == null) {
System.out.println("用户不存在!");
return;
}
if (book == null) {
System.out.println("图书不存在!");
return;
}
if (!book.isAvailable()) {
System.out.println("该图书已被借出!");
return;
}
borrowController.borrowBook(bookId, userId);
bookController.borrowBook(bookId); // 更新图书状态
System.out.println("借书成功!");
}
private void processReturn() {
System.out.print("输入用户ID: ");
String userId = scanner.nextLine();
System.out.print("输入要还的图书ID: ");
String bookId = scanner.nextLine();
User user = userController.findUserById(userId);
Book book = bookController.findBookById(bookId);
if (user == null || book == null) {
System.out.println("用户或图书信息错误!");
return;
}
// 检查是否真的借了这本书
boolean hasBorrowed = borrowController.getBorrowRecordsByUserId(userId).stream()
.anyMatch(r -> r.getBookId().equals(bookId) && !r.isReturned());
if (!hasBorrowed) {
System.out.println("该用户未借阅此书!");
return;
}
borrowController.returnBook(bookId, userId);
bookController.returnBook(bookId); // 更新图书状态
System.out.println("还书成功!");
}
private void viewBorrowRecords() {
System.out.print("输入要查询的用户ID: ");
String userId = scanner.nextLine();
List<BorrowRecord> records = borrowController.getBorrowRecordsByUserId(userId);
if (records.isEmpty()) {
System.out.println("该用户没有借阅记录。");
} else {
System.out.println("\n该用户的借阅记录:");
records.forEach(System.out::println);
}
}
}
如何运行
-
环境准备:
- 确保你已安装 JDK (Java Development Kit) 8 或更高版本。
- 确保你已安装并配置好
javac和java命令。
-
项目设置:
- 创建上述目录结构。
- 将所有
.java文件放入对应的src/main/java子目录中。 - 在
src/main/resources目录下创建books.txt,users.txt, 和borrow_records.txt这三个空文件,程序首次运行时会自动创建它们。
-
编译:
- 打开终端或命令提示符。
- 进入到
BookManagementSystem根目录。 - 执行编译命令,编译所有Java文件:
javac -d bin src/main/java/**/*.java
-d bin: 指定编译后的.class文件输出到bin目录,如果该目录不存在,会自动创建。
-
运行:
- 编译成功后,运行主类
Main:java -cp bin Main
-cp bin: 指定类路径为bin目录。
- 编译成功后,运行主类
系统演示流程
- 启动程序,选择
管理员入口。 - 进入
图书管理->添加图书,添加几本书,- ID:
001, Title:Java编程思想, Author:Bruce Eckel - ID:
002, Title:Effective Java, Author:Joshua Bloch
- ID:
- 进入
图书管理->查看所有图书,确认图书已添加。 - 进入
用户管理->添加用户,添加几个用户,- ID:
user01, Name:张三 - ID:
user02, Name:李四
- ID:
- 进入
借阅管理->用户借书:- 输入用户ID
user01,图书ID001,借书成功。
- 输入用户ID
- 再次查看所有图书,会发现
Java编程思想的状态变为No。 - 进入
借阅管理->查看用户借阅记录,输入user01,可以看到张三的借阅记录。 - 进入
借阅管理->用户还书:- 输入用户ID
user01,图书ID001,还书成功。
- 输入用户ID
- 再次查看所有图书,
Java编程思想的状态会变回Yes。 - 返回主菜单,选择
读者入口,输入一个用户ID(如user01),可以模拟读者查看自己的借阅记录。 - 退出系统,再次启动,你会发现所有数据都还在,因为它们已经被保存在文件中了。
这个项目涵盖了Java基础、面向对象编程、集合、I/O流、简单的MVC模式等多个核心知识点,是一个非常好的学习和实践项目,你可以基于此代码进行扩展,例如增加GUI界面、连接真实数据库、增加更多功能(如图书分类、逾期罚款等)。
