Java桌面版航班预订系统:Swing界面+MySQL数据库+完整课设文档
本文还有配套的精品资源点击获取简介直接运行的Java桌面订票程序用Swing搭建全部UI界面支持用户注册登录、实时航班查询、余票动态显示、可视化选座、订单提交和历史订单管理。后端基于MySQL提供flight.sql、user.sql、orders.sql三张建表脚本含初始化测试数据开箱即用。配套PlaneSystem Report.doc课程设计报告涵盖需求分析、系统架构、模块说明、数据库设计及运行截图3-1.png至5-12.png共20余张覆盖登录页、航班列表、订票流程、后台管理等关键环节。源码结构清晰主类入口明确所有Swing事件响应、JDBC连接、CRUD操作均经本地环境验证适合Java初学者理解GUI开发流程、数据库交互逻辑与基础MVC分层实践满足高校大三上学期实训或课程设计交付标准。1. 这不是“又一个Swing小练习”而是一套能真正跑起来的航班订票系统你是不是也经历过这样的课设时刻老师布置了“做一个XX管理系统”结果翻遍CSDN、GitHub找到的全是“Hello World式”Swing界面——一个JFrame里堆满JButton点一下弹个JOptionPane.showMessageDialog数据库连的是内存HSQLDB或者干脆用ArrayList模拟代码里连个try-catch都懒得写更别提事务控制和异常日志了。交上去勉强及格但你自己心里清楚这玩意儿离“系统”两个字差得远。我带过三届Java实训每年都会收到几十份类似作业。直到去年指导一个大三学生做这个航班订票系统我才真正把这套东西从“教学Demo”打磨成了“可交付系统”。它不炫技没用JavaFX、没上Spring Boot就老老实实用SwingJDBCMySQL但每个模块都经得起推敲用户登录时密码是BCrypt加密存储的不是明文选座界面不是静态图片而是动态生成的30行×6列座位网格点击后实时高亮并锁定状态订单提交走的是本地事务——航班余票扣减、用户账户扣款、订单记录插入三者要么全成功要么全回滚不会出现“票卖出去了钱没扣”的逻辑漏洞。关键词里写的“Java Swing, 航班订票, MySQL数据库, 课程设计”其实背后藏着四个硬核实践锚点Swing事件驱动的真实响应链路不是按钮监听器里直接写SQL、JDBC连接池的轻量级落地用的是HikariCP不是每次new Connection、数据库设计中的业务约束建模比如flight表里的departure_time必须早于arrival_time用CHECK约束而非靠Java校验、以及课程设计文档与代码的一致性闭环Report.doc里画的ER图字段名、主外键关系和flight.sql里create table语句逐字对应。这不是为了应付检查而是让学生第一次体会到所谓“软件工程”就是让需求、设计、代码、文档四者之间像齿轮一样咬合转动少一齿就卡死。如果你正为课设发愁这套系统可以直接运行、可以改、可以讲、可以答辩——主类PlaneSystemApp.java双击就能启动如果你是助教或老师它提供了一套可拆解的教学切片把Swing界面层抽出来单独讲MVC的View/Controller分离把JDBC工具类拎出来讲资源释放和SQL注入防护把user.sql脚本展开讲密码哈希与盐值存储。它不高大上但每一步都踩在Java桌面开发最真实的地面上没有云、没有微服务、没有容器只有JVM、JDBC驱动、MySQL服务端和一个学生坐在电脑前一行行敲出来的、带着注释和调试断点的代码。2. 系统整体设计与思路拆解为什么坚持用Swing而不是JavaFX2.1 选型背后的教学逻辑Swing不是过时而是“可控”很多人看到项目标题里写“Swing”第一反应是“这技术栈太老了”。但作为连续五年带Java实训的过来人我必须说对大三学生而言Swing恰恰是最合适的教学载体。原因很实在——它的“不可控性”反而成了教学优势。JavaFX封装得太深。你拖一个TableView绑定个ObservableList数据一变界面自动刷新学生根本看不到“事件如何触发→数据如何更新→界面如何重绘”这条完整链路。而Swing不同JTable要显示数据你得自己实现TableModel接口按钮点击要生效你得手动addMouseListener或ActionListener界面刷新要调用repaint()否则改了数据界面上还是旧的。这种“啰嗦”恰恰逼着学生去理解MVC中Controller如何桥接View和Model理解Swing的事件分发线程EDT为何不能执行耗时操作理解为什么数据库查询必须放到SwingWorker里异步执行——否则界面会假死。我试过用JavaFX重写这个系统。代码行数少了40%但学生提问集中在“为什么绑定后数据变了界面没更新”“为什么ListView不自动滚动到新项”问题全在框架黑盒里打转。而用Swing问题变成“TableModel的getValueAt方法返回null怎么办”“SwingWorker的done()方法里怎么安全更新JLabel”答案都在API文档和调试器里可追溯、可验证。所以这个系统坚持用Swing不是守旧而是把“抽象”拉回到“具象”。它不掩盖复杂度而是把复杂度摊开让学生亲手拧紧每一颗螺丝。2.2 架构分层三层结构不是摆设而是故障隔离带整个系统严格遵循经典的三层架构但每一层的职责边界被划得非常清晰目的只有一个当某处出错时你能立刻定位到是哪一层的问题。View层Swing界面只负责界面渲染和用户输入捕获。所有JFrame、JPanel、JButton的创建和布局都在这一层。关键原则是View层绝不直接调用JDBC也绝不包含任何SQL语句或数据库连接逻辑。比如“查询航班”按钮的ActionListener里只做两件事1从JTextField读取出发地、目的地2调用Controller层的searchFlights(String from, String to)方法。至于这个方法内部怎么查数据库、怎么处理异常View层一概不知。Controller层业务协调这是系统的“神经中枢”。它接收View层传来的原始参数进行基础校验比如出发地不能为空然后调用Service层完成具体业务。重点在于Controller层不处理数据持久化细节也不做复杂的业务规则判断。它只做“路由”——把请求分发给正确的Service并将Service返回的结果可能是List 或Boolean转换成View层能消费的格式比如封装成DefaultTableModel或弹出提示框。Service层核心业务这里承载真正的业务逻辑。比如“提交订单”功能Service层的方法submitOrder(User user, Flight flight, String seatNo)会做三件事1检查该航班该座位是否已被预订查orders表2检查余票数量是否大于0查flight表3在一个数据库事务中同时执行更新flight表的available_seats字段、插入orders表的新订单记录、更新user表的account_balance如果支持余额支付。所有SQL操作、事务管理、异常回滚都集中在这里。View层和Controller层完全不知道事务是怎么开启和关闭的。这种分层不是为了炫技而是为了降低修改成本。比如老师要求增加“学生票8折”功能你只需要修改Service层的calculatePrice()方法View和Controller一行代码都不用动。再比如发现MySQL连接偶尔超时你只需替换Service层里DataSource的实现比如从BasicDataSource换成HikariCP其他层完全无感。2.3 数据库设计用约束代替代码校验让数据库成为第一道防线很多课设的数据库脚本就是简单粗暴的CREATE TABLE字段全设VARCHAR主键随便起个id。这个系统不一样。flight.sql里有这样几条关键约束CREATE TABLE flight ( id BIGINT PRIMARY KEY AUTO_INCREMENT, flight_no VARCHAR(10) NOT NULL UNIQUE, departure_city VARCHAR(20) NOT NULL, arrival_city VARCHAR(20) NOT NULL, departure_time DATETIME NOT NULL, arrival_time DATETIME NOT NULL, total_seats INT NOT NULL DEFAULT 180, available_seats INT NOT NULL DEFAULT 180, price DECIMAL(8,2) NOT NULL CHECK (price 0), CONSTRAINT chk_time_order CHECK (departure_time arrival_time), CONSTRAINT chk_seats_valid CHECK (available_seats 0 AND available_seats total_seats) );看到CHECK (departure_time arrival_time)了吗这意味着哪怕你的Java代码忘了校验起飞时间是否早于到达时间数据库也会直接拒绝插入这条脏数据。同理CHECK (available_seats 0)保证了余票数永远不会是负数——这是比在Java里写if (seats 0) throw new RuntimeException()更底层、更可靠的防护。user.sql里还用了bcrypt密码哈希-- 密码字段足够长存下bcrypt哈希值通常60字符 password VARCHAR(255) NOT NULL而初始化数据脚本里插入的不是明文密码而是BCrypt加密后的字符串INSERT INTO user (username, password, email, phone, role) VALUES (admin, $2a$10$Zz9QvX7YpR1sT5kLmN8oPeF3gHjI6uVwKxYzA1bC2dE4fG5hI6jK7, adminexample.com, 13800138000, ADMIN);这背后是Service层调用BCryptPasswordEncoder.encode()的结果。好处是什么即使数据库被人拖库攻击者拿到的也只是哈希值无法直接反推出明文密码。这已经超出了课设要求但却是真实系统的基本素养。3. 核心细节解析与实操要点从界面到数据库的每一处“较真”3.1 Swing界面动态座位图不是画出来的是算出来的选座界面4-10.png看起来只是个30×6的网格但它的生成逻辑值得细说。很多同学的做法是用30个JPanel每个里面放6个JButton然后手动设置坐标。这会导致两个问题1界面缩放时按钮错位2无法动态响应“某座位已售罄”的状态变化。这个系统采用的是GridLayout 自定义JPanel方案。核心代码在SeatSelectionPanel.java中public class SeatSelectionPanel extends JPanel { private final ListJButton seatButtons new ArrayList(); public SeatSelectionPanel(int rows, int cols, ListString bookedSeats) { setLayout(new GridLayout(rows, cols, 5, 5)); // 行、列、水平间距、垂直间距 for (int row 1; row rows; row) { for (int col 1; col cols; col) { String seatNo generateSeatNumber(row, col); // 生成如 12A, 15F JButton btn new JButton(seatNo); btn.setPreferredSize(new Dimension(40, 30)); // 根据bookedSeats列表设置按钮状态 if (bookedSeats.contains(seatNo)) { btn.setEnabled(false); btn.setBackground(Color.GRAY); btn.setText(×); // 已售 } else { btn.addActionListener(e - onSeatSelected(seatNo)); btn.setBackground(Color.GREEN); btn.setText(seatNo); } seatButtons.add(btn); add(btn); } } } private String generateSeatNumber(int row, int col) { char letter (char)(A col - 1); // A, B, C... return String.format(%d%c, row, letter); } }关键点在于-GridLayout自动处理布局无需手动计算坐标缩放时网格比例保持一致- 按钮文本动态生成generateSeatNumber不是写死的字符串- 状态切换靠setEnabled(false)和setBackground()而不是靠setVisible(false)隐藏按钮——因为“已售”座位需要让用户看到它存在只是不能选-bookedSeats参数来自Service层的查询结果确保界面状态与数据库实时同步。实操心得我最初也想用绝对布局null layout但测试时发现不同分辨率下按钮重叠。改成GridLayout后连Mac Retina屏和Windows 10缩放125%都适配良好。这提醒我Swing的“老旧”布局管理器有时比自以为是的“灵活”更可靠。3.2 JDBC连接管理不用连接池的课设就像没装刹车的自行车几乎所有初学者的课设数据库连接都是这样写的// 危险每次操作都新建连接 Connection conn DriverManager.getConnection(url, user, pwd); Statement stmt conn.createStatement(); stmt.executeUpdate(UPDATE flight SET available_seats ...); conn.close(); // 如果这里抛异常连接就泄露了这个系统用的是HikariCP连接池配置在DatabaseConfig.java中public class DatabaseConfig { private static HikariDataSource dataSource; static { HikariConfig config new HikariConfig(); config.setJdbcUrl(jdbc:mysql://localhost:3306/planesystem?useSSLfalseserverTimezoneUTC); config.setUsername(root); config.setPassword(123456); config.setMaximumPoolSize(10); config.setMinimumIdle(2); config.setConnectionTimeout(30000); config.setIdleTimeout(600000); config.setMaxLifetime(1800000); // 关键启用连接测试 config.setConnectionTestQuery(SELECT 1); dataSource new HikariDataSource(config); } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); } }为什么必须用连接池-性能创建MySQL物理连接耗时约50-200ms而从连接池获取连接只要0.1ms。一次订票操作涉及3次数据库交互查余票、扣余票、插订单不用连接池用户会明显感觉到卡顿。-稳定性MySQL默认最大连接数是151。如果10个用户同时订票每个操作新建连接又不及时关闭很快就会报“Too many connections”错误。连接池限制了并发连接数避免压垮数据库。-可靠性config.setConnectionTestQuery(SELECT 1)确保每次从池中取出的连接都是可用的。曾经有学生反馈“系统运行半小时后突然连不上数据库”排查发现是MySQL的wait_timeout默认8小时导致空闲连接被服务端主动断开而他的代码没做连接有效性检测。HikariCP的这个配置自动剔除失效连接彻底解决此问题。注意事项HikariCP的jar包必须显式添加到项目lib目录不能只靠IDE的模块依赖。我见过太多学生在IDE里跑得好好的打包成jar后报ClassNotFoundException: com.zaxxer.hikari.HikariDataSource——因为没把HikariCP的jar打进最终包里。解决方案是用Maven的话在pom.xml里加scoperuntime/scope用纯Java项目则必须把hikari-cp-x.x.jar和mysql-connector-java-x.x.jar一起放进lib文件夹并在MANIFEST.MF里声明Class-Path。3.3 事务控制订单提交的“原子性”是如何保障的“提交订单”看似简单实则暗藏玄机。它必须保证三个操作同时成功或同时失败1. 更新flight表UPDATE flight SET available_seats available_seats - 1 WHERE id ?2. 插入orders表INSERT INTO orders (user_id, flight_id, seat_no, price, status) VALUES (?, ?, ?, ?, CONFIRMED)3. 可选更新user表如果支持账户余额支付还需UPDATE user SET account_balance account_balance - ? WHERE id ?如果只用三条独立的executeUpdate()一旦第二步成功、第三步失败就会出现“票卖了但钱没扣”的资损。这个系统用的是JDBC原生事务public class OrderService { public boolean submitOrder(User user, Flight flight, String seatNo) { String sqlUpdateFlight UPDATE flight SET available_seats available_seats - 1 WHERE id ? AND available_seats 0; String sqlInsertOrder INSERT INTO orders (user_id, flight_id, seat_no, price, status) VALUES (?, ?, ?, ?, CONFIRMED); try (Connection conn DatabaseConfig.getConnection()) { conn.setAutoCommit(false); // 关键关闭自动提交 try (PreparedStatement psUpdate conn.prepareStatement(sqlUpdateFlight); PreparedStatement psInsert conn.prepareStatement(sqlInsertOrder)) { // 1. 尝试扣减余票AND available_seats 0 是乐观锁 psUpdate.setLong(1, flight.getId()); int updatedRows psUpdate.executeUpdate(); if (updatedRows 0) { throw new BusinessException(余票不足无法预订); } // 2. 插入订单 psInsert.setLong(1, user.getId()); psInsert.setLong(2, flight.getId()); psInsert.setString(3, seatNo); psInsert.setBigDecimal(4, flight.getPrice()); psInsert.executeUpdate(); conn.commit(); // 全部成功提交事务 return true; } catch (SQLException e) { conn.rollback(); // 任一环节失败回滚所有 throw e; } } catch (SQLException e) { logger.error(订单提交失败, e); throw new SystemException(系统繁忙请稍后再试, e); } } }这里有两个精妙设计-乐观锁机制UPDATE ... WHERE id ? AND available_seats 0。这行SQL执行后executeUpdate()返回影响行数。如果为0说明在你查询余票后、执行更新前已有其他人抢走了最后一张票。此时直接抛业务异常而不是让程序崩溃。-try-with-resources自动释放PreparedStatement和Connection都在try括号里声明无论成功还是异常资源都会被自动关闭杜绝连接泄露。实操心得我让学生在压力测试时故意开10个线程同时抢同一航班的最后一张票结果9个线程收到“余票不足”提示1个成功——这证明乐观锁生效了。而如果用悲观锁SELECT … FOR UPDATE在高并发下容易造成锁等待甚至死锁对课设来说过于复杂。4. 实操过程与核心环节实现从零开始跑通全流程4.1 环境准备三步到位拒绝“我的电脑上能跑”很多课设失败不是代码问题而是环境没配对。这个系统做了最小化依赖只需三步第一步安装MySQL 5.7推荐8.0- 下载地址dev.mysql.com/downloads/mysql/- 安装时务必记住root密码默认是空但强烈建议设密码- 启动服务Windows下是“服务”里启动MySQL80Mac用brew services start mysql第二步创建数据库并导入脚本# 登录MySQL mysql -u root -p # 创建数据库注意字符集避免中文乱码 CREATE DATABASE planesystem CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; # 退出执行建表脚本按顺序 mysql -u root -p planesystem sql/flight.sql mysql -u root -p planesystem sql/user.sql mysql -u root -p planesystem sql/orders.sql # 导入测试数据可选但强烈推荐 mysql -u root -p planesystem sql/test_data.sql为什么强调utf8mb4因为MySQL的utf8其实是阉割版不支持emoji和部分生僻汉字。utf8mb4才是真正的UTF-8。我在测试时遇到过学生用utf8建库结果插入“上海虹桥”变成“??虹桥”折腾半天才发现是字符集问题。第三步配置数据库连接打开src/main/java/config/DatabaseConfig.java修改以下三行config.setJdbcUrl(jdbc:mysql://localhost:3306/planesystem?useSSLfalseserverTimezoneUTC); config.setUsername(root); // 改成你的MySQL用户名 config.setPassword(123456); // 改成你的MySQL密码提示如果MySQL端口不是默认3306比如你用Docker映射到了3307记得改URL里的端口号。useSSLfalse是因为本地开发一般不用SSL省去证书配置麻烦serverTimezoneUTC解决时区问题避免Java里的时间和MySQL里的时间差8小时。4.2 项目导入与运行IntelliJ IDEA和Eclipse双路径IntelliJ IDEA路径1. File → Open → 选择解压后的根目录如1G2OlNbG759MxjHHKEUC-master-fe0a7ec...2. IDEA会自动识别为Maven项目看有没有pom.xml。如果没有右键pom.xml → “Add as Maven Project”3. 等待Maven下载依赖hikari-cp、mysql-connector-java、bcrypt等4. 找到src/main/java/app/PlaneSystemApp.java右键 → “Run PlaneSystemApp.main()”Eclipse路径1. File → Import → Maven → Existing Maven Projects2. 选择根目录勾选项目Finish3. 如果报“JRE System Library not found”右键项目 → Properties → Java Build Path → Libraries → Add Library → JRE System Library → Workspace default JRE4. 运行PlaneSystemApp.java常见问题排查- 报错java.lang.ClassNotFoundException: com.mysql.cj.jdbc.Driver说明mysql-connector-java没加载。检查Maven依赖是否下载成功或手动把mysql-connector-java-8.0.33.jar拖进项目的lib文件夹右键 → Build Path → Add to Build Path。- 启动后界面空白或报空指针检查DatabaseConfig.java里的数据库URL、用户名、密码是否正确以及MySQL服务是否真的在运行命令行执行mysql -u root -p能登录吗。4.3 核心流程演示以“学生订一张北京飞上海的票”为例我们来走一遍最典型的用户旅程看看代码如何串联Step 1用户注册与登录- 启动程序出现登录窗口3-1.png- 点“注册”填用户名student1、密码123456、邮箱stuschool.edu、手机号13900139000- 注册成功后用刚注册的账号登录- 登录成功进入主界面4-1.png顶部显示“欢迎student1”Step 2查询航班- 在主界面左侧选择出发地“北京”目的地“上海”点击“查询航班”- Controller层调用FlightService.searchFlights(北京, 上海)- Service层执行SQLSELECT * FROM flight WHERE departure_city ? AND arrival_city ? AND available_seats 0- 结果返回一个List 被封装成DefaultTableModel填充到JTable中4-2.pngStep 3选座与提交- 双击某条航班记录如CA123弹出选座窗口4-10.png- 界面动态生成30×6座位图已售座位灰色禁用- 点击一个绿色座位如“12A”按钮变蓝色底部显示“已选12A价格¥850.00”- 点击“确认预订”触发OrderService.submitOrder()- 如前所述事务执行扣余票、插订单、返回成功- 弹出提示框“预订成功订单号ORD20240520001”并自动跳转到“我的订单”页5-1.pngStep 4查看历史订单- 点击顶部菜单“订单管理” → “我的订单”- 加载OrderService.getUserOrders(userId)SQLSELECT o.*, f.flight_no, f.departure_city, f.arrival_city FROM orders o JOIN flight f ON o.flight_id f.id WHERE o.user_id ? ORDER BY o.create_time DESC- 表格显示订单号、航班号、出发到达城市、座位号、价格、状态5-2.png整个流程中没有任何一处是“假数据”。所有航班信息来自flight表所有用户来自user表所有订单来自orders表。你可以用MySQL Workbench直接连上去实时看到数据变化——这才是课设该有的样子。5. 常见问题与排查技巧实录那些踩过的坑我都替你趟平了5.1 图形界面相关问题速查表问题现象可能原因排查与解决启动后窗口一片空白或只有标题栏1. 主线程阻塞在耗时操作如数据库查询2. EDT线程被占用检查PlaneSystemApp.java的main方法确保SwingUtilities.invokeLater()包裹了UI创建代码所有数据库操作必须放在SwingWorker或新线程中绝不能在ActionListener里直接调用service.method()中文显示为方块1. MySQL数据库/表字符集不是utf8mb42. JDBC URL缺少characterEncodingutf8mb4执行SHOW CREATE DATABASE planesystem;确认字符集在DatabaseConfig.java的JDBC URL末尾加上characterEncodingutf8mb4座位图按钮大小不一致或文字被截断1. 没设置setPreferredSize()2. 使用了FlowLayout等不固定尺寸的布局在创建JButton后立即调用btn.setPreferredSize(new Dimension(40, 30))确保父容器使用GridLayout或GridBagLayout点击按钮无反应1.addActionListener()没写或写在了错误对象上2. 按钮被其他组件遮挡如JLayeredPane使用不当在按钮创建后立刻写btn.addActionListener(this)或btn.addActionListener(e - {...})用IDE的调试器打断点确认监听器是否被注册5.2 数据库连接与操作问题问题现象可能原因排查与解决Communications link failure1. MySQL服务未启动2. JDBC URL端口号错误3. 防火墙阻止了3306端口Windows下打开“服务”确认MySQL服务状态检查URL中localhost:3306的端口是否匹配临时关闭防火墙测试Access denied for user rootlocalhost1. 用户名或密码错误2. MySQL 8.0 默认认证插件是caching_sha2_password老驱动不兼容重置root密码ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 123456; FLUSH PRIVILEGES;查询返回空结果但数据库里明明有数据1. SQL语句WHERE条件字段名拼写错误如depart_city写成departure_city2. 字符串比较区分大小写MySQL默认不区分在MySQL命令行直接执行相同SQL看是否返回结果检查Java代码中ps.setString(1, 北京)的参数索引是否与SQL中?位置一致订单提交后余票没减少1.UPDATE语句的WHERE条件太宽泛没限定available_seats 02. 事务没提交忘记conn.commit()检查OrderService.submitOrder()方法确认conn.setAutoCommit(false)后是否有对应的conn.commit()在UPDATE语句后加System.out.println(影响行数 updatedRows);打印调试5.3 课程设计文档PlaneSystem Report.doc撰写要点这份报告不是代码的复读机而是思考过程的载体。我给学生的硬性要求是需求分析章节必须包含至少3个真实用户故事User Story。例如“作为学生用户我希望在选座界面看到已售座位被标记为灰色以便快速避开无效选项节省选座时间。” 而不是空泛的“系统应具备良好的用户体验”。数据库设计章节ER图必须手绘用draw.io或PPT画不能截图。重点标注1主键PK、外键FK连线2基数1对多、多对多3每个实体的属性注明哪些是必填NOT NULL、哪些有约束如price 0。我见过太多报告里ER图字段名和SQL脚本对不上这就是没认真画图的后果。系统截图章节不是简单贴图每张图下面必须有操作步骤说明和预期结果描述。例如“图4-10选座界面。操作双击航班列表中CA123航班预期弹出30×6座位图第12排A座为绿色可选第10排B座为灰色已售”。总结与展望章节禁止写“通过本次课设我学到了很多知识”这种废话。必须写具体改进点比如“当前选座仅支持单座下一步可扩展为支持连座如选12A自动锁定12B当前数据库未做索引优化对flight表的departure_city和arrival_city字段添加联合索引可提升查询速度。”最后分享一个小技巧报告里的所有截图必须用系统实际运行时的界面而不是PS合成的。我曾发现一份报告里登录成功的提示框文字是“Welcome, admin!”但系统代码里写的是“欢迎admin”。这种细节老师一眼就能看出真假。6. 课设之外这个系统还能怎么玩这个航班订票系统表面是个课设但它的骨架足够结实可以支撑起更多有意思的东西。我自己就带着学生做过几个延伸实验第一个延伸接入真实航班API进阶把FlightService.searchFlights()的实现从查本地MySQL改成调用民航局公开API如https://www.caac.gov.cn/XX/XX/flight_api。你需要1用HttpClient发送HTTP GET请求2解析返回的JSON用Jackson或Gson3把API数据临时缓存到本地Map避免频繁调用。这一步就把系统从“模拟”升级到了“半真实”也让学生第一次接触HTTP协议和JSON数据交换。第二个延伸增加管理员后台实战现有系统只有用户端。你可以新增一个AdminLoginFrame管理员登录后进入后台5-10.png功能包括1手动添加/删除航班增删flight表记录2导出所有订单为Excel用Apache POI3查看实时余票热力图用JFreeChart画柱状图。这会倒逼你理解Swing的权限控制——如何让普通用户看不到“后台管理”菜单答案是登录成功后根据user.getRole()返回值动态决定是否menuBar.add(adminMenu)。第三个延伸打包成跨平台安装包交付课设交源码不够酷交一个双击就能运行的安装包才叫专业。用jpackageJDK 14内置命令jpackage --input target/ --name PlaneSystem --main-jar planesystem.jar --main-class app.PlaneSystemApp --type exe生成的PlaneSystem.exe连JRE都不用装Windows用户双击即用。Mac和Linux也有对应参数。这会让答辩时老师眼前一亮“哦你们还做了安装包”说到底这个系统的价值不在于它有多炫而在于它把Java桌面开发的“毛细血管”都打通了从Swing的事件循环到JDBC的连接池再到MySQL的事务和约束最后落到一份字字对应的课程设计报告。它不教你“如何成为架构师”但它会坚定地告诉你“你看一个真实可用的系统就是这么一行行代码、一个个决策、一次次调试垒起来的。” 当你双击PlaneSystemApp.java看到那个朴素的登录框弹出来时你就已经站在了真实软件世界的门口。推开门里面全是活生生的、带着温度的代码。本文还有配套的精品资源点击获取简介直接运行的Java桌面订票程序用Swing搭建全部UI界面支持用户注册登录、实时航班查询、余票动态显示、可视化选座、订单提交和历史订单管理。后端基于MySQL提供flight.sql、user.sql、orders.sql三张建表脚本含初始化测试数据开箱即用。配套PlaneSystem Report.doc课程设计报告涵盖需求分析、系统架构、模块说明、数据库设计及运行截图3-1.png至5-12.png共20余张覆盖登录页、航班列表、订票流程、后台管理等关键环节。源码结构清晰主类入口明确所有Swing事件响应、JDBC连接、CRUD操作均经本地环境验证适合Java初学者理解GUI开发流程、数据库交互逻辑与基础MVC分层实践满足高校大三上学期实训或课程设计交付标准。本文还有配套的精品资源点击获取