
简介这是一套基于Java、Swing和MySQL开发的考试管理系统项目面向计算机专业学生、Java桌面应用开发者以及需要快速搭建考试管理功能的教育机构或培训机构。系统围绕试题库管理、考试安排、成绩统计等真实业务场景展开利用Swing组件设计试题编辑、考试作答、成绩查询等图形界面通过JDBC连接MySQL完成数据的增删改查并涉及多线程并发处理、异常处理、文件导入导出、数据加密、基于角色的权限控制等关键技术模块覆盖桌面应用从界面、业务层到数据访问与安全控制的完整开发链路十分适合作为课程设计、毕业设计或Java GUI实战练手项目。资源压缩包为zip格式大小约2.21MB由于上游未提供文件数量与类型明细因此暂无法统计包内文件构成。目前已有1576人学习/下载该资源。借助这份项目资料读者可对照源码学习Swing界面搭建、MySQL表结构设计与SQL语句编写、JDBC增删改查流程等核心实现也能进一步梳理Java集合类的使用、事件监听机制和异常处理思路并在此基础上扩展成绩分析、试卷管理等功能快速形成一套可运行的考试管理桌面应用参考方案。1. 期末设计绕不开的 JavaSwingMySQL它到底在解决什么问题每年课程设计季总有一大批学生和转行者被分到同一个题目做一个考试管理系统技术栈限定 Java Swing 和 MySQL。说句实话这个组合在工业界已经很少见了Web 管理系统才是主流但它在教学场景里依然是绕不开的经典。原因是这个题目能把 Java 基础语法、面向对象设计、JDBC 数据库操作、GUI 事件监听这几块硬骨头一次全练到对新手来说是最短路径的“全栈”体验。本文不是给你抄一份现成源码而是以一线开发者的视角把这个项目从架构设计、数据库建表、界面落地到常见坑位讲透。你会看到一套不依赖任何框架、纯 JDBC Swing 的可靠方案也能知道哪些参数必须调、哪些雷千万别踩。如果你是正在做课程设计、或者想用这个项目练手准备 Java 面试这篇笔记应该能帮你少走两三天弯路。2. 先把架构立住Swing 做界面MySQL 存数据中间用 JDBC 搭桥2.1 分层设计别把 SQL 写进按钮的点击事件里见过太多课程设计代码全部逻辑堆在 JFrame 里按钮点击事件里直接写DriverManager.getConnection()然后现场拼 SQL。这样写的好处是代码短、交差快坏处是后续改需求基本等于重写。我们既然要把它当成一个正经项目做就要引入经典的三层结构界面层ViewSwing 组件只负责展示和接收用户操作。业务层Service考试流程、登录校验、成绩计算这些规则在这里实现。数据访问层DAO所有 JDBC 操作收拢到这里上层永远不知道你用的是 MySQL 还是文件。// dao/UserDao.java —— 数据访问层的一个典型方法 public User login(String username, String password) { String sql SELECT user_id, username, password, role FROM user 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()) { return new User(rs.getInt(user_id), rs.getString(username), rs.getString(role)); } } } catch (SQLException e) { e.printStackTrace(); } return null; }这段代码里值得注意的细节是PreparedStatement代替了字符串拼接?占位符防止 SQL 注入try-with-resources确保 Connection、PreparedStatement、ResultSet 用完后自动关闭避免连接泄漏。在写课程设计时你可以在代码注释里专门说明这两点答辩时这就是加分项。2.2 数据库连接参数url、user、password 背后的门道每个 DAO 类都需要拿到连接最笨的办法是每个类都写一遍驱动加载和连接创建。常见做法是写一个DBUtil工具类把连接参数集中在db.properties配置文件里。这里有个课程设计必考的细节为什么数据库 URL 里要带useSSLfalse和serverTimezoneAsia/Shanghai因为新版 MySQL 驱动默认会校验 SSL 证书并强制设置时区这两个参数不加连接时大概率直接抛异常。# src/db.properties jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/exam_db?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8 jdbc.userroot jdbc.password123456// util/DBUtil.java —— 静态代码块加载驱动保证只加载一次 public class DBUtil { private static String url; private static String user; private static String password; static { try (InputStream in DBUtil.class.getClassLoader().getResourceAsStream(db.properties)) { Properties props new Properties(); props.load(in); Class.forName(props.getProperty(jdbc.driver)); url props.getProperty(jdbc.url); user props.getProperty(jdbc.user); password props.getProperty(jdbc.password); } catch (Exception e) { throw new ExceptionInInitializerError(加载数据库配置失败); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(url, user, password); } }注意Class.forName这一行在 JDBC 4.0 之后其实可以省略因为 SPI 机制会自动加载驱动但保留它在新手项目里有两个好处一是让代码阅读者一眼看出用什么驱动二是兼容老版本 MySQL 驱动。你的实验报告里写清楚这一点比复制粘贴十行System.out.println强得多。2.3 一个容易翻车的选择Swing 默认线程模型Swing 是单线程模型的 GUI 框架所有界面更新必须在事件分发线程Event Dispatch ThreadEDT上执行。如果你在业务层做耗时操作比如导入大量考生数据时不另开线程界面就会卡死表现为白屏或拖不动窗口。// 正确示范在按钮事件里启动 SwingWorker 做耗时查询 JButton btnStart new JButton(开始考试); btnStart.addActionListener(e - { btnStart.setEnabled(false); // 防止重复提交 new SwingWorkerVoid, Void() { Override protected Void doInBackground() throws Exception { // 这里运行在后台线程可以执行 JDBC 查询 ExamService.startExam(); return null; } Override protected void done() { btnStart.setEnabled(true); lblStatus.setText(考试已开始); } }.execute(); });很多教程不会提这一层导致学生做出来的系统一旦数据量大就“莫名其妙很卡”。在答辩时如果你能主动说出 SwingWorker 的原理说明你真的理解了 Swing 的消息队列机制。3. 数据库建表考试系统的表设计不是堆字段3.1 五张核心表从用户到成绩的完整链路考试管理系统的数据模型往简单了做是两张表用户表、试卷表但要做成能拿得出手的课程设计至少是五张表用户表、考试表、试题表、考试题目关联表、成绩表。这样设计才能实现“一次考试多份试卷一份试卷多道题”的完整数据流。-- 用户表区分管理员、教师、学生三种角色 CREATE TABLE user ( user_id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL, role VARCHAR(10) NOT NULL DEFAULT student, real_name VARCHAR(20) DEFAULT NULL, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 考试表一次考试的基本信息和时间窗口 CREATE TABLE exam ( exam_id INT PRIMARY KEY AUTO_INCREMENT, exam_name VARCHAR(100) NOT NULL, start_time DATETIME NOT NULL, end_time DATETIME NOT NULL, duration INT DEFAULT 60 COMMENT 考试时长分钟, total_score INT DEFAULT 100, pass_score INT DEFAULT 60 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 试题表 CREATE TABLE question ( question_id INT PRIMARY KEY AUTO_INCREMENT, question_type VARCHAR(10) COMMENT single/multiple/judge, content TEXT NOT NULL, option_a VARCHAR(200), option_b VARCHAR(200), option_c VARCHAR(200), option_d VARCHAR(200), answer VARCHAR(10) NOT NULL COMMENT 单选题存A/B/C/D判断题存T/F, score INT DEFAULT 4 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 考试题目关联表一张考试卷由哪些题组成 CREATE TABLE exam_question ( exam_id INT NOT NULL, question_id INT NOT NULL, PRIMARY KEY (exam_id, question_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 成绩表考试结果按用户和考试维度记录 CREATE TABLE exam_result ( result_id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, exam_id INT NOT NULL, score DECIMAL(5,2) DEFAULT 0, status VARCHAR(10) DEFAULT not_submitted, submit_time DATETIME DEFAULT NULL ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这里有几个关键决策password字段长度设为 64 是为 MD5 加密预留空间如果你用了加盐的 SHA-256 就要 64 位十六进制字符串正好匹配DECIMAL(5,2)存成绩而非FLOAT避免浮点数精度问题所有表都指定ENGINEInnoDB因为课程设计可能涉及事务utf8mb4支持中文和特殊符号老项目用utf8会出现存储 Emoji 报错。3.2 外键到底要不要加一个纠结过很多次的问题教学里都强调外键约束能保证数据一致性实际做的时候新手最容易踩的坑是加了外键后删除顺序出错或者插入数据时被约束挡住然后一脸懵。我给你的建议是——表结构里保留外键逻辑这五张表天然有主外键关系但在建表 SQL 里不要物理加FOREIGN KEY约束改为在应用层 Service 里做校验。原因很简单这个项目的数据量不可能达到需要数据库并发强一致性的程度物理外键带来的级联删除和约束检查反而让调试变繁琐。你在实验报告里可以写“考虑到业务规模较小采用应用层保证数据完整性”答辩老师完全认可这种设计权衡。真正需要数据库事务的地方是——一个学生提交考试答案时需要同时写入成绩表和更新考试记录表这两步可以放到同一个事务里用Connection的setAutoCommit(false)控制。3.3 MySQL 编码的坑中文乱码的根源在连接层和建表层中文乱码是 Swing MySQL 项目里出现频率最高的“玄学”问题。实际上根源只有两个数据库表的字符集不是 utf8或者连接参数里没指定 characterEncoding。解决方式是双管齐下建表时统一CHARSETutf8mb4连接 URL 显式追加characterEncodingutf8。如果你用 Navicat 建表而不是命令行顺手改一下表属性里的字符集别用默认的 latin1。一个排查技巧是执行SHOW CREATE TABLE exam;看输出里有没有CHARSETutf8mb4没有就说明建表时被数据库默认字符集带偏了。4. 用 Swing 搭考试管理界面组件选用和事件流转的实战方案4.1 主窗口框架JFrame 布局和导航面板考试管理系统的界面不外乎登录页、管理主页、考试页、成绩查询页四类。登录页用GridBagLayout做居中定位管理主页用JSplitPane分隔左侧导航树和右侧内容区。代码结构上建议每个页面一个独立类继承JPanel或JFrame用CardLayout做页面切换。// view/MainFrame.java —— 管理主页核心骨架 public class MainFrame extends JFrame { private CardLayout cardLayout new CardLayout(); private JPanel cardPanel new JPanel(cardLayout); public MainFrame(User currentUser) { this.setTitle(在线考试管理系统); this.setSize(1024, 680); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setLocationRelativeTo(null); JTree navTree buildNavTree(currentUser.getRole()); JScrollPane treeScroll new JScrollPane(navTree); cardPanel.add(new ExamManagePanel(), exam_manage); cardPanel.add(new QuestionManagePanel(), question_manage); cardPanel.add(new ResultQueryPanel(), result_query); JSplitPane splitPane new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, treeScroll, cardPanel); splitPane.setDividerLocation(200); splitPane.setResizeWeight(0.2); this.setContentPane(splitPane); this.setVisible(true); } }这段代码的核心是用CardLayout持有多个子面板通过布局的show()方法切换。好处是页面跳转不销毁旧面板学生切换到考试页再切回管理页之前填写的筛选条件还在。JTree根据登录用户的角色动态生成不同导航节点——管理员看到用户管理和试题管理学生看到我的考试和我的成绩。4.2 考试倒计时与自动交卷一个典型的多线程应用考试页是整个系统最有趣的部分学生进入考试后界面上要显示剩余时间倒计时到时间自动交卷。这里有两个方案一个是在 EDT 上Thread.sleep(1000)循环更新标签一个是java.util.Timer定时任务。正确做法是后者因为前者会把事件分发线程卡住界面上的“下一题”按钮都点不动。// view/ExamPanel.java —— 倒计时与自动交卷 public class ExamPanel extends JPanel { private JLabel lblCountdown; private Timer countdownTimer; private int remainingSeconds; public void startExam(int durationMinutes) { remainingSeconds durationMinutes * 60; lblCountdown.setText(formatTime(remainingSeconds)); countdownTimer new Timer(1000, e - { remainingSeconds--; lblCountdown.setText(formatTime(remainingSeconds)); if (remainingSeconds 0) { countdownTimer.stop(); autoSubmit(); // 时间到自动交卷 } }); countdownTimer.start(); } private void autoSubmit() { JOptionPane.showMessageDialog(this, 考试时间到系统已自动交卷, 提示, JOptionPane.INFORMATION_MESSAGE); saveAnswers(); // 调用业务层保存答卷和成绩 } }javax.swing.Timer不是纯后台线程它的回调依然运行在 EDT 上且回调之间不会重叠所以更新 JLabel 是安全的。这个细节如果你能在代码注释里写明几乎可以断定答辩老师会追问一句“为什么不用 Thread.sleep”答出上面那句即可。4.3 表格渲染成绩查询页的 JTable 与 TableModel查询成绩结果集一般用JTable展示但直接把ResultSet往 JTable 塞是最常见的新手写法。正确做法是定义独立的TableModel让表格的数据和界面解耦。// model/ResultTableModel.java —— 只关心数据建模 public class ResultTableModel extends AbstractTableModel { private String[] columns {考试名称, 得分, 状态, 提交时间}; private ListExamResult results; public ResultTableModel(ListExamResult results) { this.results results; } Override public int getRowCount() { return results.size(); } Override public int getColumnCount() { return columns.length; } Override public Object getValueAt(int rowIndex, int columnIndex) { ExamResult r results.get(rowIndex); return switch (columnIndex) { case 0 - r.getExamName(); case 1 - r.getScore(); case 2 - r.getStatus(); case 3 - r.getSubmitTime(); default - null; }; } Override public String getColumnName(int column) { return columns[column]; } }用AbstractTableModel的好处是当业务层计算出新成绩后调用fireTableDataChanged()表格就会自动刷新。界面层只需要做一件事把业务层返回的ListExamResult塞进 modeltable.setModel()完事。有同学问能不能直接在 ResultSet 上 DataSource 套一层返回 TableModel答案是不要这样做因为 ResultSet 是游标型的一次性资源用完连接就关了表格数据就变成“黑匣子”状态下不可用了。5. 业务层的硬骨头登录校验、自动组卷、成绩判分5.1 登录校验的增强方案MD5 加盐与防 SQL 注入课程设计的登录功能最简单的做法是把用户名密码拼 SQL 查询。这样做不仅容易被 SQL 注入而且密码是明文存库。我建议哪怕时间再紧也要做一个加盐 MD5 的版本这段代码在面试时也能当 Java 基础篇的实战案例来讲。// service/UserService.java —— 加盐哈希与登录校验 public User login(String username, String password) throws NoSuchAlgorithmException { User user userDao.findByUsername(username); if (user ! null) { String salt user.getSalt(); String hashed md5WithSalt(password, salt); if (hashed.equals(user.getPassword())) { return user; } } return null; // 统一返回 null不提示用户是“用户不存在”还是“密码错误”防爆破 } private String md5WithSalt(String password, String salt) throws NoSuchAlgorithmException { MessageDigest md MessageDigest.getInstance(MD5); byte[] digest md.digest((password salt).getBytes(StandardCharsets.UTF_8)); StringBuilder sb new StringBuilder(); for (byte b : digest) { sb.append(String.format(%02x, b)); } return sb.toString(); }注意登录失败时不要告诉用户具体的错误原因这是安全上最常见的习惯。注册时生成随机salt把盐和哈希后的密码一起存进 user 表。虽然在工业界 MD5 已经因为彩虹表攻击被判了死刑但在这个课程设计里它比明文强太多且代码量只多几行投入产出比极高。5.2 自动组卷从题目表抽取试题的两个算法组卷就是一次 SELECT 查询核心是随机抽样。实现上最简单的是SELECT * FROM question ORDER BY RAND() LIMIT n但数据量大时ORDER BY RAND()会导致全表扫描很多做过的同学都发现系统越用越慢。数据量小2000 题以内的课程设计用这个没问题但如果你想在报告里写一点“算法优化”可以用按主键取随机数区间的方式。// service/PaperService.java —— 按题目 ID 范围随机抽样避免 ORDER BY RAND 全表排序 public ListQuestion generatePaper(int examId, int singleCount, int judgeCount) { int maxId questionDao.getMaxQuestionId(); ListQuestion selected new ArrayList(); Random rand new Random(); while (selected.size() singleCount judgeCount) { int randomId 1 rand.nextInt(maxId); // 生成 1 ~ maxId 的随机数 Question q questionDao.findById(randomId); if (q ! null !selected.contains(q)) { selected.add(q); } } return selected; }这种做法的代价是可能产生“空查”随机 ID 指向的行恰好被删除了那么findById返回 null 就需要重试。对于课程设计这个小规模数据集它是完全可以接受的方案。这里要提醒一个坑limit子句里的参数不能直接拼接要用PreparedStatement的setInt处理否则会被 MySQL 的语法解析器嫌弃。5.3 判分逻辑选择题的比对与多选题的容错判分是最容易出逻辑 bug 的地方。单选和判断直接比对答案字符串即可多选题的比对有两种策略——严格匹配错选漏选均不得分和部分得分漏选给一半分。我建议课程设计做严格匹配因为规则简单好解释如果要做部分得分需要额外记录选项个数。// service/ScoringService.java —— 考试成绩结算 public double computeScore(ListUserAnswer answers, ListQuestion questions) { double total 0; for (Question q : questions) { UserAnswer ans findAnswerByQuestionId(answers, q.getQuestionId()); if (ans null || ans.getAnswer().equals()) { continue; // 未作答给 0 分 } if (q.getQuestionType().equals(single) || q.getQuestionType().equals(judge)) { if (q.getAnswer().equalsIgnoreCase(ans.getAnswer().trim())) { total q.getScore(); } } else if (q.getQuestionType().equals(multiple)) { // 多选题严格模式选项完全一致才得分 if (normalizeMultiAnswer(q.getAnswer()).equals(normalizeMultiAnswer(ans.getAnswer()))) { total q.getScore(); } } } return total; }一个特别注意的地方如果学生把多选题答案存成了“A,B,C”这种带逗号的字符串做equals比较前要统一格式——把学生的答案字符串拆成字符数组排序后再拼回来。否则学生选了“C,B,A”标准答案是“A,B,C”文本比较一定不过。这种“玄学”失分真的非常冤枉。6. 避坑指南Swing MySQL 课程设计最常见的 5 个致命问题6.1 现象运行就报 java.sql.SQLException: No suitable driver found原因驱动 jar 包没有正确添加到项目依赖中。新手用 IDE 时常常只写了代码里的Class.forName但没把 MySQL Connector/J 的 jar 包放到项目的 lib 目录或者通过 Maven 导入。或者 jar 包版本和 MySQL 服务端版本不匹配比如 MySQL 8 配了com.mysql.jdbc.Driver老驱动。解决在项目根目录建lib文件夹将mysql-connector-java-8.0.x.jar放进去IDE 中右键 Add as Library。代码里统一用com.mysql.cj.jdbc.Driver8.x 驱动不要再用com.mysql.jdbc.Driver5.x 使用的路径。6.2 现象查询中文全部变成问号 ???原因乱码问题 90% 是建表和连接层字符集不一致。执行 SQL 时看到中文正常但 Java 代码查出来是???说明 Java 到 MySQL 的连接没有使用 utf8 编码。解决逐层排查——先看SHOW CREATE TABLE确认表是utf8mb4再检查连接 URL 是否带characterEncodingutf8最后在代码里SET NAMES utf8mb4兜底。这三层都对齐乱码一定会消失。如果还乱检查你的.java文件本身用 IDE 打开右下角看是不是 GBK 编码改成 UTF-8 保存。6.3 现象窗口打开后点击按钮半天没反应然后一股脑弹出一堆对话框原因错误的线程使用。在ActionListener里直接执行了网络请求或复杂 SQL导致 EDT 线程被阻塞界面像素渲染和事件分发全部停止。用户点多次按钮事件排队等主逻辑跑完后会连续触发多个监听器看起来就像“卡死后一次弹出多个框”。解决把耗时操作包进SwingWorker的doInBackground()完成后在done()里更新界面。特别注意一个反直觉的坑SwingWorker的done()方法内不要再启动另一个Thread也不要做Thread.sleep()模拟加载这会让 EDT 再次阻塞。6.4 现象MySQL 启动报错 ERROR 2002 (HY000): Cant connect to local MySQL server through socket原因这个报错出现时通常是 MySQL 服务没有启动或者连接 MySQL 时用了 socket 方式而服务端没开监听。我们在 Java 连接时用的是 TCP/IP 协议但也可能是客户端环境变量没配置导致命令行无法访问。解决先确认服务已经启动。Windows 下在“服务”里找 MySQL 服务确认正在运行Linux 下用systemctl status mysql或service mysql status查看。如果服务没启动系统启动时报错多半是 my.cnf 配置有问题比如 datadir 目录没有权限。注意区分两个场景命令行报错和 Java 程序报错不是一回事Java 报错一定是连接 URL 里的 host 或端口不对。6.5 现象关闭考试窗口时程序不退出或者表数据没保存就没了原因Swing 程序在默认情况下点击窗口右上角 X 只是隐藏窗口JVM 仍在运行。如果你没有设置setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)程序会残留在后台。可能更严重的是你关闭了JFrame但没关数据库连接池程序进程不退出。解决主窗口设置setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)在windowClosing事件里调用DBUtil.close()释放全部连接。记住一个血泪经验Swing 程序不会因为你关掉窗口就自动退出一定要显式写System.exit(0)或者在事件里dispose()后再退出。7. 进阶玩法用导出功能验证系统闭环给项目加一层实战价值一个能交差的考试管理系统最后一定要有一个“验证闭环”的功能——把考试成绩导出成 Excel 报告。这既能验证你的数据流是通的也能在课程设计答辩时展示“完整交付”的意识。做法是引入 Apache POI一个 Java 操作 Office 文件的库在结果查询页加一个“导出成绩单”按钮。// service/ExportService.java —— 用 Apache POI 导出成绩列表 public void exportResultToExcel(int examId, String outputPath) throws IOException { ListExamResult results resultDao.findByExamId(examId); try (Workbook wb new XSSFWorkbook()) { Sheet sheet wb.createSheet(考试成绩); Row header sheet.createRow(0); header.createCell(0).setCellValue(学号); header.createCell(1).setCellValue(姓名); header.createCell(2).setCellValue(得分); header.createCell(3).setCellValue(状态); for (int i 0; i results.size(); i) { ExamResult r results.get(i); Row row sheet.createRow(i 1); row.createCell(0).setCellValue(r.getUserId()); row.createCell(1).setCellValue(r.getUserName()); row.createCell(2).setCellValue(r.getScore()); row.createCell(3).setCellValue(r.getStatus()); } try (FileOutputStream fos new FileOutputStream(outputPath)) { wb.write(fos); } } }值得在答辩时讲的一个点是POI 的XSSFWorkbook对应的是 xlsx 格式能处理的记录数上限更高excel 2003 的 xls 上限 65536 行课程设计的数据量完全够用。如果你的项目数据量真的达到十几万那就应该改成 CSV 了——这属于技术边界问题说出来反而显得你有工程思维。这个功能的本质是给项目增加了一个“数据出口”让成绩数据不只有 GUI 表格里那一眼能看到的几十行还能交付给教师做统计分析。导出完成后用 Excel 打开检查一下学号、得分、状态三个字段有没有错列数据完整性一目了然。如果有时间还可以在导出前加一个“总分汇总 sheet”用公式统计平均分和及格率这对答辩评委来说是非常实在的加分项。我在自己带过的课程设计小组里反复强调一个习惯所有功能做完后从头把“管理员创建考试 → 学生参加考试 → 自动判分 → 导出报表”这条链路完整跑一遍任何一环断了说明前面的设计有遗漏不要等到上交前才匆忙补。这个习惯我沿用到了真正的项目开发里也算是一种贯穿始终的工作方法——希望帮到你。本文还有配套的精品资源点击获取