Java学生成绩管理系统实战:Swing+JDBC完整项目解析与二次开发指南

发布时间:2026/10/23 13:52:26
Java学生成绩管理系统实战:Swing+JDBC完整项目解析与二次开发指南 简介这是一套面向高校计算机相关专业学生与Java初学者的课程设计/期末作业级实战项目围绕学生成绩管理场景采用Java语言结合GUI界面与数据库实现可用于课程设计参考、Java综合练习或小型信息管理系统入门学习。压缩包共80个文件约5.74MB包含28个java源文件与42个class编译文件另有jar依赖包、docx实验报告、xls成绩数据表及jpg界面背景图等源码与文档配套齐全导入后即可运行调试。资源已积累9124人学习下载具备一定的参考基础。功能层面覆盖登录验证、学生与教师注册、成绩的添加修改查询删除、文件导入成绩、学生账号管理及密码修改等模块实验报告可辅助梳理系统设计与实现思路适合需要完整项目案例与文档参考的读者对照学习、二次改造与查漏补缺。1. 从一份能跑起来的 Java 成绩管理项目说起很多同学做课程设计时最头疼的不是写不出增删改查而是拼不出一个「能演示、能交差、还能讲清楚」的完整系统。这份 Java 学生成绩管理系统正好卡在这个痛点上它带 GUI 界面、带数据库脚本、还附了实验报告拿到手就能跑跑完还能照着改。我拆过不少同类项目大多数要么只有控制台、要么数据库是内存模拟的这份的完整度算比较实在。适合谁正在做 Java 课程设计的学生、想练手 Swing JDBC 组合的初学者以及需要一份可二次开发底座的开发者。下面我按「它是什么 → 怎么跑起来 → 核心代码怎么写的 → 怎么改」的顺序把这份源码拆开讲。2. 项目定位与技术栈Swing JDBC 的经典三层结构2.1 这套系统到底管了什么从功能上看这是一个典型的学生成绩管理场景围绕三张核心表展开学生信息、课程信息、成绩记录。系统要解决的事情很具体——录入学生、维护课程、登记成绩、按条件查询、统计分数。GUI 界面把这些操作包起来让非技术用户也能点着用。我一般会把这类系统的功能拆成四块来看学生管理学号、姓名、性别、班级、联系方式等基础信息的增删改查课程管理课程编号、课程名称、学分、任课教师成绩管理把学生和课程关联起来录入分数支持修改和删除查询统计按学号查个人成绩、按课程查成绩单、算平均分和及格率这四块对应到代码里就是四组 DAO 方法加四个界面面板。理解了这个映射关系后面看目录结构就不会迷路。2.2 技术选型为什么是 Swing 而不是 JavaFX项目用的是 Swing 做界面。这个选择在课程设计场景里非常合理Swing 是 JDK 自带的不需要额外引入 UI 依赖javac编译完直接java就能跑省掉了一堆环境配置的麻烦。JavaFX 从 JDK 11 之后被移出标准库要单独配模块对新手不友好。数据库这边用的是 JDBC 直连没有上 MyBatis 或 Hibernate。原因也简单——成绩管理的 SQL 就那么几条手写 JDBC 反而透明出了问题能一眼看到是哪条 SQL 挂了。常见做法是把连接信息抽到一个配置类里避免散落在各个 DAO 中。整体是「界面层 → 业务层 → 数据访问层 → 数据库」的四层走向虽然项目规模不大但分层是清晰的。这种结构对二次开发很友好想换数据库只动 DAO想改界面只动 UI 层互不牵连。2.3 目录结构长什么样拿到源码后先别急着编译花两分钟把目录扫一遍。典型的工程结构大致是这样StudentScoreSystem/ ├── src/ │ ├── com/score/ │ │ ├── ui/ # 界面层登录、主窗口、各管理面板 │ │ ├── dao/ # 数据访问层StudentDao、CourseDao、ScoreDao │ │ ├── model/ # 实体类Student、Course、Score │ │ ├── util/ # 工具类数据库连接、字符串校验 │ │ └── Main.java # 程序入口 ├── sql/ │ └── init.sql # 建库建表 初始数据 ├── lib/ │ └── mysql-connector-java.jar # JDBC 驱动 └── 实验报告.doc / .pdf # 设计文档ui包里通常一个面板一个类dao包里一个实体对应一个类model包就是纯 POJO。这种「一个职责一个类」的划分是判断一份课程设计代码是否值得二次开发的重要信号——如果所有逻辑都塞在一个Main.java里改起来会很痛苦。提示不同版本的源码包目录名可能略有差异比如dao写成db、model写成entity但分层逻辑是一致的按类名找就行。3. 跑起来环境准备、建库与启动顺序3.1 环境清单与版本建议在动手之前先把环境对齐。这份项目对版本不算挑剔但有几个点要注意组件建议版本说明JDK8 或 11Swing 在两者上都稳定8 兼容性最好MySQL5.7 或 8.08.0 注意驱动包版本要匹配JDBC 驱动mysql-connector-java 5.1.x / 8.0.x与 MySQL 版本对应IDEIntelliJ IDEA / Eclipse任一即可能配 jar 依赖JDK 和 MySQL 的安装这里不展开重点说容易翻车的地方MySQL 8.0 的驱动类名是com.mysql.cj.jdbc.Driver而 5.x 是com.mysql.jdbc.Driver连接串里还要带时区参数。这两点搞错程序启动就会报ClassNotFoundException或时区异常。3.2 建库建表先跑 init.sql数据库是这套系统的地基顺序不能乱。打开 MySQL 客户端先执行sql/init.sql-- 创建数据库字符集用 utf8mb4 避免中文乱码 CREATE DATABASE IF NOT EXISTS score_db DEFAULT CHARACTER SET utf8mb4; USE score_db; -- 学生表 CREATE TABLE student ( stu_id VARCHAR(20) PRIMARY KEY, -- 学号 name VARCHAR(50) NOT NULL, -- 姓名 gender CHAR(2), -- 性别 class_name VARCHAR(50), -- 班级 phone VARCHAR(20) -- 联系方式 ); -- 课程表 CREATE TABLE course ( course_id VARCHAR(20) PRIMARY KEY, -- 课程编号 course_name VARCHAR(50) NOT NULL, -- 课程名称 credit DECIMAL(3,1), -- 学分 teacher VARCHAR(50) -- 任课教师 ); -- 成绩表学生和课程多对多关联 CREATE TABLE score ( id INT AUTO_INCREMENT PRIMARY KEY, stu_id VARCHAR(20), course_id VARCHAR(20), score DECIMAL(5,1), -- 分数保留一位小数 FOREIGN KEY (stu_id) REFERENCES student(stu_id), FOREIGN KEY (course_id) REFERENCES course(course_id) );这段 SQL 的关键在于三张表的关系设计。score表是典型的关联表用stu_id和course_id两个外键把学生和课程连起来一条记录代表「某个学生某门课的成绩」。分数用DECIMAL(5,1)而不是INT是为了支持 89.5 这种带小数的成绩这个细节在统计平均分时很重要。外键约束建议保留。虽然有些教程为了省事会去掉外键但保留它能防止录入不存在的学号或课程号数据一致性有保障。如果导入初始数据时报外键错误先检查插入顺序——必须先插student和course再插score。3.3 配置数据库连接并启动建完库找到项目里的数据库连接工具类通常是util/DBUtil.java或类似名字把连接参数改成自己的public class DBUtil { // MySQL 8.0 用 com.mysql.cj.jdbc.Driver private static final String DRIVER com.mysql.cj.jdbc.Driver; // 连接串带上时区和字符集参数避免中文乱码和时区报错 private static final String URL jdbc:mysql://localhost:3306/score_db ?useUnicodetruecharacterEncodingutf8 serverTimezoneAsia/Shanghai; private static final String USER root; private static final String PASSWORD 你的密码; public static Connection getConnection() throws Exception { Class.forName(DRIVER); // 加载驱动 return DriverManager.getConnection(URL, USER, PASSWORD); } }参数说明useUnicodetruecharacterEncodingutf8保证中文姓名和课程名不乱码serverTimezone是 MySQL 8.0 的必填项不写会抛The server time zone value is unrecognized。USER和PASSWORD换成自己本机的账号密码。配置好之后把 JDBC 驱动 jar 加到项目的 classpath 里。IDEA 里的做法是File → Project Structure → Libraries → 选中 jar 包Eclipse 是右键项目Build Path → Add External Archives。这一步漏了编译能过但运行必挂。最后运行Main.java。如果一切正常会先弹出登录窗口常见默认账号是admin/admin具体看源码里的判断逻辑登录后进入主界面。启动顺序总结成一句话先建库 → 再配连接 → 加驱动 → 跑 Main顺序错了排查起来会很绕。4. 核心模块拆解从登录校验到成绩统计的完整链路4.1 实体类与 DAO一条 SQL 背后的封装先看实体类它决定了数据在内存里怎么表示。以Student为例public class Student { private String stuId; private String name; private String gender; private String className; private String phone; // 无参构造 全参构造 getter/setter 省略 public String getStuId() { return stuId; } public void setStuId(String stuId) { this.stuId stuId; } // ... 其余字段同理 }实体类没什么花活就是字段加 getter/setter。它的价值在于给 DAO 层提供一个类型安全的载体——查询结果从ResultSet映射成Student对象界面层拿到的就是对象而不是一堆字符串。DAO 层是真正干活的地方。以学生查询为例public class StudentDao { // 按学号查询单个学生 public Student findByStuId(String stuId) { String sql SELECT * FROM student WHERE stu_id ?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, stuId); // 参数化防 SQL 注入 try (ResultSet rs ps.executeQuery()) { if (rs.next()) { Student s new Student(); s.setStuId(rs.getString(stu_id)); s.setName(rs.getString(name)); s.setGender(rs.getString(gender)); s.setClassName(rs.getString(class_name)); s.setPhone(rs.getString(phone)); return s; } } } catch (Exception e) { e.printStackTrace(); // 实际项目应记录日志 } return null; } }这段代码有几个值得说的点。第一用PreparedStatement加?占位符而不是字符串拼接 SQL这是防注入的基本功成绩系统里学号是用户输入的拼接就是给自己埋雷。第二try-with-resources自动关闭Connection、PreparedStatement、ResultSet避免连接泄漏——这类系统连接数不多但泄漏积累久了照样把数据库连接池耗光。第三ResultSet的列名要和建表时的字段名对上class_name对应实体里的className映射错了就是空值。增删改的方法结构类似只是把executeQuery换成executeUpdate返回受影响行数判断成功与否。批量导入成绩时我一般会用addBatch()executeBatch()比循环单条插入快一个数量级。4.2 登录与权限校验一个容易被忽略的入口登录界面是系统的第一道门。很多课程设计在这里偷懒把账号密码硬编码在代码里比较// 登录按钮的点击事件 loginBtn.addActionListener(e - { String user userField.getText().trim(); String pwd new String(pwdField.getPassword()); if (user.isEmpty() || pwd.isEmpty()) { JOptionPane.showMessageDialog(this, 账号和密码不能为空); return; } // 常见做法查 user 表校验或与配置的默认账号比对 if (userDao.checkLogin(user, pwd)) { new MainFrame().setVisible(true); // 进入主界面 this.dispose(); // 关闭登录窗 } else { JOptionPane.showMessageDialog(this, 账号或密码错误); } });这里pwdField.getPassword()返回的是char[]而不是StringSwing 这么设计是出于安全考虑用完可以手动清零。校验逻辑走userDao.checkLogin内部同样是一条参数化查询。如果项目里没有独立的用户表而是硬编码了admin/123456二次开发时建议补一张user表把账号管理做成可维护的——这是从「能演示」到「能交付」的关键一步。登录成功后new MainFrame().setVisible(true)打开主窗口同时dispose()释放登录窗资源。这个细节处理得好不会出现登录窗残留在后台的情况。4.3 成绩录入与统计业务逻辑最集中的地方成绩模块是整套系统的核心涉及录入、修改、查询、统计四类操作。录入时最关键的是校验——分数必须在 0 到 100 之间学号和课程号必须存在public boolean addScore(String stuId, String courseId, double score) { // 业务校验分数范围 if (score 0 || score 100) { throw new IllegalArgumentException(分数必须在 0-100 之间); } // 校验学生和课程是否存在避免外键报错 if (studentDao.findByStuId(stuId) null) { throw new IllegalArgumentException(学号不存在 stuId); } String sql INSERT INTO score(stu_id, course_id, score) VALUES(?,?,?); try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, stuId); ps.setString(2, courseId); ps.setDouble(3, score); return ps.executeUpdate() 0; } catch (Exception e) { e.printStackTrace(); return false; } }校验放在 DAO 之前是为了给用户友好提示而不是让数据库抛一个看不懂的外键异常。这种「先校验再落库」的顺序是业务层该做的事。统计功能通常是一条聚合 SQL 搞定// 统计某门课程的平均分、最高分、及格率 public MapString, Object statByCourse(String courseId) { String sql SELECT AVG(score) avg_score, MAX(score) max_score, SUM(CASE WHEN score 60 THEN 1 ELSE 0 END) / COUNT(*) pass_rate FROM score WHERE course_id ?; // 执行查询并封装结果略 }SUM(CASE WHEN ...)是算及格率的常用写法比查出来在 Java 里循环统计更高效。AVG和MAX直接交给数据库算减少数据传输。这类聚合查询在实验报告里往往是加分项因为它体现了对 SQL 的掌握程度。4.4 界面层组织面板切换与事件绑定Swing 界面层的组织方式决定了代码好不好维护。常见做法是用JTabbedPane把学生、课程、成绩分成三个标签页每个标签页一个独立的JPanel子类public class MainFrame extends JFrame { public MainFrame() { setTitle(学生成绩管理系统); setSize(900, 600); setLocationRelativeTo(null); // 居中显示 setDefaultCloseOperation(EXIT_ON_CLOSE); JTabbedPane tabs new JTabbedPane(); tabs.addTab(学生管理, new StudentPanel()); tabs.addTab(课程管理, new CoursePanel()); tabs.addTab(成绩管理, new ScorePanel()); add(tabs); } }每个 Panel 内部自己管自己的表格、按钮和事件主窗口只负责组装。这种划分让「改一个模块不影响其他模块」成为可能。表格用JTable加DefaultTableModel查询结果填充到 model 里界面自动刷新。事件绑定统一用addActionListener的 lambda 写法比匿名内部类简洁。按钮点击后先做输入校验再调 DAO最后刷新表格——这个「校验 → 操作 → 刷新」的三段式是每个按钮事件的标准骨架照着套就行。5. 避坑与排查启动失败、乱码、外键报错的真实记录5.1 启动报 ClassNotFoundException: com.mysql.cj.jdbc.Driver现象程序一运行就抛驱动类找不到的异常堆栈指向Class.forName。原因两种情况——要么 JDBC 驱动 jar 没加到 classpath要么驱动类名和 MySQL 版本不匹配5.x 的包用了 8.0 的类名或反过来。解决先确认lib目录下的 jar 已加入项目依赖再核对版本MySQL 8.0 用com.mysql.cj.jdbc.Driver5.7 用com.mysql.jdbc.Driver。改完重新编译别只改代码不重新构建。5.2 中文姓名存进去变成问号现象录入「张三」数据库里显示???或者查询出来是乱码。原因字符集链路没对齐。建库时没指定utf8mb4或者连接串缺characterEncodingutf8任何一环掉链子都会乱码。解决三处一起查——建库语句带DEFAULT CHARACTER SET utf8mb4、连接串带useUnicodetruecharacterEncodingutf8、MySQL 服务端字符集配置正确。改完最好重建库再测旧数据已经乱码的救不回来。5.3 插入成绩时报外键约束失败现象Cannot add or update a child row: a foreign key constraint fails。原因往score表插数据时stu_id或course_id在对应的主表里不存在。常见于手工测试时随手编了个学号。解决插入前先确认学生和课程已存在代码里就是 4.3 节那段前置校验。如果是导入初始数据报错检查init.sql里的插入顺序必须先主表后关联表。5.4 界面能打开但表格一片空白现象登录进主界面表格区域什么都没有也不报错。原因查询结果没填进DefaultTableModel或者ResultSet的列名和getString的参数对不上取出来全是 null。解决在 DAO 里打印一下rs的行数确认 SQL 确实查到了数据再核对列名class_name别写成className。表格 model 的列名数组要和填充数据的顺序一致错位了数据就串行。5.5 关闭窗口后进程不退出现象点了关闭按钮窗口没了但任务管理器里 Java 进程还在。原因JFrame的默认关闭行为是HIDE_ON_CLOSE只隐藏不退出或者有非守护线程比如数据库连接池还在跑。解决主窗口设setDefaultCloseOperation(EXIT_ON_CLOSE)确保关闭即退出。如果用了连接池在windowClosing事件里手动关闭池。这类问题不影响功能但演示时进程残留会显得不专业。6. 二次开发把课程设计改成能交付的小系统6.1 从硬编码到可配置先动连接和账号拿到源码后我建议第一刀砍向硬编码。数据库连接信息、默认登录账号这些散落在代码里的常量全部抽到config.propertiesdb.urljdbc:mysql://localhost:3306/score_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai db.userroot db.password123456然后用Properties类加载DBUtil只读配置不写死。这样换台机器部署改一个文件就行不用重新编译。账号体系同理补一张user表密码存 MD5 或 BCrypt 哈希登录走查表校验——这一步做完系统就从「演示品」往「能交付」迈了一大截。6.2 加功能的标准姿势以「按班级统计」为例二次开发最常见的需求是加统计维度。以「按班级算平均分」为例改动路径很清晰ScoreDao加一个方法SQL 用JOIN把score和student连起来按class_name分组ScorePanel加一个按钮和对应的结果表格按钮事件里调新方法把结果填进表格public ListObject[] avgByClass() { String sql SELECT s.class_name, AVG(sc.score) avg_score FROM score sc JOIN student s ON sc.stu_id s.stu_id GROUP BY s.class_name ORDER BY avg_score DESC; ListObject[] result new ArrayList(); // 执行查询每行封装成 Object[]{班级, 平均分}略 return result; }这个套路可以复制到任何新统计需求上DAO 加查询 → 界面加控件 → 事件里串联。三层各改一处互不干扰这就是分层带来的好处。改完记得在实验报告里补一段设计说明答辩时能讲清楚「为什么这么加」。6.3 验证改动是否真的生效改完代码别急着说「应该没问题了」按这个顺序验一遍先单独跑 DAO 方法写个main打印结果确认 SQL 对再启动界面点按钮确认数据填对了最后造几条边界数据——分数 0、分数 100、不存在的学号——看校验有没有拦住。我见过太多「界面能点但数据是错的」的情况根子都在跳过了 DAO 单测这一步。注意改数据库结构后记得同步更新init.sql和实验报告里的表设计章节否则下次重建库又对不上。6.4 一个我踩过的坑早些年我改这类项目习惯直接在MainFrame里加逻辑图省事。结果功能越加越多一个类膨胀到上千行后来想换个表格样式都不敢动牵一发动全身。从那以后我每次动源码前都强制先看一眼分层边界——该进 DAO 的 SQL 绝不写在界面里该在业务层做的校验绝不下放到按钮事件。这个习惯让后续每次改造都省心不少。希望这份拆解能帮你少走点弯路把这份成绩管理系统真正跑起来、改下去。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询