ARTICLE DETAIL

资讯详情

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

从环境搭建到事务处理:Java+MySQL图书管理系统实战指南

从环境搭建到事务处理:Java+MySQL图书管理系统实战指南 简介这是一份基于JAVA与MySQL的图书管理系统完整项目资源面向计算机专业学生、课程设计及毕业设计者也适合希望学习Java项目开发的初学者。系统采用MVC三层架构组织代码实现了用户登录、用户信息管理、图书信息增删改查、图书借阅与归还等完整功能并附带所有界面图片资源和数据库文件导入IDE即可直接运行。压缩包整体约24.46MB内含多个项目文件其中以Java源码、SQL数据库脚本、图片素材和Word文档报告为主要类型Word报告包含摘要、可行性研究、系统流程图、数据流图等内容能够帮助读者快速理清系统设计脉络。资源包已有2840人学习下载对于需要参考完整实现方案或在此基础上进行功能扩展的学习者来说是一份实用且可操作的参考资料。1. 一拿到“JAVA MySQL 图书管理系统.zip”先别解压这个项目到底在解决什么解压这个基于JAVA MySQL的图书管理系统.zip之前很多人其实已经失败过一次了——不是代码跑不起来而是根本没走到代码那一步。这个典型的课程设计/毕业设计项目本质是一条完整的Java后端基础链路JDK环境、MySQL 5.7数据库、JDBC连接、登录与CRUD、事务处理和项目打包规范。它能帮你解决的不是“造一个图书馆”而是“用最小成本把Java基础、MySQL常用操作和工程化意识串成一套能答辩、能二次开发的东西”。适合三类人准备课设答辩的学生、刚学完Java基础想动手做项目的初学者、需要一个骨架改业务做毕设的从业者。下面这篇文章的目标很简单让你在半天内把它跑通并且每一行代码你都讲得出口。2. 先把运行环境立住JDK、MySQL 5.7.44与Java环境变量配置失败时看哪里2.1 先定版本为什么课设项目最好用MySQL 5.7而不是8.0网上铺天盖地的mysql安装教程大多在讲8.0但课设压缩包里的代码往往还停留在5.x时代。这类项目的JDBC工具类里写的多半是Class.forName(com.mysql.jdbc.Driver)这个驱动类名从MySQL Connector/J 8.0开始就被改名成com.mysql.cj.jdbc.Driver了。你按最新版装好8.0再跑老代码第一关就是 ClassNotFoundException紧接着又是SSL参数、时区参数、认证插件报错每一步都会消耗你不该消耗的耐心。所以我的建议很直白除非老师明确要求8.0否则一律装MySQL 5.7.44。它是5.7系列的后期版本稳定、资料全、默认认证插件是mysql_native_password对老驱动最友好网上搜“mysql 5.7.44 安装过程详细”也能找到大量对照。下载时去MySQL官网的下载归档页找 Windows x64 ZIP Archive 版本别下MSI安装版MSI版在部分机器上会卡在最后的配置向导而且不好卸载重来。ZIP版解压即用配合一个 my.ini 文件整个环境完全在你的掌控里。2.2 JDK安装与JAVA_HOME环境变量配置命令、验证与常见翻车JAVA版本也建议选JDK 8。很多课设代码是用JDK 8编译的JSP/Servlet项目在JDK 17以上会遇到模块访问限制就算你技术新装个JDK 11也够用了别一上来就上JDK 21。JDK下载安装时记住一个原则安装路径不要有中文、空格和特殊符号C:\Program Files\Java\jdk1.8.0_202这样的路径是最稳的。安装后配置JAVA_HOME环境变量。命令行方式最快但要注意setx的坑setx JAVA_HOME C:\Program Files\Java\jdk1.8.0_202 setx Path %JAVA_HOME%\bin;%Path% java -version javac -version第一行写入JAVA_HOME变量第二行把JDK的bin目录追加到Path最前面然后必须重开一个命令行窗口再执行java -version否则读到的还是旧环境变量。这里最典型的翻车点是用setx的%Path%会被展开成当前值再写回如果你系统Path原本很长二次执行可能被截断。所以我一般推荐在“系统属性 → 环境变量”里手动点选编辑把%JAVA_HOME%\bin加在Path最前面一劳永逸。如果java -version能出来而javac报错说明你只装了JRE或者JAVA_HOME指错了位置回到变量设置里重新核对路径八成是bin目录写成了jre\bin。2.3 MySQL 5.7.44安装配置my.ini、初始化与启动ZIP版MySQL没有安装程序一切靠命令。先把压缩包解压到纯英文目录我一般放到D:\mysql-5.7.44-winx64。在解压目录下新建一个my.ini内容如下[mysqld] basedirD:/mysql-5.7.44-winx64 datadirD:/mysql-5.7.44-winx64/data port3306 character-set-serverutf8mb4 collation-serverutf8mb4_general_ci default-authentication-pluginmysql_native_password skip-grant-tables0 [client] default-character-setutf8mb4 port3306basedir指向解压根目录datadir指向数据目录这个目录一开始是不存在的别手动去建空目录让初始化命令自己生成。character-set-server和collation-server用 utf8mb4避免后面中文乱码default-authentication-plugin指定老式密码插件就是为了让5.1.49版本的JDBC驱动能顺利认账。注意datadir不要写成D:/mysql-5.7.44-winx64/data/这种带结尾斜杠的路径某些版本会报错。接着用管理员身份打开命令行进入解压目录的bin目录执行初始化。这里建议用--initialize-insecure它会生成一个空密码的root账户比你猜一个密码再忘记来得省事cd /d D:\mysql-5.7.44-winx64\bin mysqld --defaults-fileD:\mysql-5.7.44-winx64\my.ini --initialize-insecure --console--defaults-file必须写在最前面这是MySQL读取配置的固定顺序。看到console输出的最后一行没有error说明初始化成功此时 data 目录自动生成。然后安装并启动Windows服务mysqld --defaults-fileD:\mysql-5.7.44-winx64\my.ini --install MySQL57 net start MySQL57 mysql -uroot -p--install是注册Windows服务服务名取MySQL57方便和系统里的其他MySQL区分。如果提示服务已存在先执行mysqld --remove再重来如果net start报错立刻执行mysqld --console前台启动真正的错误原因会直接打在屏幕上比翻Windows事件查看器高效得多。连接测试成功后给root设置密码并刷新权限ALTER USER rootlocalhost IDENTIFIED BY 123456; FLUSH PRIVILEGES;注意mysql 5.7 的 root 默认只允许 localhost 连接后面代码里连接地址写localhost或127.0.0.1即可别改成%到处允许外网访问课设阶段用不到也不需要。3. 从建库到连库图书管理系统的表设计与JDBC连接代码3.1 核心表结构图书表、读者表与借阅表怎么设计一个能拿得出手的图书管理系统至少要有三张表存放图书信息的book、存放登录账号的reader、记录借还行为的borrow。别一上来就堆十几张表课设答辩时你讲不清楚代码也会被事务搅成一团。三张表刚好覆盖了MySQL最常用的三个特性主键自增、唯一约束、外键关联。book表是业务主体书名、作者、出版社、ISBN、库存数量、分类是标配。stock字段我强烈建议加NOT NULL DEFAULT 0有同学写成允许空结果库存为NULL时stock - 1直接得到NULL判断逻辑全乱DEFAULT 0配合COMMENT写清楚含义老师和答辩评委一看就懂你理解了默认值的价值。reader表除了用户名密码还要一个status状态字段默认0表示正常1表示禁用这也是“mysql设置默认值为0”的正确姿势——状态字段就该有默认值而不是每次插入都靠代码传参。3.2 MySQL建库脚本字符集、外键与默认值打开命令行进入MySQL执行下面的建库建表脚本。这套脚本我建议你直接放到项目里的db/init.sql方便老师复现CREATE DATABASE IF NOT EXISTS library DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE library; CREATE TABLE book ( book_id INT PRIMARY KEY AUTO_INCREMENT, book_name VARCHAR(200) NOT NULL COMMENT 书名, author VARCHAR(100) DEFAULT NULL COMMENT 作者, publisher VARCHAR(100) DEFAULT NULL COMMENT 出版社, isbn VARCHAR(20) DEFAULT NULL, stock INT NOT NULL DEFAULT 0 COMMENT 当前库存默认0, category VARCHAR(50) DEFAULT NULL COMMENT 分类, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_isbn (isbn) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE reader ( reader_id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL, password VARCHAR(64) NOT NULL, real_name VARCHAR(50) DEFAULT NULL, status TINYINT NOT NULL DEFAULT 0 COMMENT 0正常 1禁用, UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE borrow ( borrow_id INT PRIMARY KEY AUTO_INCREMENT, book_id INT NOT NULL, reader_id INT NOT NULL, borrow_date DATE NOT NULL, due_date DATE NOT NULL, return_date DATE DEFAULT NULL, renew_count INT NOT NULL DEFAULT 0 COMMENT 续借次数, CONSTRAINT fk_borrow_book FOREIGN KEY (book_id) REFERENCES book (book_id), CONSTRAINT fk_borrow_reader FOREIGN KEY (reader_id) REFERENCES reader (reader_id), INDEX idx_borrow_return (return_date) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;CREATE DATABASE ... DEFAULT CHARACTER SET直接锁死数据库级别字符集每张表再单独声明ENGINEInnoDB这是因为后面借书还书要用事务而MyISAM是不支持行级锁和事务回滚的。UNIQUE KEY保证isbn、username不重复这是“唯一约束”的典型应用比你每次先查再插更可靠。外键FOREIGN KEY在课设里写上没问题它能体现你对关系型数据库的理解但要注意插入borrow之前book和reader必须有对应记录否则会报外键约束错误所以业务层的插入顺序要控制好。3.3 DBUtil工具类一行行读懂JDBC连接的参数配置环境搭好、表建完下一步是让Java连上MySQL。这是整个项目里最容易被复制粘贴糊弄过去的地方但也是报错最多的地方。我写一个最小可用的DBUtil.java每个参数都解释清楚package com.library.util; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class DBUtil { // useSSLfalse5.7不需要加密连接明文警告也消失 // serverTimezoneAsia/Shanghai解决时区识别乱码 // characterEncodingutf8解决中文乱码 private static final String URL jdbc:mysql://localhost:3306/library ?useSSLfalse serverTimezoneAsia/Shanghai characterEncodingutf8 allowPublicKeyRetrievaltrue; private static final String USER root; private static final String PASSWORD 123456; static { try { Class.forName(com.mysql.jdbc.Driver); } catch (ClassNotFoundException e) { throw new ExceptionInInitializerError( MySQL驱动未找到请确认 mysql-connector-java 已在 classpath); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } public static void close(Connection conn, Statement stmt, ResultSet rs) { try { if (rs ! null) rs.close(); } catch (SQLException e) { e.printStackTrace(); } try { if (stmt ! null) stmt.close(); } catch (SQLException e) { e.printStackTrace(); } try { if (conn ! null) conn.close(); } catch (SQLException e) { e.printStackTrace(); } } }Class.forName在静态代码块里执行作用是提前加载驱动类注册到DriverManager。getConnection每次调用都新建物理连接课设完全可以接受但你要知道真实的项目会用Druid或HikariCP做连接池否则并发一高就连不上库了这也是答辩时你可以主动提的加分项。URL里的四个参数缺一不可useSSLfalse消除SSL警告serverTimezoneAsia/Shanghai解决时区乱码characterEncodingutf8保证中文写入allowPublicKeyRetrievaltrue是为了配合8.0连接器时不用每次手动确认公钥。close方法统一关闭资源而且关闭顺序必须是 rs、stmt、conn反过来会引发“连接关闭导致结果集失效”的隐藏问题。4. 登录校验与图书CRUD把核心功能写成可以照抄的Java代码4.1 登录模块用PreparedStatement挡住SQL注入登录是每个管理系统都有的功能但写法不同技术含量完全不同。最忌讳的写法是把参数直接拼进SQL字符串里SELECT * FROM reader WHERE username username AND password password 。只要在密码框输入 or 11整个WHERE条件就被改写了这就是所谓的SQL注入也是java面试题里常问的基础安全问题。正确写法是用PreparedStatement的参数占位符让MySQL驱动帮你转义public Reader login(String username, String password) { String sql SELECT reader_id, username, real_name, status FROM reader WHERE username ? AND password ?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, username); ps.setString(2, password); try (ResultSet rs ps.executeQuery()) { if (rs.next()) { int status rs.getInt(status); if (status 1) { throw new RuntimeException(该账号已被禁用); } Reader reader new Reader(); reader.setReaderId(rs.getInt(reader_id)); reader.setUsername(rs.getString(username)); reader.setRealName(rs.getString(real_name)); return reader; } } } catch (SQLException e) { e.printStackTrace(); } return null; }?占位符是PreparedStatement的核心setString(1, username)会把第一个问号替换成参数值并自动处理单引号转义。这个方法还顺手做了状态判断第12行查出status后1就抛异常拒绝登录。这里没有用Statement而是PreparedStatement除了安全还有编译缓存带来的轻微性能提升课设代码写到这里已经比一堆网上的老代码高一个档次了。注意返回对象封装成Reader实体类这就是三层结构中entity层的作用别直接返回ResultSet连接一关数据就没了。4.2 图书查询列表LIMIT分页与模糊查询的参数绑定图书列表最容易被做成“一次性把所有数据查出来”数据量小没事数据量一大页面就卡死。这里直接上分页模糊查询这是真实项目里的标准做法public ListBook queryBooksByPage(String keyword, int page, int pageSize) { String sql SELECT book_id, book_name, author, publisher, isbn, stock FROM book WHERE book_name LIKE ? OR author LIKE ? ORDER BY book_id DESC LIMIT ?, ?; ListBook books new ArrayList(); try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, % keyword %); ps.setString(2, % keyword %); ps.setInt(3, (page - 1) * pageSize); ps.setInt(4, pageSize); try (ResultSet rs ps.executeQuery()) { while (rs.next()) { Book b new Book(); b.setBookId(rs.getInt(book_id)); b.setBookName(rs.getString(book_name)); b.setAuthor(rs.getString(author)); b.setStock(rs.getInt(stock)); books.add(b); } } } catch (SQLException e) { e.printStackTrace(); } return books; }LIKE ?配合setString(1, % keyword %)实现了模糊匹配注意百分号是拼在参数上的不是拼在SQL里的。LIMIT ?, ?是分页的核心公式偏移量(page - 1) * pageSize、每页条数pageSize。这个公式必须背下来答辩时老师换成“每页20条查第3页”你也能立刻说出offset是40。ORDER BY book_id DESC让新书排前面这是排序在SQL层解决的活不要用java里的冒泡排序手动去排数据库做排序比Java程序做高效得多理解这一点就理解了“数据计算尽量下推到数据库”这条工程原则。4.3 借书与还书用MySQL事务保证库存和借阅记录一致借书这个动作表面上是“往borrow表插一条记录”实际上涉及两步把book表的stock减1再往borrow表插入借阅记录。这两步必须同时成功或同时失败否则就会出现“borrow有记录但stock没减”的数据不一致。这就是mysql事务处理最典型的应用场景。我常用的写法是public void borrowBook(int bookId, int readerId, Date borrowDate, Date dueDate) { String checkSql SELECT stock FROM book WHERE book_id ? FOR UPDATE; String updateSql UPDATE book SET stock stock - 1 WHERE book_id ? AND stock 0; String insertSql INSERT INTO borrow (book_id, reader_id, borrow_date, due_date) VALUES (?, ?, ?, ?); try (Connection conn DBUtil.getConnection()) { conn.setAutoCommit(false); // 开启事务 int stock -1; try (PreparedStatement ps conn.prepareStatement(checkSql)) { ps.setInt(1, bookId); try (ResultSet rs ps.executeQuery()) { if (rs.next()) stock rs.getInt(stock); } } if (stock 0) { conn.rollback(); throw new RuntimeException(库存不足借书失败); } try (PreparedStatement ps conn.prepareStatement(updateSql)) { ps.setInt(1, bookId); int rows ps.executeUpdate(); if (rows 0) { conn.rollback(); throw new RuntimeException(库存扣减失败); } } try (PreparedStatement ps conn.prepareStatement(insertSql)) { ps.setInt(1, bookId); ps.setInt(2, readerId); ps.setDate(3, new java.sql.Date(borrowDate.getTime())); ps.setDate(4, new java.sql.Date(dueDate.getTime())); ps.executeUpdate(); } conn.commit(); } catch (SQLException e) { // 连接异常时回滚操作必须在连接未关闭前执行 throw new RuntimeException(借书失败, e); } }setAutoCommit(false)是事务的开关从此之后所有SQL都在同一个事务里执行。SELECT ... FOR UPDATE在MySQL InnoDB引擎下会对book_id对应的行加排他锁两个用户同时借同一本书时后一个请求会阻塞直到前一个提交这避免了“两个人同时查库存都是1、都借走”的经典并发问题。UPDATE ... WHERE stock 0是第二道保险就算锁没有问题条件更新也能兜底executeUpdate()返回影响行数为0就说明没抢到库存。所有检查都通过才commit()任何中途异常走catch回滚注意rollback和commit必须在连接还未关闭时调用。4.4 代码组织一个四层结构让课设答辩时更好讲代码不要全堆在UI页面里。我一般给学生建议一个极简四层entity放Book、Reader实体类dao放所有JDBC操作service放业务判断比如借书里的库存检查ui或controller只负责接收用户输入和展示结果。这个分层不是花架子它让“查询”和“业务规则”分开了——dao只管SQLservice只管判断UI只管交互。答辩时老师问“你修改图书库存的逻辑在哪里”你能直接指到service层对应的借书方法这就是工程意识的体现。5. 避坑手册从SSL连接错误到中文乱码6个让新手翻车的细节5.1 现象Access denied for user rootlocalhost密码确认没记错但一连接就报Access denied for user rootlocalhost (using password: YES)。原因只有三个密码确实不对代码里的USER写成了其他用户名root的认证插件是8.0风格的caching_sha2_password而你的驱动是5.1.49老版本。解决方式用命令行进入MySQL重置密码和插件ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 123456; FLUSH PRIVILEGES;坑就在IDENTIFIED WITH mysql_native_password这一句只改密码不换认证插件老驱动仍然不认账。改完后再用新密码连一次若还报错检查DBUtil.java里USER字段是否多写了空格这是复制粘贴常留的坑。5.2 现象Establishing SSL connection without servers identity verification is not recommended这句话第一次出现时能吓住一半新手因为一整屏红字结尾还带个warning。其实它不是异常只是一个建议性的警告源自Connector/J驱动你连了MySQL但没有做SSL验证。课设场景根本不需要加密连接所以直接在URL里加useSSLfalse警告消失。如果你用的是8.0驱动连5.7库还需要加allowPublicKeyRetrievaltrue它允许客户端在初次连接时从服务端获取RSA公钥否则还会报Public Key Retrieval is not allowed。这两个参数是同进同出的组合记住它们就避开了最常见的“mysql ssl连接错误”。5.3 现象The server time zone value Öйú±ê׼ʱ¼ä is unrecognized这个报错一眼看过去像乱码实际是时区问题MySQL 5.7 默认使用系统时区中文系统的时区名会被连接器读成乱码然后就抛了 unrecognized。解决方式是在连接URL里强制指定serverTimezoneAsia/Shanghai。还有一个连带坑如果你在连接串上写了serverTimezoneGMT%2B8符号会被解析成空格正确写法就写Asia/Shanghai这个标准时区ID。时区参数是ZIP版MySQL最容易漏的配置每次新建连接工具比如Navicat也会弹这个错填Asia/Shanghai就能过。5.4 现象Java控制台显示中文正常但存进MySQL后查出来全是问号这个现象最让人抓狂因为Java侧没问题连接也没报错问题出在字符集的三处一致性上。第一处连接URL要有characterEncodingutf8第二处数据库建库时指定DEFAULT CHARACTER SET utf8mb4表字段也跟库走第三处my.ini里character-set-serverutf8mb4。这三处任一处是默认的latin1中文写入就会变问号。排查顺序建议从MySQL端开始执行SHOW VARIABLES LIKE character%;看服务端字符集再查连接串最后用SELECT HEX(book_name) FROM book WHERE book_id1;看存储的字节是否变成3F也就是问号的十六进制。5.5 现象ClassNotFoundException: com.mysql.jdbc.Driver代码看起来没问题就是编译通过、运行报“找不到驱动类”。原因是mysql-connector-java的jar包没有进入运行时的classpath。普通IDEA项目要“右键jar包 → Add as Library”Web项目必须把jar放进WEB-INF/lib目录放错到WEB-INF/classes都没用。驱动版本建议统一用5.1.49它同时兼容5.7和JDK 8如果你非要用8.0的Connector/J驱动类名要改成com.mysql.cj.jdbc.Driver连接参数也会更苛刻这也是为什么老项目推荐用老驱动。5.6 现象MySQL 5.7.44服务起不来或3306端口被占用net start MySQL57报“服务无法启动”或者启动后连不上。先把服务卸载前台执行mysqld --defaults-fileD:\mysql-5.7.44-winx64\my.ini --console前台模式会把真正的错误打在屏幕上最常见是basedir和datadir路径写错、data目录没初始化、缺少VC运行库。另一个高发问题是3306端口被占用执行netstat -ano | findstr :3306如果已经有PID在监听要么是装了多个MySQL要么是开发工具自带的MySQL。这时候别想着换端口绕路最干净的办法是mysqld --remove卸载旧服务确认占用进程后重新安装服务。有同学问能不能用docker安装mysql绕开这些我的答案是课设阶段不建议docker的数据卷、端口映射、时区参数又多了一层黑匣子报错了更不好向老师解释本地ZIP版是所有问题里最可控的路径。6. 交付前的两小时功能自测、数据备份与WORD报告内容对齐6.1 用一组SQL快速验证借还与库存逻辑代码写完后别急着打开界面点来点去先用SQL做一次逻辑验证效率最高。模拟一次借书后执行下面这条查询检查库存和借阅记录是否一致SELECT b.book_id, b.book_name, b.stock, (SELECT COUNT(*) FROM borrow WHERE book_id b.book_id AND return_date IS NULL) AS borrowing_count FROM book b WHERE b.book_id 1;正常情况下stock borrowing_count应该等于你初始设置的库存总量。如果对不上优先检查事务边界是不是update stock成功了但insert borrow没执行或者相反。还书流程则验证UPDATE borrow SET return_date NOW() WHERE borrow_id ?和UPDATE book SET stock stock 1 WHERE book_id ?是否成对出现。自测通过后用mysqldump -uroot -p library library_backup.sql导出一份数据库备份这个文件既是你答辩现场万一数据搞坏的后悔药也可以作为WORD报告里“数据库设计”一章的实物附件。6.2 把WORD报告和工程实物对齐一份对照表和三个常见扣分点WORD文档报告是这个压缩包的另一半价值很多人代码做完了报告却写得和实现是两张皮。答辩老师不一定逐行看你的代码但一定会对着报告提问。我习惯在提交前做一个对照表确保报告里的每个章节都能在工程里找到实物报告章节工程中的对应物系统功能结构图UI菜单或Controller里的action列表数据库设计E-R图第3章的建表SQL字段要与ER图一一对应核心代码与说明DAO或Service层的关键方法代码必须来自工程运行截图登录页、图书列表、借书成功的真实截图测试用例第6.1节的两条验证SQL及结果三个高频扣分点我见得太多了提醒一句第一E-R图里的关系与实际外键对不上比如E-R图画了“读者可以同时借多本书”代码里却只有一条外键约束这属于设计与实现不一致第二截图里显示的时间是两年前的老师会问是不是网上扒的图所以截图必须当场做完再截第三报告里的核心代码段用的驱动类名和工程里不一样纯复制粘贴没检查这比代码写错更尴尬。最后说一句我自己踩过的老坑。当年我交课设时报告里的流程图画成了MVC架构代码实际却写了个三层结构答辩老师一眼就看出对不上当场让我把流程图画出来。从那以后我养成了一个习惯写完代码再写报告报告里每一张图、每一段代码都从工程里现取不凭记忆画。这个习惯后来帮我避开过很多次“实现与文档脱节”的倒霉事也希望帮到你。本文还有配套的精品资源点击获取
返回列表