Java Swing+MySQL成绩管理系统实战:从环境配置到答辩通关
2026/9/8 4:31:36 网站建设 项目流程

简介:面向Java初学者的Swing成绩管理系统源码项目,完整覆盖学生信息维护、成绩录入查询、统计分析、数据持久化、权限控制与事件处理等桌面应用常见场景,可帮助学习者理解Java GUI程序从界面搭建、业务逻辑编写到数据库连接的完整过程。压缩包共54个文件,主体为5个Java源文件与29个class字节码,另包含MySQL 8.0.16驱动JAR、工程配置文件及说明文档,整体大小仅2.12MB,结构紧凑,便于按需查看源码和直接运行。当前已有79人学习浏览。代码按角色设计了选择入口、学生端与教师端界面,通过JTable展示数据、按钮触发操作,并配合异常处理和输入校验保证程序稳定;从各界面类与模型类的协作中,可以学到Swing常见布局、事件监听器用法以及基于JDBC的数据增删改查思路。项目还体现了用户角色区分与数据保存策略,对于完成课程设计、毕业设计或入门Java桌面开发都有直接参考价值,尤其适合需要实现可视化成绩管理原型并理解前后端交互流程的学习者。 如果你的课程设计任务清单里躺着“Java swing成绩管理系统”这一项,别急着叹气。这个题目看起来普通,但每年都有大量学生在这上面翻车:环境变量没配好、JDBC驱动加载失败、JTable数据不刷新、MySQL编码乱码、答辩时被问到底层原理直接卡壳。我见过太多人不是因为不会写代码挂掉,而是因为项目看着“太像抄的”而答不上来。这篇文章不会只给你一份能跑的代码,而是把你从选题、建库、写界面,到排错、答辩、扩展的完整链路梳理清楚,让你做出来的东西既能用、又能讲、还扛得住追问。

先说清楚这篇博文能解决什么问题:我会带你拆解一个基于Java Swing + MySQL + JDBC的典型成绩管理系统,覆盖核心模块设计、关键代码实现、高频运行报错的排查思路,以及如何从“能跑”升级到“拿得出手”。适合正在做课程设计、毕业设计,或者想用Swing快速练手Java的同学参考。

1. 从选题到选型:为什么Swing依然是课程设计的首选

很多人看到Swing的第一反应是“老古董”。确实,JavaFX、Web前端、Spring Boot满天飞的今天,Swing看起来毫无现代感。但你要明白一个事实:课程设计的评分标准从来不是“用了最新的技术”,而是“你能不能说清楚系统是如何工作的”。

Swing最大的优势在于三点。第一,它是JDK自带的GUI工具包,不需要额外配置复杂的前端构建链,装好JDK打开IDE就能写。第二,它的MVC思想非常直白:JTable对应视图,TableModel对应数据模型,控制器就是你的ActionListener,这种结构拿来解释“分层设计”刚刚好。第三,它的控件体系足够完整,表格、树、选项卡、对话框全都有,做一个管理类系统绰绰有余。

如果你的核心诉求是“快速做出一个功能完整、能演示、能答辩的管理系统”,Swing的性价比远高于你去现学Vue和Spring Boot。而且Swing项目跑起来轻快,不像Spring Boot启动就要占几百MB内存,在机房老机器上也能流畅演示。

选型层面,我建议技术栈固定为:Swing负责界面,JDBC负责数据库访问,MySQL负责存储,再叠加一个分层结构——视图层(View)、业务层(Service)、数据访问层(DAO)。不要图省事把SQL直接全写在按钮监听器里,等你答辩被问到“怎么改造成三层架构”时,代码横向一比就露馅了。

2. 数据库先行:三张业务表加一张用户表的建模思路

写Swing和写网页不一样,你没法像前端那样先搭个页面再用接口连数据。更顺的路线是先把数据库建好,再把界面字段与表字段一一对应,最后才写监听逻辑。数据库是成绩管理系统的骨架,骨架歪了,后面所有代码都在打补丁。

常见的成绩管理系统最少需要四张表:用户表(user)、学生表(student)、课程表(course)、成绩表(score)。下面是我认为最合理的字段设计,直接照着建就行:

-- 用户表 CREATE TABLE user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) UNIQUE NOT NULL, password VARCHAR(64) NOT NULL, role VARCHAR(20) DEFAULT 'student' ); -- 学生表 CREATE TABLE student ( id INT PRIMARY KEY AUTO_INCREMENT, stu_no VARCHAR(20) UNIQUE NOT NULL, name VARCHAR(50) NOT NULL, gender VARCHAR(10), class_name VARCHAR(50), phone VARCHAR(20) ); -- 课程表 CREATE TABLE course ( id INT PRIMARY KEY AUTO_INCREMENT, course_no VARCHAR(20) UNIQUE NOT NULL, course_name VARCHAR(50) NOT NULL, credit DOUBLE ); -- 成绩表 CREATE TABLE score ( id INT PRIMARY KEY AUTO_INCREMENT, stu_id INT NOT NULL, course_id INT NOT NULL, score DOUBLE, FOREIGN KEY (stu_id) REFERENCES student(id), FOREIGN KEY (course_id) REFERENCES course(id) );

这里有几个建模时容易忽略的点,逐个说。

第一,为了保障数据一致性,成绩不单独存学号和课程名,而是存外键stu_id和course_id,查询时再关联学生表和课程表。这不是为了炫技,而是防止你删掉一个学生后,成绩表里留着一堆幽灵数据。

第二,密码字段长度我特意设置成64位,因为要用MD5加密存储。直接存明文是课程设计里最严重的扣分点,答辩时,老师看到密码表里是一串md5值,首先能感受到你具备安全常识。

第三,student和course的编号字段都加了UNIQUE约束,避免重复录入。成绩表里最好再加一个联合唯一索引UNIQUE(stu_id, course_id),防止同一个学生同一门课录入两次成绩。这个小细节也很加分,因为说明你考虑过重复数据问题。

建完表之后,打开你的数据库图形化工具(Navicat、DataGrip、MySQL Workbench都行),生成一份ER关系图,并截图保存。后面无论是写课程设计文档,还是答辩做PPT,这张图都是核心素材。

3. 功能模块落地:登录、CRUD与模糊查询的实现顺序

3.1 登录模块:考虑角色判断和密码加密

页面设计上,不要一上来就搞花哨的动画,Swing做动画既费劲又不讨好。你可以用JFrame作为主窗口,内部嵌套JPanel,用GridBagLayout把用户名、密码、登录按钮居中排列。登录按钮的事件处理逻辑,按下面这个思路来写:

LoginService.login(username, password);

Service层内部做的事情,主要包括以下几步。

第一步,传入用户名和密码,密码用MessageDigest做MD5加密,注意加盐:md5(password + username),这样即使两张表拿到相同密码,明文字段也不会一样。

第二步,执行SQL查询,用PreparedStatement而不是Statement,减少拼接SQL带来的注入问题:

SELECT * FROM user WHERE username = ? AND password = ?

第三步,拿到查询结果后,根据role字段的值决定跳转到哪个主界面。比如admin进入管理员界面,student进入学生查询界面。这比做一个按钮管全部要专业得多。

实际写代码时,新手的常见错误是点击登录按钮后没有做空值校验,用户名或密码为空时SQL还能继续跑,然后报一堆看不懂的异常。所以在调用登录逻辑前,先判断输入框内容是否为空,为空就弹JOptionPane提示信息,并直接return。

3.2 学生管理与课程管理:标准CRUD的样板代码

学生管理和课程管理的界面结构可以完全一致,左右分栏布局:左边是以JTable为核心的表格展示区,右边是表单输入区(学号、姓名、性别、班级、电话等)。按钮设计为四个:新增、修改、删除、清空。

按钮的事件处理要遵循同样的套路:

  • 新增:从输入框取数据,校验非空,调用DAO层的addStudent(Student s),执行成功后在表格模型中插入一条新行,并清空输入框。
  • 修改:先获取表格当前选中行的ID,然后判断ID是否为-1,为-1则提示先选择一行,否则更新数据库后刷新表格。
  • 删除:执行前二次确认,点击“是”才真正调用删除语句,这是一个很有必要的防误删设计。
  • 清空:把输入框全部置为空字符串,但不要触碰表格数据。

写DAO层时,注意打开连接用try-with-resources,让Connection、PreparedStatement、ResultSet自动关闭:

try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { // 设置参数并执行 }

如果你还在手动写Connection c = null然后最后在finally里一个个close,这部分代码冗余又容易出错。换成try-with-resources后,不但代码干净,还能在答辩时显得更熟悉Java 7以上的语法特性。

3.3 成绩管理:组合查询带来的SQL分页难度

成绩模块是系统的核心,也是课程设计的重点加分项。界面布局建议用JTabbedPane分成两个选项卡:一个叫“成绩录入”,一个叫“成绩查询”。

成绩录入界面的核心控件是JComboBox——课程下拉框里装课程名,学生信息下来框里装学生姓名,选好后填分数,点击“保存”。这里存在一个隐藏坑:JComboBox里显示的是文字,但你存到成绩表里需要的是对应的ID。所以常规做法是自定义一个Item对象,重写toString方法返回名字,同时保存id引用,选中后通过getSelectedItem拿到整个对象再取id。

成绩查询模块才是真正拉开档次的地方。不要做成简单全表查询,而是做组合查询:条件包括学生姓名(模糊)、课程名称(精确)、分数区间(min到max)。界面上放三个输入条件框和一个查询按钮,SQL动态拼接。这种按需查出的结果,比一次性全查出来再在内存里过滤要更有说服力。

写动态SQL时要小心SQL注入,可以用下面的模板:

StringBuilder sql = new StringBuilder( "SELECT s.stu_no, s.name AS student_name, c.course_name, sc.score " + "FROM score sc JOIN student s ON sc.stu_id = s.id " + "JOIN course c ON sc.course_id = c.id WHERE 1=1"); if (!name.isEmpty()) { sql.append(" AND s.name LIKE ?"); params.add("%" + name + "%"); } if (!courseName.isEmpty()) { sql.append(" AND c.course_name = ?"); params.add(courseName); }

这里的WHERE 1=1只是一个占位技巧,是为了后面拼接AND条件的方便,不是SQL语法错误。在答辩时如果被追问,你可以如实说这是为了让动态条件拼接更简洁。

4. JTable动态刷新与窗口跳转:最容易卡住的三个细节

4.1 数据刷新不是重新设置TableModel,而是通知模型刷新

很多人在修改或删除数据后,为了让界面上显示最新结果,会执行类似于table.setModel(buildTableModel(newResultSet))这样的代码。这样做在功能上是没错的,但它会丢失列的宽度设置、选中状态和表头渲染样式。

正确的思路是:保留TableModel对象,通过更新其中的数据集合,然后调用fireTableDataChanged()方法通知表格重绘:

DefaultTableModel model = new DefaultTableModel(data, columnNames); table.setModel(model); // 数据变更后 model.setRowCount(0); // 清空旧数据 for (Object[] row : newDataList) { model.addRow(row); } model.fireTableDataChanged();

上面的写法是对的。因为DefaultTableModel基于Vector存储行数据,只要你在调用了setRowCount(0)和addRow后,界面自然会被刷新。真正的问题在于:如果你新增数据后只更新数据库而忘了更新TableModel,就必须刷新整个表格。比较实用的模式是,把加载表格数据的方法抽成refreshTable(),每次增删改成功后在回调里调用一次,从而保证数据库和界面的一致性。

4.2 子窗口修改数据后,主窗口怎么同步更新

成绩管理系统里,你很可能会有“在子窗口中编辑成绩,点确定后回到主窗口需要刷新数据”的需求。实现方式很简单:子窗口提供一个私有的boolean变量isUpdated,点击“确定”按钮时把它设为true。主窗口需要把子窗口设为模态对话框,例如setModal(true),然后再执行setVisible(true)后面的逻辑,此时可以判断isUpdated的值,为true就刷新主窗口表格。

这样做的原理是JDialog模态框在关闭时,会阻塞主窗口的代码流,所以setVisible(true)后面的代码会在对话框关闭后继续执行。普通课程设计,很多人遇到这个问题会通过反复重新打开主界面来缓解,效果很糟。如果能在答辩前把这个细节改了,演示体验会好很多。

4.3 表格列宽设计:直接决定演示体验

JTable默认列宽分配往往是平均的,但成绩管理界面里,“学号”和“学生姓名”显然不需要等宽。推荐在封装表格时手动指定列宽度和拖拽行为:

TableColumnModel colModel = table.getColumnModel(); colModel.getColumn(0).setPreferredWidth(100); // 学号 colModel.getColumn(1).setPreferredWidth(120); // 姓名 colModel.getColumn(2).setPreferredWidth(150); // 课程名 colModel.getColumn(3).setPreferredWidth(80); // 分数 table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);

同时设置表格可选行单选,将selectionMode设为ListSelectionModel.SINGLE_SELECTION,避免用户多选后点击删除导致逻辑混乱。单元格不可编辑重写isCellEditable返回false,否则用户双击表格单元格,程序会尝试直接编辑底层数据。

5. 高频运行报错排查:从环境变量到ClassNotFound的完整链路

Swing项目可以说是“写起来容易,跑起来报错一堆”。下面这几个报错出现的频率极高,我把完整的排查思路写出来。

5.1 java.lang.NoClassDefFoundError

你搜热词的时候会看到一条:uncaught exception java.lang.noclassdeffounderror: java/applet/applet in thread。这类错误发生的原因很一致:你编译用的JDK版本比运行时环境高,或者是用了太高版本的JDK启动一个需要applet API的旧项目。Java 11已经移除了java.applet.Applet类,所以如果项目里存在旧依赖引用,就会在运行时报NoClassDefFoundError。

排查思路分三步。第一步,打开终端输入java -version和javac -version,确认两次输出的版本是否一致。第二步,检查IDEA的项目结构里Project SDK和项目语言级别(Project Structure)是否匹配,不匹配就统一改成一致版本;第三步,如果你用的是Maven或Gradle项目,检查pom.xml或build.gradle里依赖的编译级别,例如maven.compiler.source和target是否与JDK匹配。项目中不要直接用Applet类,它是Java早期的浏览器嵌入方案,跟桌面程序没有任何关系。

5.2 Lombok报错:You aren't using a compiler supported by Lombok

热词里有一条很典型的Lombok错误。Lombok在较新版本的JDK上,尤其是非官方版本或早期预览版上,会报“you aren't using a compiler supported by lombok”。虽然成绩管理系统里完全可以不用Lombok,但如果你从GitHub上拉来的参考代码里用了@Data、@Getter等注解,而你的IDE没有安装Lombok插件,或者JDK版本过新,就会触发这个报错。

最简单的解决办法:不使用Lombok,把实体类的getter/setter手写出来,也就几十行代码,但排除了一个最大的环境隐患。如果你坚持用Lombok,先装插件,再把依赖版本升到最新,同时把JDK降到Lombok官方支持的版本范围,比如JDK 8或JDK 11。

5.3 mysql驱动加载不到:Class.forName抛ClassNotFoundException

凡是Swing连MySQL的项目,新手最常见的问题就是明明在项目里引入了mysql-connector-java.jar,运行时却仍然报ClassNotFoundException: com.mysql.cj.jdbc.Driver。

原因通常是三种。第一,jar包没有真正进入运行时的classpath,在IDEA里需要右键jar包选择Add as Library;第二,驱动类名写错,5.x版本用com.mysql.jdbc.Driver,8.x版本用com.mysql.cj.jdbc.Driver;第三,你用命令行运行程序,但jar包没放在classpath指定的目录下。

建议统一使用8.x版本驱动和对应的Driver类,连接URL再加两个参数:

jdbc:mysql://localhost:3306/score_db?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8

不指定serverTimezone会报时区错误,不指定characterEncoding=utf8则插入中文会变成问号。这两个参数,可以说是Swing连接MySQL必踩的坑。环境变量配置的热词常年搜索量很高,稍有基础的人都从JDK和PATH配起,但真正让项目跑不起来的问题往往出在这里的classpath和驱动版本上。

5.4 Redis的increment()报错

如果你在课程设计中主动引入了Redis做缓存,这属于加分项。但注意,Redis存储的value必须是字符串或整数类型,使用increment()时报“not integer or out of range”,通常因为执行increment之前这个key对应的value是字符串“abc”或一个超大的数值。产生原因:业务代码里先使用set方法写入字符串,再对它调用increment操作。

解决办法是严格规划key的类型,所有做计数的key从初始化就用incr操作创建,不要在计数逻辑里混用set存普通字符串。Redis在单纯成绩管理系统中不是必需品,如果你用Redis,建议用StringRedisTemplate来处理计数相关的数据。

6. 扩展与答辩:从照样跑通到真正拿得出手

成绩管理系统这类题目,老师看过的版本比大多数学生写过的代码都多。想拿高分,功夫要下在功能和讲解之外的几个地方。

6.1 三个有区分度的扩展点

最建议做的是这三个扩展:导入导出Excel、可视化统计报表、管理员审核学生信息。Excel导入导出用Apache POI,核心代码不外乎创建工作簿、创建行、创建单元格,但能实现真正可用的批量添加学生功能;统计报表不追求高深,用JFreeChart做一个班级各科平均分的柱状图,就能让界面档次提升不少;审核机制的意思是,学生账号注册后是待审核状态,管理员审批后才能登录,这体现的是状态流转的设计思路。

三个扩展选一个做就好,不要贪多。课程设计评审看的是完整度,一个模块做到闭环并说得清,胜过三个半成品堆在一起。

6.2 答辩现场怎么讲项目

答辩不夸张地说,会讲三分功。介绍项目的黄金结构是“信息系统三句话”:第一句,系统面向什么角色解决了什么问题;第二句,系统的核心业务流程是什么,从登录到录成绩到查成绩;第三句,自己最熟悉的一个技术难点。

如果你的项目里加入了动态SQL组合查询,就主动讲怎么拼接参数、怎么防止注入、为什么用PreparedStatement而不是Statement。如果你的项目实现了MD5加密存储密码,就讲为什么要加密,加密流程如何,为什么还要加盐。老师通常会沿着你抛出来的点去追问,所以不要主动提自己都没搞懂的部分。

6.3 课程设计说明书写法参考

如果你在写课程设计说明书,文档结构建议按这个顺序组织:

  • 封面(学校模板)
  • 摘要:概括系统功能、技术栈、核心难点,300字左右即可
  • 目录
  • 第一章 需求分析:明确角色(管理员、教师、学生)、功能列表、用例描述
  • 第二章 总体设计:架构分层图、功能模块图、数据库ER图
  • 第三章 详细设计:逐个模块说明界面设计和关键代码
  • 第四章 系统测试:设计几个测试用例,含正常数据和异常数据,表格形式展示
  • 第五章 总结:写收获与不足

文档中放界面的截图很重要,放代码不要整段贴,只贴关键方法,不要贴得贵在精,代码截全图反而显得没有整理。

最后再分享一个经验:项目做完之后,不要急着交,先在命令行里用一个全新的工作目录跑一遍整个流程,模拟老师的检查环境。如果在你自己的电脑上能正常运行,到老师那里换了一台机器却报错进不去,前面所有努力都白费了。把JDK、MySQL版本号记好,写进README,能省去现场一半的麻烦。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询