news 2026/9/1 12:20:40

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

作者头像

张小明

前端开发工程师

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

很多Java初学者在学完基础语法后,常常会陷入“不知道能做什么项目”的迷茫。一个结构清晰、功能完整的图书管理系统,正是连接Java理论与实战开发的绝佳桥梁。它不仅涵盖了面向对象、集合、数据库操作等核心知识,还能让你体验从需求分析到功能实现的完整开发流程。本文将手把手带你从零搭建一个控制台版本的图书管理系统,深入剖析每一行代码的设计思路,并提供完整的项目源码。无论你是刚入门的小白,还是想巩固基础知识的开发者,都能通过这个项目获得扎实的成长。

1. 项目背景与核心价值

1.1 为什么选择图书管理系统作为练手项目?

图书管理系统是一个经典的业务场景,它麻雀虽小,五脏俱全。对于初学者而言,它具有以下几个不可替代的优势:

  1. 业务逻辑直观:每个人都对“借书”、“还书”、“查询图书”等操作有基本认知,无需花费大量时间理解复杂的业务规则,可以将精力集中于技术实现本身。
  2. 技术栈覆盖全面:一个完整的项目会涉及Java基础语法(类、对象、继承、多态)、集合框架(List、Map存储数据)、JDBC数据库操作控制台交互(Scanner)、异常处理文件IO(如果使用文件存储)等核心知识点。
  3. 分层架构启蒙:即使是控制台程序,我们也可以有意识地按照MVC(Model-View-Controller)三层架构(表现层、业务逻辑层、数据访问层)的思想来组织代码,为后续学习Spring Boot等框架打下坚实基础。
  4. 可扩展性强:基础版本完成后,你可以轻松地为它添加新功能,如用户登录、图书分类、借阅逾期计算、数据统计报表等,让项目不断“生长”,持续练习。

1.2 我们将实现什么?

本文将实现一个基于控制台和集合(暂不使用数据库)的简易图书管理系统。主要功能包括:

  • 管理员功能:图书的增、删、改、查。
  • 用户功能:查询图书、借阅图书、归还图书。
  • 数据持久化:使用文件来模拟数据库,实现程序关闭后数据不丢失。

通过这个项目,你将清晰地掌握如何将现实世界的“图书”和“用户”抽象为Java中的类和对象,并管理它们之间的交互关系。

2. 开发环境准备

在开始编码之前,请确保你的开发环境已经就绪。本教程以最通用的环境为例,重点在于思路的传递,你可以根据实际情况调整。

  • 操作系统:Windows 10/11, macOS, Linux 均可。
  • Java开发工具包 (JDK):版本 8 或以上(推荐 JDK 11 或 17)。确保已正确安装并配置环境变量。
    • 验证方法:打开命令行(CMD或Terminal),输入java -versionjavac -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; // 图书是否被借出 (另一种设计:用stock>0判断) 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 List<String> 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 List<String> 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为图书ID,value为Book对象 private Map<String, Book> bookMap; // 使用Map存储用户,key为用户ID,value为User对象 private Map<String, 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 List<Book> 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 = (Map<String, 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 = (Map<String, 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()); } } }

关键点解析

  1. 单例模式DataRepository采用单例模式,确保整个程序只有一个数据源,避免数据不一致。
  2. 序列化:使用ObjectOutputStreamObjectInputStream将对象保存到文件,实现简易持久化。注意,实体类BookUser需要实现Serializable接口(代码中已省略,实际需加上)。
  3. 数据隔离:图书和用户数据分开存储在不同的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() { List<Book> 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; } List<String> 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; } List<String> 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 如何运行项目?

  1. 在你的IDE(如IntelliJ IDEA)中,创建一个新的Java项目。
  2. 按照上文提供的代码,创建对应的包 (com.library.model,com.library.repository,com.library.service) 和类文件。
  3. 注意:BookUser类需要实现Serializable接口,即在类声明后加implements Serializable
  4. MainApp类设置为运行主类。
  5. 运行MainAppmain方法。

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已正确设置。
运行时报ClassNotFoundExceptionNoClassDefFoundError实体类BookUser未实现Serializable接口。在这两个类的声明后添加implements java.io.Serializable
程序无法读取或保存数据文件1. 文件读写权限不足。
2. 程序运行的工作目录不对。
1. 检查当前用户对项目目录是否有读写权限。
2. 在IDE中运行配置里,设置正确的工作目录(通常是项目根目录)。数据文件books.datusers.dat会生成在那里。
输入数字时程序报NumberFormatException用户在应该输入数字的地方输入了非数字字符。代码中已使用try-catch进行基本处理。在实际项目中,需要更健壮的输入验证,例如循环提示直到输入合法。
借阅图书时,库存减少了但用户借阅列表没增加(或反之)业务逻辑不完整或存在漏洞,没有同时更新两个地方的状态。仔细检查borrowBookreturnBook方法,确保用户操作和图书状态更新是原子性的,即要么都成功,要么都失败。
用户菜单和管理员菜单功能混淆权限判断逻辑 (isAdmin) 有误。检查登录后设置isAdmin的逻辑。本文示例简单通过用户ID判断,真实系统应基于角色(Role)字段。

8. 项目优化与扩展建议

完成基础版本后,你可以尝试以下挑战,让项目更接近实际应用:

  1. 引入真正的数据库:将DataRepository中的文件存储替换为MySQLSQLite数据库。学习使用 JDBC 进行连接、执行 SQL 语句(增删改查)。这是从“玩具项目”到“真实项目”的关键一步。
  2. 完善用户系统
    • 增加用户注册功能。
    • 为密码加密存储(使用 MD5、SHA 或 BCrypt)。
    • 增加用户角色(如管理员、普通用户),并基于角色进行更细粒度的权限控制。
  3. 增强业务逻辑
    • 增加借阅期限和逾期罚款计算。
    • 实现图书分类和按分类、书名、作者查询。
    • 增加图书预约功能。
  4. 改善用户体验
    • 将控制台界面升级为Java SwingJavaFX图形界面。
    • 或者,学习Spring Boot将其改造成一个带有简单页面的Web应用。
  5. 代码结构优化
    • 引入接口,如BookRepositoryUserRepository,让数据访问层更清晰。
    • 使用工厂模式或依赖注入来管理LibraryServiceDataRepository的创建。
    • 增加更全面的异常处理机制和日志记录(如使用 Log4j 或 SLF4J)。

这个图书管理系统项目就像一颗种子,包含了Java核心应用的诸多要素。通过亲手实现它,你不仅能巩固基础知识,更能建立起“需求-设计-编码-测试”的完整开发思维。建议你不要止步于复制代码,而是多思考“为什么这样设计”,并大胆尝试上述的扩展功能。每解决一个扩展问题,你的实战能力就向前迈进一大步。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/1 12:20:34

阔比例手机屏幕适配:从沉浸体验到开发实践的全方位解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/1 12:18:31

3D打印古建筑模型全流程:从模型修复到切片优化实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/1 12:17:50

嵌入式Linux项目文档:从能跑到能讲清楚的关键

一位做嵌入式Linux开发的师弟&#xff0c;前阵子去面试&#xff0c;回来后和我复盘了一个特别典型的场景&#xff1a;面试官指着他简历上的“基于Qt的嵌入式监控终端”问&#xff0c;如果串口突然丢数据&#xff0c;你怎么排查&#xff1f;项目是他和同学一起做的&#xff0c;当…

作者头像 李华
网站建设 2026/9/1 12:14:40

华为AI岗秋招面试复盘:大模型、RAG与Agent全流程拆解

10月15号&#xff0c;我把华为AI岗的秋招面试一天之内全部走完了。早上九点半开始&#xff0c;一面、二面、主管面连着打&#xff0c;结束的时候已经下午快六点&#xff0c;整个人像被掏空&#xff0c;但脑子反而特别清醒——因为所有考题、追问、手撕代码的点&#xff0c;我在…

作者头像 李华
网站建设 2026/9/1 12:14:40

AI视频工具新版实操:单剧本生成与分镜随机性解析

这次我们来看“多多 AI 视频工具 0827 新版”的实操。标题里几个关键词已经把这个工具最值得关注的点说完了&#xff1a;单剧本生成、自动标题标签、分镜随机性&#xff0c;再加上 8.28 多多带货观察。简单说&#xff0c;这是一个面向带货场景的 AI 视频创作工具&#xff0c;重…

作者头像 李华
网站建设 2026/9/1 12:14:24

从多项式表示出发,量化神经网络“简单性”的ED方法解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华