简介这是一套面向高校计算机相关专业学生与Java初学者的学生成绩管理系统完整项目可作为课程设计、期末大作业或Java GUI与数据库综合练习的参考方案。系统基于Java语言开发采用图形界面交互覆盖登录验证、学生与教师注册、成绩的增删改查、文件导入成绩、学生账号管理及密码修改等核心功能并配套数据库驱动与实验报告便于直接运行与二次开发。压缩包共80个文件约5.74MB其中28个java源码与42个class文件构成主体程序另含2个jar依赖、2个docx实验报告、xls成绩数据表及图片等资源源码与文档齐备导入后即可使用。目前已有9123人学习下载适合需要快速获取可运行项目、理解成绩管理业务逻辑与数据库连接方式的读者参考借鉴。1. 从一份“交作业级”源码说起这套 Java 学生成绩管理系统到底能跑出什么很多同学做课程设计时最头疼的不是写不出增删改查而是“功能凑齐了、报告写不出来、数据库还连不上”。这套 Java 学生成绩管理系统含数据库以及实验报告就是冲着这个场景来的它把学生信息、课程成绩、班级统计、用户登录这几块做成了一个能直接运行的桌面程序配套的 SQL 建表脚本和实验报告文档也一并打包。技术栈上走的是 Java Swing JDBC MySQL 这条经典路线没有上 Spring 那套重框架对刚学完 Java 基础、正在啃数据库连接的人来说阅读和改造的门槛都低。它适合两类人一类是要交课程设计、需要一份结构完整可演示的源码另一类是想拿一个真实小项目练手 JDBC 和 GUI 事件处理的开发者。下面我按“能干什么 → 怎么跑 → 怎么改”的顺序把这份资源拆开讲。2. 技术选型与目录结构为什么是 Swing JDBC 而不是别的2.1 这套组合的取舍逻辑先说明一点这个项目没有用 JavaFX也没有用 Maven 做依赖管理而是最朴素的 Swing JDBC 手动导入 mysql-connector 的 jar 包。这不是偷懒而是课程设计场景下的合理选择Swing 是 JDK 自带的不需要额外配环境JDBC 直连数据库代码里每一步 SQL 都看得见不像 MyBatis 那样把 SQL 藏进 XML 或注解里对理解“Java 怎么跟数据库说话”这件事更直观。常见做法是把数据库连接参数写在一个单独的配置类里而不是散落在各个 DAO 中。这个项目大概率也是这么处理的因为一旦要换数据库地址或密码只改一处比全局搜索要省事得多。下面是我一般会建议的目录组织方式和这类项目的实际结构基本吻合StudentScoreSystem/ ├── src/ │ ├── com/scs/ │ │ ├── entity/ # 实体类Student、Course、Score、User │ │ ├── dao/ # 数据访问层StudentDao、ScoreDao 等 │ │ ├── util/ # 工具类DBUtil 数据库连接 │ │ ├── view/ # 界面层LoginFrame、MainFrame、各功能面板 │ │ └── Main.java # 程序入口 ├── lib/ │ └── mysql-connector-java-x.x.x.jar ├── sql/ │ └── init.sql # 建库建表 初始数据 └── doc/ └── 实验报告.docx这个分层不复杂但边界是清楚的entity 只放字段和 getter/setterdao 只负责 SQL 执行view 只负责界面渲染和事件绑定。新手最容易犯的错是把 SQL 直接写在按钮的点击事件里导致界面和数据库逻辑缠在一起后面想改一个字段名要翻好几个文件。这份源码如果按上面的结构组织改造起来会舒服很多。2.2 数据库表设计三张核心表怎么关联成绩管理系统的数据模型绕不开三个实体学生、课程、成绩。成绩表是典型的中间表用学生 ID 和课程 ID 做联合关联。建表脚本一般长这样-- 学生表 CREATE TABLE student ( id VARCHAR(20) PRIMARY KEY, -- 学号用字符串避免前导零丢失 name VARCHAR(50) NOT NULL, gender VARCHAR(4), class_name VARCHAR(50), -- 班级 major VARCHAR(50) -- 专业 ); -- 课程表 CREATE TABLE course ( id VARCHAR(20) PRIMARY KEY, -- 课程编号 name VARCHAR(50) NOT NULL, credit DECIMAL(3,1) -- 学分保留一位小数 ); -- 成绩表 CREATE TABLE score ( id INT AUTO_INCREMENT PRIMARY KEY, student_id VARCHAR(20), course_id VARCHAR(20), score DECIMAL(5,1), -- 支持 100.0 这种一位小数 FOREIGN KEY (student_id) REFERENCES student(id), FOREIGN KEY (course_id) REFERENCES course(id) );这里有几个参数值得注意。学号用 VARCHAR 而不是 INT是因为很多学校的学号带前导零或者字母用整型会丢格式。成绩用 DECIMAL(5,1) 而不是 FLOAT是为了避免浮点误差导致 89.9 显示成 89.89999 这种尴尬。外键约束加上之后删学生之前必须先处理他的成绩记录否则数据库会直接拒绝这其实是一种保护。2.3 数据库连接工具类一个容易被忽视的关键点DBUtil 这个类看起来简单但它是整个项目能不能跑起来的命门。典型实现如下public class DBUtil { private static final String URL jdbc:mysql://localhost:3306/student_score?useSSLfalseserverTimezoneUTCcharacterEncodingutf8; private static final String USER root; private static final String PASSWORD 123456; static { try { Class.forName(com.mysql.cj.jdbc.Driver); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } public static void close(Connection conn, Statement stmt, ResultSet rs) { // 依次关闭注意顺序和空判断 } }serverTimezoneUTC这个参数在 MySQL 8 之后基本是必加的不加会报时区错误。characterEncodingutf8是为了中文姓名不乱码。驱动类名从 MySQL 8 开始是com.mysql.cj.jdbc.Driver老版本是com.mysql.jdbc.Driver用错了会直接抛 ClassNotFoundException。这些细节在实验报告里通常不会写但实际跑的时候一个都不能少。3. 从零跑起来环境准备、建库、导包、启动的完整链路3.1 环境清单与版本对应关系在动手之前先把版本对齐否则后面报的错会让你怀疑人生。这套项目对版本不算挑剔但有几个组合是经过验证比较稳的组件建议版本说明JDK8 或 11JDK 8 兼容性最好11 也能跑MySQL5.7 或 8.08.0 需要改驱动类名和 URL 参数mysql-connector8.0.x与 MySQL 8.0 配套IDEEclipse / IntelliJ IDEA两者均可注意源码编码设为 UTF-8如果你的 MySQL 是 5.7驱动可以用 5.1.xURL 里不需要serverTimezone。如果是 8.0就必须用 8.0.x 的驱动并加上时区参数。这个对应关系搞错了最常见的现象就是启动时直接抛Communications link failure或者Unknown database。3.2 建库与初始化数据拿到 sql 目录下的脚本后不要急着在命令行里 source先打开看一眼库名和表名是否和代码里一致。执行方式有两种命令行如下# 登录 MySQL mysql -u root -p # 执行初始化脚本 source /path/to/init.sql;或者在 Navicat、DBeaver 这类工具里直接打开脚本运行。执行完之后用SHOW TABLES;确认三张表都在再用SELECT * FROM student;看看初始数据有没有插进去。如果脚本里没有插入初始数据登录界面可能因为没有账号而进不去这时候需要手动往 user 表里插一条管理员记录。提示执行脚本前先确认脚本开头的CREATE DATABASE语句里的库名和 DBUtil 里 URL 的库名完全一致大小写也要一致Linux 下 MySQL 默认区分大小写。3.3 导入项目与依赖 jar 包在 IDE 里新建 Java 项目把 src 下的包整体拷进去。然后关键一步把 mysql-connector 的 jar 加到 Build Path 里。Eclipse 里是右键项目 → Build Path → Configure Build Path → Libraries → Add External JARsIDEA 里是 File → Project Structure → Libraries → 加号 → Java。加完之后jar 包会出现在 Referenced Libraries 下面这时候 DBUtil 里的Class.forName才能找到驱动类。很多人卡在这一步现象是编译能过一运行就报No suitable driver found。原因就是 jar 没进 classpath或者进了但驱动类名写错了。检查方法很简单在 DBUtil 的静态块里加一句System.out.println(driver loaded)启动时看控制台有没有输出。3.4 启动入口与登录流程Main 类通常只做一件事SwingUtilities.invokeLater(() - new LoginFrame().setVisible(true));。用 invokeLater 是 Swing 的规矩所有界面创建都要放在事件调度线程里不然可能出现界面闪烁或者组件不显示。登录框一般校验 user 表里的用户名和密码验证通过后 dispose 掉登录窗打开主窗口。主窗口里通常是左侧一个菜单树或者选项卡右侧是内容面板。点击“学生管理”就加载学生列表的 JTable点击“成绩录入”就切换到录入表单。这个切换逻辑如果用 CardLayout 实现会比反复 new 窗口更流畅也不会留下一堆隐藏窗口占内存。4. 核心模块拆解登录校验、学生 CRUD、成绩录入与统计的代码实现4.1 登录模块PreparedStatement 防注入的正确姿势登录校验看起来简单但用拼接 SQL 就是给自己埋雷。正确写法是用 PreparedStatementpublic boolean login(String username, String password) { String sql SELECT * FROM user WHERE username ? AND password ?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, username); ps.setString(2, password); ResultSet rs ps.executeQuery(); return rs.next(); // 有记录就说明账号密码匹配 } catch (SQLException e) { e.printStackTrace(); return false; } }?是占位符setString按位置从 1 开始填。这样写的好处是即使用户输入 OR 11这种内容也会被当成普通字符串处理不会改变 SQL 语义。try-with-resources 是 JDK 7 之后的标准写法连接和语句会自动关闭不用手动写 finally。如果这里用了 Statement 加字符串拼接那这个项目就只能当作业交不能当例子学。4.2 学生管理表格渲染与增删改的联动学生列表一般用 JTable 展示数据来自SELECT * FROM student。核心是把 ResultSet 转成表格模型public void loadStudents(JTable table) { String[] columns {学号, 姓名, 性别, 班级, 专业}; DefaultTableModel model new DefaultTableModel(columns, 0); String sql SELECT id, name, gender, class_name, major FROM student; try (Connection conn DBUtil.getConnection(); Statement stmt conn.createStatement(); ResultSet rs stmt.executeQuery(sql)) { while (rs.next()) { model.addRow(new Object[]{ rs.getString(id), rs.getString(name), rs.getString(gender), rs.getString(class_name), rs.getString(major) }); } } catch (SQLException e) { e.printStackTrace(); } table.setModel(model); }这里用 Statement 是因为没有用户输入参数不存在注入风险。DefaultTableModel的第二个参数 0 表示初始行数为零后面用addRow逐行追加。每次刷新都重新 setModel而不是去清空旧 model这样能避免表格里残留脏数据。删除操作拿到选中行的学号后执行DELETE FROM student WHERE id ?如果该学生有成绩记录外键会阻止删除这时候要提示用户先删成绩或者做级联处理。4.3 成绩录入与统计一次插入多条与平均分计算成绩录入通常有两种模式单个录入和批量导入。单个录入就是选学生、选课程、填分数然后 insert。批量导入一般是从表格里读多行循环执行 insert这时候用批处理会快很多public void batchInsert(ListScore list) { String sql INSERT INTO score(student_id, course_id, score) VALUES(?,?,?); try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { conn.setAutoCommit(false); // 关闭自动提交 for (Score s : list) { ps.setString(1, s.getStudentId()); ps.setString(2, s.getCourseId()); ps.setDouble(3, s.getScore()); ps.addBatch(); // 加入批次 } ps.executeBatch(); // 一次性提交 conn.commit(); } catch (SQLException e) { e.printStackTrace(); } }setAutoCommit(false)加上commit()是为了保证要么全成功要么全回滚避免插了一半失败留下脏数据。统计功能一般是按课程算平均分、最高分、最低分SQL 层面用AVG、MAX、MIN就能出结果Java 这边只负责把结果填到界面标签上。如果要在 Java 里算那就把某个课程的所有成绩查出来用 Stream 的mapToDouble().average()处理但数据量大时不如交给数据库。5. 避坑与排查跑不起来时先看这几条5.1 中文乱码数据库、连接、界面三处都要查现象是学生姓名显示成问号或者方块。原因可能有三层数据库建表时没指定 utf8连接 URL 没加 characterEncoding或者 Swing 组件的字体不支持中文。解决顺序是先确认SHOW CREATE TABLE student;里字符集是不是 utf8mb4再检查 URL 参数最后在代码里给 JLabel、JTable 设置支持中文的字体比如new Font(微软雅黑, Font.PLAIN, 14)。5.2 驱动加载失败jar 包和类名双检查现象是启动就报ClassNotFoundException: com.mysql.cj.jdbc.Driver。原因通常是 jar 没加进 classpath或者 MySQL 是 5.7 但用了 8.0 的驱动类名。解决办法是确认 Referenced Libraries 里有 connector jar然后根据 MySQL 版本选对类名5.7 用com.mysql.jdbc.Driver8.0 用com.mysql.cj.jdbc.Driver。5.3 时区报错URL 参数补全现象是连接时抛The server time zone value ... is unrecognized。原因是 MySQL 8 的驱动要求明确指定时区。解决办法是在 URL 后面加上?serverTimezoneAsia/Shanghai或者?serverTimezoneUTC同时保留useSSLfalse避免 SSL 警告。5.4 外键约束导致删除失败现象是点删除学生时弹 SQLException。原因是 score 表里有该学生的成绩记录外键阻止了删除。解决办法是先删成绩再删学生或者在建表时把外键改成ON DELETE CASCADE让数据库自动级联删除。两种方式各有取舍级联删除方便但风险高手动删更安全但代码多几行。5.5 界面卡死数据库操作别放事件线程现象是点击查询按钮后界面无响应几秒钟。原因是 JDBC 查询是阻塞操作直接跑在 Swing 的事件调度线程里。解决办法是把数据库操作放到SwingWorker的 doInBackground 里查询完再在 done 方法里更新表格。数据量小的时候感觉不明显一旦学生上千条就会很明显。6. 二次开发怎么改从单机版到带导出和权限的实用版拿到这份源码之后最值得做的改造不是加花哨界面而是补两个真实场景里一定会用到的能力数据导出和角色权限。下面说具体怎么动。数据导出最实用的是导出 Excel。不用引入 POI 那么重的库先用最简单的 CSV 实现在成绩查询的 DAO 里加一个方法把 ResultSet 逐行拼成逗号分隔的字符串写进.csv文件。注意中文要用 UTF-8 BOM 开头否则 Excel 打开会乱码。代码大概长这样public void exportCsv(String filePath) throws IOException { String sql SELECT s.id, s.name, c.name, sc.score FROM score sc JOIN student s ON sc.student_id s.id JOIN course c ON sc.course_id c.id; try (Connection conn DBUtil.getConnection(); Statement stmt conn.createStatement(); ResultSet rs stmt.executeQuery(sql); BufferedWriter writer new BufferedWriter( new OutputStreamWriter(new FileOutputStream(filePath), UTF-8))) { writer.write(\uFEFF); // BOM防 Excel 乱码 writer.write(学号,姓名,课程,成绩\n); while (rs.next()) { writer.write(rs.getString(1) , rs.getString(2) , rs.getString(3) , rs.getDouble(4) \n); } } catch (SQLException e) { e.printStackTrace(); } }\uFEFF这个 BOM 字符是给 Excel 看的不加的话中文列名会变成乱码。导出按钮绑定这个方法用 JFileChooser 让用户选保存路径就行。角色权限的改造稍微大一点但思路清晰在 user 表加一个 role 字段登录成功后把角色存到一个全局的 Session 类里主窗口根据角色决定哪些菜单可见。比如管理员能看到所有功能教师只能录入和查询成绩学生只能查自己的成绩。关键是在 DAO 层也要做校验不能只靠界面隐藏按钮否则改一下请求就能绕过。验证改造是否成功我一般会走一遍完整链路用管理员登录 → 录入一条成绩 → 导出 CSV → 用 Excel 打开确认无乱码 → 换教师账号登录 → 确认看不到用户管理菜单。每一步都过了才算改完。从那以后我每次拿到这类课程设计源码都强制自己先跑通登录和一条增删改查再动任何一行代码。因为很多坑不在业务逻辑里而在环境和配置上先确认基线能跑后面改出问题才知道是哪一步引入的。希望这份拆解能帮你少走点弯路。本文还有配套的精品资源点击获取