ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Java图书管理系统实战:从零搭建控制台应用,掌握面向对象与数据持久化

Java图书管理系统实战:从零搭建控制台应用,掌握面向对象与数据持久化 很多Java初学者在学完基础语法后常常会陷入“不知道能做什么项目”的迷茫。一个结构清晰、功能完整的图书管理系统正是连接Java理论与实战开发的绝佳桥梁。它不仅涵盖了面向对象、集合、数据库操作等核心知识还能让你体验从需求分析到功能实现的完整开发流程。本文将手把手带你从零搭建一个控制台版本的图书管理系统深入剖析每一行代码的设计思路并提供完整的项目源码。无论你是刚入门的小白还是想巩固基础知识的开发者都能通过这个项目获得扎实的成长。1. 项目背景与核心价值1.1 为什么选择图书管理系统作为练手项目图书管理系统是一个经典的业务场景它麻雀虽小五脏俱全。对于初学者而言它具有以下几个不可替代的优势业务逻辑直观每个人都对“借书”、“还书”、“查询图书”等操作有基本认知无需花费大量时间理解复杂的业务规则可以将精力集中于技术实现本身。技术栈覆盖全面一个完整的项目会涉及Java基础语法类、对象、继承、多态、集合框架List、Map存储数据、JDBC数据库操作、控制台交互Scanner、异常处理、文件IO如果使用文件存储等核心知识点。分层架构启蒙即使是控制台程序我们也可以有意识地按照MVCModel-View-Controller或三层架构表现层、业务逻辑层、数据访问层的思想来组织代码为后续学习Spring Boot等框架打下坚实基础。可扩展性强基础版本完成后你可以轻松地为它添加新功能如用户登录、图书分类、借阅逾期计算、数据统计报表等让项目不断“生长”持续练习。1.2 我们将实现什么本文将实现一个基于控制台和集合暂不使用数据库的简易图书管理系统。主要功能包括管理员功能图书的增、删、改、查。用户功能查询图书、借阅图书、归还图书。数据持久化使用文件来模拟数据库实现程序关闭后数据不丢失。通过这个项目你将清晰地掌握如何将现实世界的“图书”和“用户”抽象为Java中的类和对象并管理它们之间的交互关系。2. 开发环境准备在开始编码之前请确保你的开发环境已经就绪。本教程以最通用的环境为例重点在于思路的传递你可以根据实际情况调整。操作系统Windows 10/11, macOS, Linux 均可。Java开发工具包 (JDK)版本 8 或以上推荐 JDK 11 或 17。确保已正确安装并配置环境变量。验证方法打开命令行CMD或Terminal输入java -version和javac -version能看到版本信息即表示成功。集成开发环境 (IDE)强烈推荐使用IntelliJ IDEA Community Edition免费或 Eclipse。IDEA在代码提示、项目管理方面对新手更友好。项目构建本项目为纯Java SE项目无需Maven或Gradle直接使用IDE创建普通Java项目即可。版本说明本文代码基于Java 8语法编写兼容更高版本。所有核心逻辑均使用标准Java SE API不依赖任何第三方库确保最大程度的可复现性。3. 核心概念与系统设计在动手写代码前先进行设计。好的设计是成功的一半。3.1 实体类设计 (Model层)我们需要用Java类来模拟系统中的实体。这里主要设计两个核心类Book图书和User用户。Book类代表一本图书应包含图书的基本属性。// 文件src/main/java/com/library/model/Book.java package com.library.model; /** * 图书实体类 */ public class Book { // 图书ID唯一标识 private String id; // 图书名称 private String name; // 图书作者 private String author; // 图书价格 private double price; // 图书库存数量 private int stock; // 图书是否被借出 (另一种设计用stock0判断) private boolean isBorrowed; // 全参构造方法 public Book(String id, String name, String author, double price, int stock) { this.id id; this.name name; this.author author; this.price price; this.stock stock; this.isBorrowed (stock 0); // 根据库存初始化状态 } // Getter和Setter方法 (IDE可自动生成) 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 String getAuthor() { return author; } public void setAuthor(String author) { this.author author; } public double getPrice() { return price; } public void setPrice(double price) { this.price price; } public int getStock() { return stock; } public void setStock(int stock) { this.stock stock; this.isBorrowed (stock 0); // 更新库存时同步状态 } public boolean isBorrowed() { return isBorrowed; } public void setBorrowed(boolean borrowed) { isBorrowed borrowed; } // 重写toString方法方便打印图书信息 Override public String toString() { return String.format(图书ID: %s | 书名: 《%s》 | 作者: %s | 价格: %.2f | 库存: %d | 状态: %s, id, name, author, price, stock, (stock 0 ? 可借 : 已借完)); } }User类代表系统用户读者。// 文件src/main/java/com/library/model/User.java package com.library.model; import java.util.ArrayList; import java.util.List; /** * 用户实体类 */ public class User { // 用户ID private String userId; // 用户名 private String username; // 密码 (简易版实际应用需加密) private String password; // 用户当前借阅的图书列表 (存储图书ID) private ListString borrowedBookIds; public User(String userId, String username, String password) { this.userId userId; this.username username; this.password password; this.borrowedBookIds new ArrayList(); } // Getter和Setter public String getUserId() { return userId; } public void setUserId(String userId) { this.userId userId; } public String getUsername() { return username; } public void setUsername(String username) { this.username username; } public String getPassword() { return password; } public void setPassword(String password) { this.password password; } public ListString getBorrowedBookIds() { return borrowedBookIds; } // 借书方法 public boolean borrowBook(String bookId) { if (!borrowedBookIds.contains(bookId)) { borrowedBookIds.add(bookId); return true; } return false; // 已经借过此书 } // 还书方法 public boolean returnBook(String bookId) { return borrowedBookIds.remove(bookId); } Override public String toString() { return String.format(用户ID: %s | 用户名: %s | 借阅数量: %d, userId, username, borrowedBookIds.size()); } }3.2 数据存储设计 (模拟DAO层)我们不直接使用数据库而是用内存中的集合(HashMap,ArrayList)来存储数据并用文件进行持久化。这里设计一个DataRepository类来统一管理所有数据。// 文件src/main/java/com/library/repository/DataRepository.java package com.library.repository; import com.library.model.Book; import com.library.model.User; import java.io.*; import java.util.*; /** * 数据仓库负责所有数据的存储、检索和持久化。 * 使用单例模式确保全局唯一的数据访问点。 */ public class DataRepository { private static DataRepository instance; // 使用Map存储图书key为图书IDvalue为Book对象 private MapString, Book bookMap; // 使用Map存储用户key为用户IDvalue为User对象 private MapString, User userMap; // 当前登录的用户 private User currentUser; // 数据文件路径 private static final String BOOKS_FILE books.dat; private static final String USERS_FILE users.dat; private DataRepository() { bookMap new HashMap(); userMap new HashMap(); currentUser null; loadDataFromFile(); // 启动时加载数据 initDefaultData(); // 如果文件为空初始化一些默认数据 } // 获取单例实例 public static synchronized DataRepository getInstance() { if (instance null) { instance new DataRepository(); } return instance; } // 初始化一些默认数据仅当数据文件为空时 private void initDefaultData() { if (bookMap.isEmpty()) { addBook(new Book(B001, Java核心技术 卷I, Cay S. Horstmann, 119.00, 5)); addBook(new Book(B002, Effective Java, Joshua Bloch, 89.00, 3)); addBook(new Book(B003, 深入理解计算机系统, Randal E. Bryant, 139.00, 2)); } if (userMap.isEmpty()) { // 添加一个默认管理员用户 addUser(new User(U001, admin, admin123)); // 添加一个普通用户 addUser(new User(U002, zhangsan, 123456)); } } // ---------- 图书相关操作 ---------- public void addBook(Book book) { bookMap.put(book.getId(), book); saveBooksToFile(); } public Book getBookById(String id) { return bookMap.get(id); } public ListBook getAllBooks() { return new ArrayList(bookMap.values()); } public boolean updateBook(Book book) { if (bookMap.containsKey(book.getId())) { bookMap.put(book.getId(), book); saveBooksToFile(); return true; } return false; } public boolean deleteBookById(String id) { if (bookMap.containsKey(id)) { bookMap.remove(id); saveBooksToFile(); return true; } return false; } // ---------- 用户相关操作 ---------- public void addUser(User user) { userMap.put(user.getUserId(), user); saveUsersToFile(); } public User getUserById(String id) { return userMap.get(id); } public User login(String username, String password) { for (User user : userMap.values()) { if (user.getUsername().equals(username) user.getPassword().equals(password)) { currentUser user; return user; } } return null; // 登录失败 } public void logout() { currentUser null; } public User getCurrentUser() { return currentUser; } // ---------- 文件持久化 ---------- // 保存图书数据到文件使用Java序列化简易版 SuppressWarnings(unchecked) private void loadDataFromFile() { try (ObjectInputStream ois new ObjectInputStream(new FileInputStream(BOOKS_FILE))) { bookMap (MapString, Book) ois.readObject(); } catch (FileNotFoundException e) { System.out.println(未找到图书数据文件将创建新文件。); } catch (IOException | ClassNotFoundException e) { System.out.println(加载图书数据失败将使用空数据。); } try (ObjectInputStream ois new ObjectInputStream(new FileInputStream(USERS_FILE))) { userMap (MapString, User) ois.readObject(); } catch (FileNotFoundException e) { System.out.println(未找到用户数据文件将创建新文件。); } catch (IOException | ClassNotFoundException e) { System.out.println(加载用户数据失败将使用空数据。); } } private void saveBooksToFile() { try (ObjectOutputStream oos new ObjectOutputStream(new FileOutputStream(BOOKS_FILE))) { oos.writeObject(bookMap); } catch (IOException e) { System.err.println(保存图书数据到文件失败: e.getMessage()); } } private void saveUsersToFile() { try (ObjectOutputStream oos new ObjectOutputStream(new FileOutputStream(USERS_FILE))) { oos.writeObject(userMap); } catch (IOException e) { System.err.println(保存用户数据到文件失败: e.getMessage()); } } }关键点解析单例模式DataRepository采用单例模式确保整个程序只有一个数据源避免数据不一致。序列化使用ObjectOutputStream和ObjectInputStream将对象保存到文件实现简易持久化。注意实体类Book和User需要实现Serializable接口代码中已省略实际需加上。数据隔离图书和用户数据分开存储在不同的Map中并通过ID进行关联如用户借阅列表存储的是图书ID。4. 业务逻辑与控制器实现 (Service/Controller层)有了数据和模型我们需要实现具体的业务逻辑。这里创建一个LibraryService类来处理核心业务。// 文件src/main/java/com/library/service/LibraryService.java package com.library.service; import com.library.model.Book; import com.library.model.User; import com.library.repository.DataRepository; import java.util.List; import java.util.Scanner; /** * 图书管理系统的核心业务逻辑类 */ public class LibraryService { private DataRepository repository; private Scanner scanner; public LibraryService() { this.repository DataRepository.getInstance(); this.scanner new Scanner(System.in); } // ---------- 用户身份验证 ---------- public User login() { System.out.print(请输入用户名: ); String username scanner.nextLine(); System.out.print(请输入密码: ); String password scanner.nextLine(); // 注意控制台输入密码会明文显示 User user repository.login(username, password); if (user ! null) { System.out.println(登录成功欢迎 user.getUsername() ); return user; } else { System.out.println(登录失败用户名或密码错误。); return null; } } public void logout() { repository.logout(); System.out.println(已退出登录。); } // ---------- 图书管理 (管理员功能) ---------- public void addBook() { System.out.println(\n--- 添加图书 ---); System.out.print(请输入图书ID: ); String id scanner.nextLine(); // 检查ID是否已存在 if (repository.getBookById(id) ! null) { System.out.println(错误该图书ID已存在); return; } System.out.print(请输入图书名称: ); String name scanner.nextLine(); System.out.print(请输入作者: ); String author scanner.nextLine(); System.out.print(请输入价格: ); double price 0; try { price Double.parseDouble(scanner.nextLine()); } catch (NumberFormatException e) { System.out.println(价格输入格式错误添加失败。); return; } System.out.print(请输入库存数量: ); int stock 0; try { stock Integer.parseInt(scanner.nextLine()); } catch (NumberFormatException e) { System.out.println(库存输入格式错误添加失败。); return; } Book newBook new Book(id, name, author, price, stock); repository.addBook(newBook); System.out.println(图书《 name 》添加成功); } public void showAllBooks() { ListBook books repository.getAllBooks(); if (books.isEmpty()) { System.out.println(当前书库为空。); } else { System.out.println(\n--- 所有图书列表 ---); for (Book book : books) { System.out.println(book); // 调用Book的toString方法 } System.out.println(共 books.size() 本图书。); } } public void updateBook() { System.out.print(请输入要修改的图书ID: ); String id scanner.nextLine(); Book book repository.getBookById(id); if (book null) { System.out.println(未找到ID为 \ id \ 的图书。); return; } System.out.println(当前图书信息: book); System.out.println(请输入新的图书名称 (直接回车跳过): ); String newName scanner.nextLine(); if (!newName.trim().isEmpty()) { book.setName(newName); } // 类似地可以修改作者、价格、库存... System.out.print(请输入新的库存数量 (直接回车跳过): ); String stockInput scanner.nextLine(); if (!stockInput.trim().isEmpty()) { try { int newStock Integer.parseInt(stockInput); book.setStock(newStock); } catch (NumberFormatException e) { System.out.println(库存输入无效未修改。); } } if (repository.updateBook(book)) { System.out.println(图书信息更新成功); } else { System.out.println(更新失败。); } } public void deleteBook() { System.out.print(请输入要删除的图书ID: ); String id scanner.nextLine(); System.out.print(确认删除ID为 \ id \ 的图书(y/n): ); String confirm scanner.nextLine(); if (y.equalsIgnoreCase(confirm)) { if (repository.deleteBookById(id)) { System.out.println(图书删除成功); } else { System.out.println(删除失败未找到该图书。); } } else { System.out.println(已取消删除操作。); } } // ---------- 图书借阅 (用户功能) ---------- public void borrowBook(User user) { if (user null) { System.out.println(请先登录); return; } showAllBooks(); System.out.print(请输入要借阅的图书ID: ); String bookId scanner.nextLine(); Book book repository.getBookById(bookId); if (book null) { System.out.println(未找到该图书。); return; } if (book.getStock() 0) { System.out.println(抱歉该图书库存不足无法借阅。); return; } // 检查是否已借过 if (user.getBorrowedBookIds().contains(bookId)) { System.out.println(您已经借阅了此书请勿重复借阅。); return; } // 执行借阅用户借阅列表增加图书库存减少 if (user.borrowBook(bookId)) { book.setStock(book.getStock() - 1); repository.updateBook(book); // 更新图书库存 System.out.println(借阅成功您已借阅《 book.getName() 》。); } } public void returnBook(User user) { if (user null) { System.out.println(请先登录); return; } ListString borrowedIds user.getBorrowedBookIds(); if (borrowedIds.isEmpty()) { System.out.println(您当前没有借阅任何图书。); return; } System.out.println(您当前借阅的图书ID: borrowedIds); System.out.print(请输入要归还的图书ID: ); String bookId scanner.nextLine(); Book book repository.getBookById(bookId); if (book null) { System.out.println(系统未找到该图书信息。); return; } if (user.returnBook(bookId)) { book.setStock(book.getStock() 1); repository.updateBook(book); System.out.println(归还成功); } else { System.out.println(归还失败您未借阅此图书。); } } public void showMyBooks(User user) { if (user null) { System.out.println(请先登录); return; } ListString borrowedIds user.getBorrowedBookIds(); if (borrowedIds.isEmpty()) { System.out.println(您当前没有借阅任何图书。); } else { System.out.println(\n--- 我的借阅列表 ---); for (String bookId : borrowedIds) { Book b repository.getBookById(bookId); if (b ! null) { System.out.println(b); } } } } }5. 控制台界面与主程序 (View层)最后我们需要一个控制台菜单来驱动整个程序。这是程序的入口和视图层。// 文件src/main/java/com/library/MainApp.java package com.library; import com.library.model.User; import com.library.service.LibraryService; import java.util.Scanner; /** * 图书管理系统主程序 */ public class MainApp { private static LibraryService libraryService new LibraryService(); private static Scanner scanner new Scanner(System.in); private static User currentUser null; private static boolean isAdmin false; // 简单判断是否为管理员 public static void main(String[] args) { System.out.println( 欢迎使用图书管理系统 ); showMainMenu(); } private static void showMainMenu() { while (true) { System.out.println(\n 主菜单 ); System.out.println(1. 用户登录); System.out.println(2. 注册新用户 (功能预留)); System.out.println(3. 退出系统); System.out.print(请选择操作 (1-3): ); String choice scanner.nextLine(); switch (choice) { case 1: currentUser libraryService.login(); if (currentUser ! null) { // 简单权限判断假设用户ID为U001的是管理员 isAdmin U001.equals(currentUser.getUserId()); if (isAdmin) { showAdminMenu(); } else { showUserMenu(); } } break; case 2: System.out.println(注册功能暂未开放请联系管理员。); break; case 3: System.out.println(感谢使用再见); scanner.close(); System.exit(0); break; default: System.out.println(输入无效请重新选择。); } } } private static void showAdminMenu() { while (currentUser ! null isAdmin) { System.out.println(\n********** 管理员菜单 **********); System.out.println(欢迎您 currentUser.getUsername() 管理员); 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(请选择操作 (1-6): ); String choice scanner.nextLine(); switch (choice) { case 1: libraryService.addBook(); break; case 2: libraryService.showAllBooks(); break; case 3: libraryService.updateBook(); break; case 4: libraryService.deleteBook(); break; case 5: System.out.println(用户管理功能开发中...); break; case 6: libraryService.logout(); currentUser null; isAdmin false; System.out.println(已返回主菜单。); return; // 退出管理员菜单循环 default: System.out.println(输入无效请重新选择。); } } } private static void showUserMenu() { while (currentUser ! null !isAdmin) { System.out.println(\n********** 用户菜单 **********); System.out.println(欢迎您 currentUser.getUsername() ); System.out.println(1. 查看所有图书); System.out.println(2. 借阅图书); System.out.println(3. 归还图书); System.out.println(4. 查看我的借阅); System.out.println(5. 退出登录); System.out.print(请选择操作 (1-5): ); String choice scanner.nextLine(); switch (choice) { case 1: libraryService.showAllBooks(); break; case 2: libraryService.borrowBook(currentUser); break; case 3: libraryService.returnBook(currentUser); break; case 4: libraryService.showMyBooks(currentUser); break; case 5: libraryService.logout(); currentUser null; System.out.println(已返回主菜单。); return; // 退出用户菜单循环 default: System.out.println(输入无效请重新选择。); } } } }6. 项目运行与测试6.1 如何运行项目在你的IDE如IntelliJ IDEA中创建一个新的Java项目。按照上文提供的代码创建对应的包 (com.library.model,com.library.repository,com.library.service) 和类文件。注意Book和User类需要实现Serializable接口即在类声明后加implements Serializable。将MainApp类设置为运行主类。运行MainApp的main方法。6.2 测试流程示例程序启动后控制台会显示主菜单 欢迎使用图书管理系统 主菜单 1. 用户登录 2. 注册新用户 (功能预留) 3. 退出系统 请选择操作 (1-3):测试管理员登录输入1用户名输入admin密码输入admin123。登录成功后进入管理员菜单可以尝试添加、查看、修改、删除图书。测试普通用户登录退出管理员登录后用用户名zhangsan密码123456登录。进入用户菜单可以查看图书、借阅、归还、查看自己的借阅列表。测试数据持久化添加几本图书后关闭程序重新启动。再次查看所有图书你会发现之前添加的图书依然存在因为数据已经保存到books.dat文件中了。7. 常见问题与排查思路在实现和运行本项目时你可能会遇到以下问题问题现象可能原因解决思路编译错误找不到类或包1. 包名与目录结构不匹配。2. 未正确导入项目依赖。1. 检查src/main/java下的目录是否与package语句一致。2. 在IDE中确保项目SDK已正确设置。运行时报ClassNotFoundException或NoClassDefFoundError实体类Book或User未实现Serializable接口。在这两个类的声明后添加implements java.io.Serializable。程序无法读取或保存数据文件1. 文件读写权限不足。2. 程序运行的工作目录不对。1. 检查当前用户对项目目录是否有读写权限。2. 在IDE中运行配置里设置正确的工作目录通常是项目根目录。数据文件books.dat和users.dat会生成在那里。输入数字时程序报NumberFormatException用户在应该输入数字的地方输入了非数字字符。代码中已使用try-catch进行基本处理。在实际项目中需要更健壮的输入验证例如循环提示直到输入合法。借阅图书时库存减少了但用户借阅列表没增加或反之业务逻辑不完整或存在漏洞没有同时更新两个地方的状态。仔细检查borrowBook和returnBook方法确保用户操作和图书状态更新是原子性的即要么都成功要么都失败。用户菜单和管理员菜单功能混淆权限判断逻辑 (isAdmin) 有误。检查登录后设置isAdmin的逻辑。本文示例简单通过用户ID判断真实系统应基于角色(Role)字段。8. 项目优化与扩展建议完成基础版本后你可以尝试以下挑战让项目更接近实际应用引入真正的数据库将DataRepository中的文件存储替换为MySQL或SQLite数据库。学习使用 JDBC 进行连接、执行 SQL 语句增删改查。这是从“玩具项目”到“真实项目”的关键一步。完善用户系统增加用户注册功能。为密码加密存储使用 MD5、SHA 或 BCrypt。增加用户角色如管理员、普通用户并基于角色进行更细粒度的权限控制。增强业务逻辑增加借阅期限和逾期罚款计算。实现图书分类和按分类、书名、作者查询。增加图书预约功能。改善用户体验将控制台界面升级为Java Swing或JavaFX图形界面。或者学习Spring Boot将其改造成一个带有简单页面的Web应用。代码结构优化引入接口如BookRepository、UserRepository让数据访问层更清晰。使用工厂模式或依赖注入来管理LibraryService和DataRepository的创建。增加更全面的异常处理机制和日志记录如使用 Log4j 或 SLF4J。这个图书管理系统项目就像一颗种子包含了Java核心应用的诸多要素。通过亲手实现它你不仅能巩固基础知识更能建立起“需求-设计-编码-测试”的完整开发思维。建议你不要止步于复制代码而是多思考“为什么这样设计”并大胆尝试上述的扩展功能。每解决一个扩展问题你的实战能力就向前迈进一大步。
返回列表