资讯动态

Java Swing酒店管理系统源码拆解:从Swing界面到MySQL数据库的完整实现

发布时间:2026/10/18 23:33:38 来源:尧图企业网站定制
简介一套面向Java初学者与毕业设计者的酒店管理系统项目源码基于Swing构建图形界面配合MySQL持久化数据覆盖客房管理、预订登记、客户信息维护及账单结算等酒店日常运营核心模块。资源共294个文件压缩包72MB左右其中包含54个Java源文件、59个class编译文件、135张gif操作演示截图、17张png界面图另有SQL脚本、数据库备份文件以及doc文档便于直接导入数据库后运行查看效果。项目在源码中完整实践了Swing事件处理、JDBC数据库访问、DAO分层以及MVC设计思想并通过try-catch处理数据库操作与用户输入的异常适合用来理解桌面应用从界面布局到数据落地的全过程。已有2180人学习查看尤其适合课程设计、毕业设计或希望快速上手JavaGUI开发的读者借助演示动图、文档说明和可运行的工程结构可以在调试过程中逐步掌握预订业务流程与数据表设计思路并方便在此基础上扩展功能或二次开发。1. 先看门道这套Swing酒店管理系统解决了前台管理的什么痛点某酒店前台一直用纸质登记簿管房态客人电话查订单要翻好几分钟月底对账更是头疼。我拆的这套 Java Swing MySQL 酒店管理系统源码就是针对这类场景做的一站式桌面管理工具散客登记、团队入住、客房预订、订单查询、账单结算全部在一个 Swing 窗口里完成数据落在 MySQL 里查询不用再翻本子。适合正在学 Java 桌面应用和数据库课程的开发者——它把 Swing 组件、事件监听、JDBC、DAO 这些知识点串在一条真实的业务链路上不是只教你拖按钮的 Demo而是能直接跑起来当课程设计交差的完整工程。2. 技术选型与工程结构Swing界面下的MVC和DAO怎么分工2.1 为什么选 Swing MySQL 而不是 Web 方案这套系统选 Swing 做界面层最直接的原因是它不需要部署 Tomcat、不需要浏览器兼容性测试双击启动类就能看到窗口非常适合酒店前台这种内网单机环境。Swing 自带 JFrame、JButton、JTable、JComboBox 等组件做表单、表格、弹窗这类交互是顺手的事。MySQL 则负责把所有业务数据持久化——客房状态、客户资料、预订记录、结算金额全部通过 JDBC 写入数据库程序退出再打开数据还在。和 Web 方案比起来Swing 项目的结构更直白没有前端框架、没有 HTTP 请求路由用户点一个按钮事件监听器直接调用业务方法方法里操作 DAO 访问数据库。这种界面—逻辑—数据三层在代码里是肉眼可见的对初学者理解分层思想反而更友好。2.2 源码目录结构从 .class 文件反推工程布局拿到这套源码时里面是一批编译好的 .class 文件和一张 off.bmp 图标没有直接给 src 目录。我用反编译工具配合类名推断整理出它的原始工程结构。类名起得很规矩基本一眼能看出职责hotel-management-system/ ├── src/ │ ├── HotelFrame.java // 主窗口程序入口 │ ├── Engage.java // 预订业务逻辑 │ ├── EngageInfo.java // 预订信息实体类 │ ├── Individual.java // 散客登记面板 │ ├── Team.java // 团队登记面板 │ ├── Query.java // 订单查询面板 │ ├── DBHelper.java // JDBC连接工具 │ ├── RoomDAO.java // 客房数据访问对象 │ └── EngageDAO.java // 订单数据访问对象 ├── images/ │ └── off.bmp // 界面装饰用位图 └── hotel_db.sql // 建库建表脚本所有类都平铺在默认包下没有拆 controller / service / dao 子包这是课程设计最常见的组织方式。功能不算少但 MVC 的影子是能看出来的HotelFrame、Individual、Team、Query 这些 JFrame / JPanel 属于视图层Engage 里处理按钮点击和业务校验承担控制器职责EngageInfo 是模型层对应订单表的一条记录RoomDAO、EngageDAO 把 SQL 操作封装起来上层不直接写 JDBC 代码。2.3 主窗口框架事件驱动是怎么跑起来的Swing 程序的核心是事件分发线程EDT用户点按钮、输文字、选下拉框都会产生事件由注册的监听器响应。HotelFrame 的主界面是典型的左侧导航 右侧内容区布局用 JPanel 的 CardLayout 做面板切换。看一下主窗口初始化的常见写法public HotelFrame() { setTitle(酒店管理系统); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(900, 600); setLocationRelativeTo(null); // 左侧功能导航按钮区 JPanel navPanel new JPanel(new GridLayout(0, 1, 5, 5)); JButton engageBtn new JButton(客房预订); JButton individualBtn new JButton(散客登记); JButton teamBtn new JButton(团队登记); JButton queryBtn new JButton(订单查询); navPanel.add(engageBtn); navPanel.add(individualBtn); navPanel.add(teamBtn); navPanel.add(queryBtn); // 右侧卡片面板每个业务模块一个面板 cardPanel new JPanel(new CardLayout()); cardPanel.add(new EngagePanel(this), engage); cardPanel.add(new IndividualPanel(this), individual); cardPanel.add(new TeamPanel(this), team); cardPanel.add(new QueryPanel(this), query); add(navPanel, BorderLayout.WEST); add(cardPanel, BorderLayout.CENTER); // 点击左侧按钮右侧切换对应卡片 engageBtn.addActionListener(e - cardLayout.show(cardPanel, engage)); individualBtn.addActionListener(e - cardLayout.show(cardPanel, individual)); teamBtn.addActionListener(e - cardLayout.show(cardPanel, team)); queryBtn.addActionListener(e - cardLayout.show(cardPanel, query)); }这段代码里值得注意的参数细节GridLayout(0, 1, 5, 5)表示不限行数、1 列按钮之间的水平和垂直间距各 5 像素e - cardLayout.show(...)是 Java 8 的 lambda 写法等价于匿名 ActionListener 类编译时要求源码级别在 1.8 以上很多新手在这上面翻车——编译报错说 lambda 不被支持多半是 IDE 的 Java 版本没切到 8。卡片面板的名字要和add时的第一个字符串参数一致否则show找不到面板界面点了没反应这种问题不报异常排查起来全靠细心。整个事件驱动的链路是按钮点击 → 触发 ActionEvent → 监听到事件 → 切换卡片或调用业务方法 → 业务方法操作 DAO → DAO 访问 MySQL → 返回结果刷新 JTable。搞清楚这条线后面的功能拆解就顺了。3. 核心模块拆解从点击预订到房间落库的完整链路3.1 订单实体EngageInfo 承载哪些字段预订是这个系统的核心业务先看承载业务数据的实体类 EngageInfo。它对应数据库里的一张预订表字段设计直接决定后面查询和结算怎么做public class EngageInfo { private String orderId; // 订单号主键 private int roomId; // 关联的房间ID private String customerName; // 顾客姓名 private String idCard; // 身份证号 private String engageDate; // 预订入住日期 private String leaveDate; // 预计离店日期 private double price; // 房间单价 private String state; // 订单状态未入住/已入住/已退房 public EngageInfo() {} public EngageInfo(String orderId, int roomId, String customerName, String idCard, String engageDate, double price, String state) { this.orderId orderId; this.roomId roomId; this.customerName customerName; this.idCard idCard; this.engageDate engageDate; this.price price; this.state state; } // 各字段的 getter / setter 省略 }这里有个设计选择要注意engageDate和leaveDate用的是 String 而不是 java.util.Date。String 能直接从 JTextField 拿值写进数据库也简单SQL 里DATE类型能隐式转换但如果后面要做日期区间统计比如7 月一共入住多少间夜String 存储会导致 SQL 里写一堆STR_TO_DATE转换性能和写法都别扭。我一般会建议改成java.sql.Date界面上用JFormattedTextField配合yyyy-MM-dd格式代价是编码多一些收益是查询统计省心很多。3.2 预订动作从按钮事件到订单落库预订的核心逻辑在 Engage 类里用户在前台页面填好姓名、身份证、选好房型、入住日期点预订按钮后事件监听器会触发一段类似下面的处理流程public void doEngage() { // 1. 从界面控件取值 String customerName nameField.getText().trim(); String idCard idCardField.getText().trim(); String roomType (String) roomTypeBox.getSelectedItem(); String engageDate dateField.getText().trim(); // 2. 基础非空校验不通过就弹窗提示 if (customerName.isEmpty() || idCard.isEmpty() || engageDate.isEmpty()) { JOptionPane.showMessageDialog(frame, 姓名、身份证号、入住日期不能为空); return; } // 3. 校验身份证长度18位 if (idCard.length() ! 18) { JOptionPane.showMessageDialog(frame, 身份证号必须为18位); return; } // 4. 查该房型剩余空房数量 int freeCount roomDAO.countFreeRooms(roomType); if (freeCount 0) { JOptionPane.showMessageDialog(frame, 该房型已满房请选择其他房型); return; } // 5. 生成订单号YD 时间戳 两位随机数 String orderId YD System.currentTimeMillis() String.format(%02d, new Random().nextInt(100)); // 6. 取出该房型下第一间空闲房间 Room room roomDAO.findFirstFreeRoom(roomType); // 7. 构造订单对象写入订单表 EngageInfo info new EngageInfo(orderId, room.getRoomId(), customerName, idCard, engageDate, room.getPrice(), 未入住); boolean ok engageDAO.insert(info); // 8. 写库成功后把房间状态改为已预订(state1) if (ok) { roomDAO.updateState(room.getRoomId(), 1); JOptionPane.showMessageDialog(frame, 预订成功订单号 orderId); clearForm(); refreshTable(); } else { JOptionPane.showMessageDialog(frame, 预订失败请稍后重试); } }这段代码把一条完整的预订业务链路走完了。几个设计点我展开说一下订单号生成YD System.currentTimeMillis() 两位随机数同一毫秒内并发下单才有极小概率撞号单机系统够用。但注意String.format(%02d, ...)是 Java 5 之后才有的老项目里如果看到Random.nextInt(100) 这种拼接也别觉得奇怪。两步写在两个 DAO 里先查空房、再插入订单、最后改状态这中间没有事务控制。insert成功了updateState万一抛异常会出现订单存在但房间状态没改的死数据。课程设计不会深究但这就是后面真实系统要上Transactional或手动 commit/rollback 的原因。界面字段校验只做了非空和身份证长度没有校验身份证校验位、日期格式这些。Integer.parseInt这类转换操作也没套 try-catch用户输入abc当房费金额时程序会直接崩。这个位置的健壮性二次开发时可以优先补。3.3 散客登记与团队登记Individual 和 Team 的差异Individual 是散客登记面板场景是客人已经在店内、直接办理入住。和预订的区别在于散客登记时房间状态要直接从空闲改成已入住同时订单状态从未入住改成已入住并且在登记界面要把房间号、入住时间、预收押金这些字段一并填掉。Individual 的表单通常是一个大 JPanel里面嵌 JTextField、JComboBox 和 JTableJTable 实时显示当前空闲房间列表数据来自roomDAO.findRoomsByState(0)。Team 团队登记的复杂度高一个档次。一个团队可能同时开 5 间房界面上要有房间数选择、领队信息、团队成员名单核心逻辑是循环处理每间房逐间查空房、逐间生成订单。这类循环写起来不难但最容易踩坑的是部分成功——5 间房订到第 3 间时发现第 4 间没了前面 3 间已经写库。课程设计里可以用最土的办法解决先一次性查出所有空闲房数量不够就直接拒绝够再循环写入。这属于典型的用业务约束绕开事务问题的思路虽然不优雅但稳。3.4 查询模块Query 里的多表关联Query 是用户每天用得最多的功能前台用它查某个客人现在住哪间房某订单是什么状态。它的核心 SQL 涉及两张表——订单表关联客房表public ListMapString, Object searchOrders(String keyword) { String sql SELECT o.order_id, r.room_no, r.room_type, o.customer_name, o.engage_date, o.leave_date, o.state FROM engage_info o LEFT JOIN room r ON o.room_id r.room_id WHERE o.customer_name LIKE ? ORDER BY o.engage_date DESC; ListMapString, Object result new ArrayList(); try (Connection conn DBHelper.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, % keyword %); try (ResultSet rs ps.executeQuery()) { while (rs.next()) { MapString, Object row new HashMap(); row.put(orderId, rs.getString(order_id)); row.put(roomNo, rs.getString(room_no)); row.put(customerName, rs.getString(customer_name)); row.put(state, rs.getString(state)); result.add(row); } } } catch (SQLException e) { e.printStackTrace(); } return result; }LEFT JOIN在这里比INNER JOIN合理——就算订单表里 room_id 因为数据问题对不上客房表查询也不会把订单丢掉。LIKE ?配合%keyword%实现模糊搜索这个位置必须用 PreparedStatement 的参数占位符拼如果图省事直接字符串拼接 SQL等于把 SQL 注入漏洞写在脸上。返回结果用ListMapString, Object是灵活的做法表结构改了不用跟着改实体类但代价是取值时字段名拼错不会编译报错要等运行时才发现。另一种做法是定义一个OrderVO视图对象安全性更高代码也更啰嗦。Query 查出来的结果最终要铺到 JTable 里。Swing 的 JTable 本身不直接接受 List需要套一层DefaultTableModel把 List 转成二维数组。这一步也是新手最容易迷茫的地方——明明查询返回了一堆数据界面上表格就是空的八成是 Model 的行数据没填对或者列的标识数组和行数组长度不一致。4. 数据库设计与 JDBC 访问层酒店数据怎么存、怎么读4.1 建表脚本room 和 engage_info 的关系这套系统的数据库核心就两张表但两张表之间的关系已经足够展示关系型设计的基本思路。客房表管房态订单表管业务通过 room_id 外键关联-- 客房表房间静态信息和当前状态 CREATE TABLE room ( room_id INT PRIMARY KEY AUTO_INCREMENT COMMENT 房间ID, room_no VARCHAR(10) NOT NULL COMMENT 房间号如 501、602, room_type VARCHAR(20) NOT NULL COMMENT 房型单人间/标准间/三人间/套房, price DECIMAL(10,2) NOT NULL COMMENT 门市价单位元, state TINYINT DEFAULT 0 COMMENT 0空闲 1已预订 2已入住 ); -- 订单表每一笔预订/入住记录 CREATE TABLE engage_info ( order_id VARCHAR(30) PRIMARY KEY COMMENT 订单号业务主键, room_id INT NOT NULL COMMENT 关联客房表, customer_name VARCHAR(50) NOT NULL COMMENT 顾客姓名, id_card VARCHAR(18) NOT NULL COMMENT 身份证号, engage_date DATE NOT NULL COMMENT 入住日期, leave_date DATE DEFAULT NULL COMMENT 离店日期, price DECIMAL(10,2) NOT NULL COMMENT 成交价, state TINYINT DEFAULT 0 COMMENT 0已预订 1已入住 2已退房 3已取消, FOREIGN KEY (room_id) REFERENCES room(room_id) );两个表里的state字段含义不一样这是最容易看晕的地方。room.state 描述的是物理房态engage_info.state 描述的是订单生命周期。两个状态需要联动预订成功 → room1办理入住 → room2、订单1退房结算 → room0、订单2。这个联动逻辑分散在 Engage、Individual、Team 的代码里没有统一封装属于后续重构的重点区域。字段类型的选择也值得抄笔记金额用DECIMAL(10,2)而不用 double是因为二进制浮点数算钱会有精度误差0.1 0.2 这种在 double 里是 0.30000000000000004TINYINT存状态比字符串省空间、查询快order_id用 VARCHAR 而不用自增 INT是因为业务上订单号要给人看、要能一眼看出是预订还是入住。这套设计不是最优的但每个选择都有理由适合做课程设计答辩素材。4.2 DBHelperJDBC 连接的标准写法JDBC 连接这块几乎所有课程设计项目都长一个样一个工具类负责加载驱动、提供 Connection其他 DAO 从这里拿连接。看代码public class DBHelper { private static final String DRIVER com.mysql.jdbc.Driver; private static final String URL jdbc:mysql://localhost:3306/hotel_db ?useUnicodetruecharacterEncodingutf8 serverTimezoneAsia/Shanghai; private static final String USER root; private static final String PASSWORD 123456; public static Connection getConnection() { try { Class.forName(DRIVER); return DriverManager.getConnection(URL, USER, PASSWORD); } catch (ClassNotFoundException e) { throw new RuntimeException(MySQL JDBC驱动未找到请检查jar包, e); } catch (SQLException e) { throw new RuntimeException(数据库连接失败请检查账号密码和MySQL服务, e); } } }URL 里的三个参数是血泪经验堆出来的useUnicodetruecharacterEncodingutf8少一个就会出现中文乱码serverTimezoneAsia/Shanghai是 MySQL 8.0 的硬性要求不写直接报The server time zone value异常。驱动类名也有讲究——MySQL 5.x 的驱动是com.mysql.jdbc.DriverMySQL 8.x 改成了com.mysql.cj.jdbc.Driver新旧驱动和旧写法不匹配时控制台会报ClassNotFoundException但这个错其实是提示你驱动类名写错了不是驱动包没引入。初学者最容易在这里卡半小时。DriverManager.getConnection每次都在创建物理连接单机系统还好如果后续要做统计报表、并发高一点连接池改造会是很自然的下一步。另外注意这个工具类没有任何关闭连接的逻辑关闭连接的责任落在 DAO 的finally块或 try-with-resources 里用上面的try (Connection conn ...)写法编译器会保证连接自动关闭这是 Java 7 之后的推荐做法比手动finally { conn.close(); }少很多样板代码。4.3 DAO 层设计SQL 与业务逻辑的边界DAOData Access Object模式的核心目标是让上层代码完全看不到 SQL。业务层只调roomDAO.findRoomsByState(0)或engageDAO.insert(info)底层是 PreparedStatement 还是存储过程上层不关心。看一个查询空闲房间的 DAO 实现public ListRoom findRoomsByState(int state) { String sql SELECT * FROM room WHERE state ? ORDER BY room_no; ListRoom list new ArrayList(); try (Connection conn DBHelper.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setInt(1, state); try (ResultSet rs ps.executeQuery()) { while (rs.next()) { Room room new Room(); room.setRoomId(rs.getInt(room_id)); room.setRoomNo(rs.getString(room_no)); room.setRoomType(rs.getString(room_type)); room.setPrice(rs.getBigDecimal(price).doubleValue()); room.setState(rs.getInt(state)); list.add(room); } } } catch (SQLException e) { e.printStackTrace(); } return list; }方法里做的事情拆开看是四步拼接 SQL 模板、用?占位符绑定参数、执行查询、把 ResultSet 每一行转成 Room 对象。ResultSet这层转换是最枯燥也最容易出错的——列名拼错不报编译错运行时报Column room_noo not foundrs.getInt(state)拿到 NULL 时返回 0如果数据库允许 state 为空会产生业务误判。习惯做法是像上面这样取每一列时明确指定列名而不是getInt(1)这种按序号取——表结构加了一列之后按序号取的代码全线错位按列名取的代码毫发无伤。DAO 层还有一个设计细节Room是一个简单的 POJO只包含字段和 getter/setter不掺杂任何 JDBC 逻辑。这样 UI 层可以直接用room.getRoomNo()做展示也可以把 Room 对象传到其他模块复用。后面如果想把系统改成 MyBatis 或 JPA 持久化只要重写 DAO 实现类业务层完全不用动这就是分层的收益。5. 本地跑通与踩坑记录环境配置、启动顺序和常见问题5.1 环境准备与启动步骤跑这套系统前先确认三样东西JDK 8 及以上、MySQL 5.7 或 8.0、对应版本的 mysql-connector-java 驱动 jar。JDK 8 配 5.1.49 驱动是稳的组合JDK 11 以上建议直接上 8.0.x 驱动。启动的完整流程启动 MySQL 服务在命令行执行mysql -uroot -p登录。用source命令导入建表脚本或者用 Navicat / DataGrip 直接执行 hotel_db.sql 里的建库建表语句。mysql source /path/to/hotel_db.sql;在源码根目录放好驱动 jar编译所有 Java 文件。注意指定编码Windows 下默认 GBK 会编译报错javac -encoding UTF-8 -cp mysql-connector-java-8.0.33.jar *.java运行主类。Windows 用分号分隔 classpathLinux / macOS 用冒号java -cp .;mysql-connector-java-8.0.33.jar HotelFrame编译通过但运行报ClassNotFoundException的99% 是第二步 -cp 只写了 jar 没带上.JVM 找不到 HotelFrame 类。Windows 下如果-cp引号没加对也会有类似问题建议把驱动 jar 直接放在源码目录里再执行少绕一层路径坑。5.2 常见问题排查五个真实踩坑记录以下问题我拆项目时逐一复现过每条按现象、原因、解决的顺序写拿到源码跑不通时直接对着查。问题一编译时报错误: 编码 UTF-8 的不可映射字符现象javac 编译时中文注释和字符串里的汉字全部变成乱码报错行号指向所有含中文的代码行。原因是 Windows 中文系统默认编码是 GBK而源码文件是 UTF-8 保存的javac 用了错误编码读源文件。解决方法是编译命令强制加-encoding UTF-8或者在 IDE 里把项目全局编码设为 UTF-8。问题二运行时报 ClassNotFoundException: com.mysql.jdbc.Driver现象代码里 Class.forName 找不到驱动类程序直接抛异常。这个情况分两种一是驱动 jar 没放到 classpath二是 mysql-connector-java 8.x 已经放弃旧驱动类名。8.x 的正确类名是com.mysql.cj.jdbc.Driver如果代码里写的是旧的com.mysql.jdbc.Driver要么改代码要么把 jar 降到 5.1.49。我一般建议用 5.1.49 配 JDK 8课程设计环境最稳。问题三连接时报 SQLException: The server time zone value现象MySQL 8.0 连数据库时报时区异常连接 URL 是jdbc:mysql://localhost:3306/hotel_db。原因是 8.0 驱动强制校验数据库时区没有明确指定就报错。解决URL 后面拼serverTimezoneAsia/Shanghai顺便把useUnicodetruecharacterEncodingutf8加上一个字符串解决问题。问题四界面上所有中文都是问号现象按钮文字、表格内容全是??英文正常。原因分两层连接 URL 少了characterEncodingutf8或者建库时没有指定 utf8 字符集。解决URL 加参数后重启程序建库语句明确写成CREATE DATABASE hotel_db DEFAULT CHARSET utf8 COLLATE utf8_general_ci。两个地方都检查过再启动乱码一定消失。问题五点了按钮界面卡死转圈圈现象点击查询全部订单后窗口无响应鼠标变成等待状态几秒后恢复或一直不恢复。原因是数据库查询、网络操作直接跑在事件分发线程EDT上慢 SQL 执行期间界面无法处理其他事件。课程设计阶段数据量小感觉不明显一旦订单表上了几千行卡顿就会出现。解决先用索引优化 SQL——engage_info表的customer_name字段加普通索引state字段如果常用来过滤也建索引更彻底的做法是用SwingWorker把耗时查询丢到后台线程执行查询完成再通过done()方法刷新 JTable。这个改造在下一章展开讲。6. 二次开发思路从单机桌面端走向可扩展的管理后台拿到这套源码的人跑通只是第一步我更建议做一轮课设翻新式的改造既能避开答辩追问也能真正把 Swing MySQL 用得像个正经系统。三个方向按性价比排序连接池替换 JDBC 直连、密码字段加盐哈希、界面切换SwingWorker做异步查询。连接池改造是最值得做的一项。用 Druid 替代DBHelper里的DriverManager.getConnection如下DruidDataSource dataSource new DruidDataSource(); dataSource.setDriverClassName(com.mysql.cj.jdbc.Driver); dataSource.setUrl(jdbc:mysql://localhost:3306/hotel_db?serverTimezoneAsia/Shanghai); dataSource.setUsername(root); dataSource.setPassword(123456); dataSource.setInitialSize(5); dataSource.setMaxActive(10); // 改造后的 DAO 从池里拿连接用完归还不再反复创建 Connection conn dataSource.getConnection();这套改动不用动业务代码只要把DBHelper.getConnection()内部换成从dataSource取连接即可。第二个方向是密码字段原来的 customer 表如果只有明文密码字段改成SHA-256(密码 固定盐)存储在登录校验时用同样的盐算一遍再比较哈希值。第三个方向是用SwingWorkerListRoom, Void包装查询方法doInBackground()里跑 SQLdone()里把结果塞进 JTable 的DefaultTableModel彻底告别界面卡死。动手改的时候记住一条原则每改一块先跑一遍对应功能再改下一块。这个项目没有自动化测试全靠手动点界面验证一次改太多地方出了问题根本定位不到是哪一步引入的。我拆过不少课设项目发现最大的通病不是功能少而是改到最后连自己都说不清哪些代码还在用。所以我会先删掉无用代码、整理类注释再动结构。几次把项目交给朋友跑对方反手就把连接串少一个参数的问题截图发回来。从那以后我每次接手 Swing 项目都会先花十分钟把连接配置和主窗口启动流程捋一遍再动业务代码。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑