生信息管理系統(tǒng)課設(shè)實(shí)戰(zhàn):從需求分析到答辯全流程)
簡介面向Java課程設(shè)計(jì)與期末項(xiàng)目這是一份學(xué)生信息管理系統(tǒng)完整源碼包適合高校學(xué)生與Java入門者直接導(dǎo)入運(yùn)行、二次開發(fā)。系統(tǒng)圍繞學(xué)生與成績的增刪改查、登錄驗(yàn)證、管理員管理等課設(shè)常見需求展開用Swing構(gòu)建圖形界面并采用集合、JDBC、文件序列化、事件監(jiān)聽與異常處理等關(guān)鍵技術(shù)幫助讀者把Java語法和面向?qū)ο笤O(shè)計(jì)落到實(shí)際工程中。資源包共54個(gè)文件以22個(gè)Java源文件和29個(gè)class編譯文件為主體附帶Eclipse工程配置體積僅51KB結(jié)構(gòu)清晰導(dǎo)入開發(fā)環(huán)境即可查看或運(yùn)行。當(dāng)前已有558人學(xué)習(xí)下載適合作為課程設(shè)計(jì)報(bào)告撰寫、答辯演示、畢業(yè)設(shè)計(jì)起步的參考。通過研讀源碼可獲得一套完整可運(yùn)行的課設(shè)項(xiàng)目框架明確從界面交互到數(shù)據(jù)持久化的實(shí)現(xiàn)路徑便于在此基礎(chǔ)上擴(kuò)展新功能、加深對Java編程和軟件工程實(shí)踐的認(rèn)知。1. 從「課設(shè)被催」到「一次過審」一個(gè) Java 學(xué)生信息管理系統(tǒng)的完整落地思路如果你正在為 Java 課程設(shè)計(jì)發(fā)愁大概率是因?yàn)轭}目聽起來太普通——學(xué)生信息管理系統(tǒng)幾乎是每屆 Java 課設(shè)的“保留曲目”。但正因?yàn)樽龅娜硕鄬?dǎo)師對它的要求反而更細(xì)不能只是一個(gè)能增刪改查的玩具得有完整的登錄鑒權(quán)、關(guān)聯(lián)查詢、異常處理、驗(yàn)收文檔。本方案就以這個(gè)經(jīng)典題目為例從需求、表結(jié)構(gòu)、分層代碼到答辯話術(shù)給你一條可以直接抄作業(yè)的完整鏈路。這套方案適合兩類人一是正在做畢設(shè)或課設(shè)、需要一份結(jié)構(gòu)完整的參考實(shí)現(xiàn)的學(xué)生二是想快速搭一個(gè)內(nèi)部教學(xué)演示系統(tǒng)的開發(fā)者。它選用 Java Swing 做界面、JDBC 操作 MySQL采用經(jīng)典的三層架構(gòu)把登錄、學(xué)生信息維護(hù)、成績統(tǒng)計(jì)、用戶管理四個(gè)核心場景做透并補(bǔ)齊了數(shù)據(jù)庫初始化腳本、工具類和驗(yàn)收清單。你拿到的不是一段段零散代碼而是一套從建庫到答辯的閉環(huán)方案。2. 需求分析與技術(shù)選型先搞清楚這門課到底驗(yàn)收什么2.1 功能需求從導(dǎo)師視角倒推出來的四類必做模塊很多同學(xué)拿到題目就開始寫代碼結(jié)果寫到一半發(fā)現(xiàn)導(dǎo)師要的是「能夠管理班級和學(xué)生成績的系統(tǒng)」而不是「能存學(xué)生信息的記事本」。我一般建議先列功能清單把導(dǎo)師最容易追問的點(diǎn)提前暴露出來。一個(gè)標(biāo)準(zhǔn)的學(xué)生信息管理系統(tǒng)至少要覆蓋以下四塊登錄與權(quán)限不同角色管理員、教師、學(xué)生登錄后看到不同菜單操作權(quán)限不同。這是課設(shè)答辯時(shí)的高頻加分項(xiàng)。學(xué)生信息管理對學(xué)生的學(xué)號(hào)、姓名、性別、出生日期、班級、電話、郵箱等字段做增刪改查支持按姓名或?qū)W號(hào)模糊搜索。成績管理錄入各科成績、自動(dòng)計(jì)算總分與平均分支持按課程查詢并能導(dǎo)出到文本或 Excel 格式。數(shù)據(jù)統(tǒng)計(jì)與展示按班級統(tǒng)計(jì)人數(shù)、按課程統(tǒng)計(jì)平均分、按分?jǐn)?shù)段統(tǒng)計(jì)人數(shù)分布通常用柱狀圖或表格呈現(xiàn)。此外還要考慮非功能需求數(shù)據(jù)要持久化到數(shù)據(jù)庫而不是內(nèi)存界面操作要有錯(cuò)誤提示和二次確認(rèn)密碼不能明文存儲(chǔ)。這些要求看起來簡單但如果你在驗(yàn)收時(shí)能主動(dòng)提到「我用 MD5 加鹽存密碼」「我用事務(wù)保證成績批量錄入的一致性」導(dǎo)師會(huì)認(rèn)為你真正理解了系統(tǒng)設(shè)計(jì)。2.2 技術(shù)選型為什么是 Swing JDBC 而不是更“新”的框架課設(shè)場景下技術(shù)選型的核心原則是「能自圓其說 能寫清楚原理」。很多同學(xué)糾結(jié)要不要用 Spring Boot MyBatis但導(dǎo)師更看重你是否理解數(shù)據(jù)是怎么從界面層傳到數(shù)據(jù)庫的。Swing 是 JDK 自帶的 GUI 組件用它可以擺脫 Web 前端那些繁瑣的配置把精力集中在 Java 核心語法和 JDBC 的使用上。數(shù)據(jù)訪問層選擇原生 JDBC 而不是 MyBatis同樣是為了讓底層邏輯透明。你可以清晰回答「預(yù)編譯語句如何防止 SQL 注入」「事務(wù)的提交和回滾發(fā)生在哪一行代碼」。如果你用 Spring Boot反而容易被框架細(xì)節(jié)絆住答辯時(shí)一問三不知。當(dāng)然如果你已經(jīng)熟練掌握了框架也可以把框架版本作為延伸內(nèi)容展示。本方案提供的代碼按 JDK 8 MySQL 5.7 環(huán)境編寫兼容絕大多數(shù)高校機(jī)房環(huán)境。2.3 環(huán)境準(zhǔn)備與項(xiàng)目結(jié)構(gòu)一上來就少踩兩個(gè)坑在寫任何業(yè)務(wù)代碼之前先把項(xiàng)目結(jié)構(gòu)和環(huán)境固定好。我用的是這樣的包結(jié)構(gòu)src/ ├── com/edu/smis/ │ ├── view/ # 界面層登錄窗口、主窗口、學(xué)生管理面板 │ ├── controller/ # 控制層事件監(jiān)聽與業(yè)務(wù)邏輯跳轉(zhuǎn) │ ├── service/ # 業(yè)務(wù)層登錄校驗(yàn)、成績計(jì)算等 │ ├── dao/ # 數(shù)據(jù)訪問層studentDao、scoreDao、userDao │ ├── entity/ # 實(shí)體類Student、Score、User、Clazz │ ├── util/ # 工具類DBUtil、MD5Util、ExcelUtil │ └── config/ # 配置類數(shù)據(jù)庫連接參數(shù)這個(gè)結(jié)構(gòu)是經(jīng)典的三層架構(gòu)變體。view 管界面顯示dao 管數(shù)據(jù)庫操作service 管業(yè)務(wù)判斷entity 只做數(shù)據(jù)載體。不要圖省事把 SQL 寫在界面里否則后期改一個(gè)字段名就要滿文件找。另外一個(gè)常見坑是 JDK 版本過高導(dǎo)致 JMesa 之類第三方庫兼容異常建議用 JDK 8 配 Eclipse 或 IntelliJ IDEA數(shù)據(jù)庫驅(qū)動(dòng)用mysql-connector-java 5.1.49這類穩(wěn)定版本。3. 總體架構(gòu)設(shè)計(jì)三層職責(zé)劃分與數(shù)據(jù)庫表關(guān)系3.1 架構(gòu)圖與調(diào)用流程從按鈕點(diǎn)擊到數(shù)據(jù)落庫的完整鏈路整個(gè)系統(tǒng)的調(diào)用關(guān)系可以用一條線說清楚用戶在 Swing 界面點(diǎn)擊「保存」按鈕View 層把表單數(shù)據(jù)封裝成 Student 對象調(diào)用 Service 層的addStudent()方法Service 層先校驗(yàn)學(xué)號(hào)是否重復(fù)再調(diào)用 DAO 層的insert()方法DAO 層通過 JDBC 的 PreparedStatement 執(zhí)行 SQL并返回受影響行數(shù)最后 View 層根據(jù)返回值彈出「保存成功」或「保存失敗」。異常從 DAO 層向上拋逐層包裹最終由 View 層捕獲并展示給用戶。我一般會(huì)在答辯時(shí)把這條調(diào)用鏈畫在 PPT 上導(dǎo)師一看就知道你理解了解耦。這里有個(gè)設(shè)計(jì)細(xì)節(jié)值得強(qiáng)調(diào)Service 層要處理事務(wù)比如「新增學(xué)生后同時(shí)寫入一條班級統(tǒng)計(jì)」此時(shí)要保證兩個(gè) DAO 操作在同一個(gè)事務(wù)里不能一個(gè)成功一個(gè)失敗。JDBC 的默認(rèn)行為是每條 SQL 自動(dòng)提交所以事務(wù)控制必須手動(dòng)開啟。3.2 數(shù)據(jù)庫設(shè)計(jì)五張表搞定全部需求數(shù)據(jù)庫是這類系統(tǒng)的核心資產(chǎn)。我設(shè)計(jì)了五張表用戶表t_user、學(xué)生表t_student、班級表t_clazz、課程表t_course、成績表t_score。它們的關(guān)系是一個(gè)班級包含多個(gè)學(xué)生一個(gè)學(xué)生可以選多門課程成績表通過學(xué)生ID和課程ID關(guān)聯(lián)。下面是關(guān)鍵建表語句-- 班級表 CREATE TABLE t_clazz ( id INT PRIMARY KEY AUTO_INCREMENT, clazz_name VARCHAR(50) NOT NULL UNIQUE, grade_year VARCHAR(20) COMMENT 入學(xué)年份 ); -- 學(xué)生表 CREATE TABLE t_student ( id INT PRIMARY KEY AUTO_INCREMENT, stu_no VARCHAR(20) NOT NULL UNIQUE COMMENT 學(xué)號(hào), stu_name VARCHAR(30) NOT NULL, gender CHAR(2) DEFAULT 男, birthday DATE, phone VARCHAR(15), clazz_id INT, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (clazz_id) REFERENCES t_clazz(id) ); -- 成績表一個(gè)學(xué)生一門課一條記錄 CREATE TABLE t_score ( id INT PRIMARY KEY AUTO_INCREMENT, stu_id INT NOT NULL, course_id INT NOT NULL, score DECIMAL(5,2), exam_time VARCHAR(20), UNIQUE KEY uk_stu_course (stu_id, course_id) );注意t_student的學(xué)號(hào)字段加了UNIQUE約束這樣即使代碼里忘記校驗(yàn)數(shù)據(jù)庫層面也能擋住重復(fù)學(xué)號(hào)。成績表用聯(lián)合唯一鍵防止同一學(xué)生同一課程出現(xiàn)兩條記錄。這里體現(xiàn)的是「數(shù)據(jù)庫約束優(yōu)先于代碼判斷」的思路答辯時(shí)可以展開講——你用雙重手段保證數(shù)據(jù)完整性證明你考慮過并發(fā)場景下的臟數(shù)據(jù)。3.3 工具類與配置管理DBUtil 與 MD5Util 的寫法數(shù)據(jù)操作的第一步是獲得連接。我不會(huì)在每個(gè) DAO 里寫DriverManager.getConnection()而是封裝一個(gè) DBUtil 工具類并用配置文件維護(hù)連接參數(shù)方便不同機(jī)器切換數(shù)據(jù)庫。核心代碼如下public class DBUtil { private static final String DRIVER com.mysql.jdbc.Driver; private static final String URL jdbc:mysql://localhost:3306/smis?useUnicodetruecharacterEncodingutf8useSSLfalse; private static final String USER root; private static final String PASSWORD 123456; static { try { Class.forName(DRIVER); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } public static void close(Connection conn, PreparedStatement ps, ResultSet rs) { // 逐個(gè)關(guān)閉處理空值 } }這段代碼有三個(gè)要點(diǎn)useSSLfalse是為了避免 MySQL 8.0 默認(rèn) SSL 提示characterEncodingutf8保證中文不亂碼靜態(tài)塊只注冊一次驅(qū)動(dòng)。實(shí)際課設(shè)中很多人會(huì)忘記關(guān)連接導(dǎo)致連接池耗盡。我的習(xí)慣是在 finally 塊里關(guān)閉資源或者用 try-with-resources 語法這是 Java 7 以后的推薦方式。你也可以在 DBUtil 里增加一個(gè)打印堆棧的調(diào)試方法方便查錯(cuò)。4. 核心模塊實(shí)現(xiàn)登錄鑒權(quán)、學(xué)生管理、成績統(tǒng)計(jì)與導(dǎo)出4.1 登錄模塊MD5 加鹽與驗(yàn)證碼循環(huán)檢測登錄模塊是最容易做出「安全感」的地方也是導(dǎo)師最愛問的模塊。我把用戶表設(shè)計(jì)成包含username、password、role三列其中密碼存儲(chǔ)的是 MD5 加鹽后的哈希值而不是明文。加鹽的規(guī)則是「用戶名 固定鹽值 用戶密碼」拼成字符串再取哈希。這樣即使數(shù)據(jù)庫泄露攻擊者也無法直接拿到原始密碼。登錄校驗(yàn)邏輯如下public class LoginService { // 驗(yàn)證用戶名密碼 public boolean checkLogin(String username, String password) { try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement( SELECT password, role FROM t_user WHERE username ?)) { ps.setString(1, username); try (ResultSet rs ps.executeQuery()) { if (rs.next()) { String md5Stored rs.getString(password); String md5Input MD5Util.md5(username SALT password); // 比對哈希值 return md5Stored.equals(md5Input); } } } catch (SQLException e) { e.printStackTrace(); } return false; } }這里用PreparedStatement而不是Statement是為了防止 SQL 注入。你可以嘗試在用戶輸入框里輸入 or 11如果系統(tǒng)還能登錄說明代碼不安全。登錄成功的用戶角色會(huì)寫進(jìn)一個(gè)全局 Session 對象后續(xù)主窗口根據(jù)角色決定哪些菜單可用。比如管理員能看到「用戶管理」教師只能看到「成績錄入」學(xué)生只能看「成績查詢」。答辯時(shí)你可以現(xiàn)場演示一個(gè)普通賬號(hào)嘗試訪問管理功能被拒絕的過程這比任何口述都有說服力。4.2 學(xué)生信息管理分頁查詢與模糊搜索學(xué)生列表是整個(gè)系統(tǒng)使用頻率最高的界面因此必須支持分頁和模糊查詢。分頁可以在 SQL 層面用LIMIT offset, size實(shí)現(xiàn)也可以把所有數(shù)據(jù)查回來再在內(nèi)存里切。更好的做法是 SQL 分頁尤其當(dāng)數(shù)據(jù)量超過幾百條時(shí)兩者性能差異明顯。我實(shí)現(xiàn)的 DAO 如下public ListStudent findByPage(String keyword, int offset, int size) { ListStudent list new ArrayList(); String baseSql SELECT s.*, c.clazz_name FROM t_student s LEFT JOIN t_clazz c ON s.clazz_id c.id ; String where ; if (keyword ! null !keyword.trim().isEmpty()) { where WHERE s.stu_no LIKE ? OR s.stu_name LIKE ? ; } String sql baseSql where ORDER BY s.id DESC LIMIT ?, ?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { int idx 1; if (!where.isEmpty()) { String like % keyword.trim() %; ps.setString(idx, like); ps.setString(idx, like); } ps.setInt(idx, offset); ps.setInt(idx, size); try (ResultSet rs ps.executeQuery()) { // 遍歷 rs 封裝 Student 對象 } } catch (SQLException e) { e.printStackTrace(); } return list; }注意LIKE語句的拼接方式我用LIKE ?綁定參數(shù)而不是直接拼接keyword到 SQL 字符串里。這樣搜索內(nèi)容里只要含有%或_也不會(huì)被數(shù)據(jù)庫誤解析成通配符。分頁參數(shù)offset由界面層傳入每頁大小固定為 10。界面層的上一頁、下一頁按鈕本質(zhì)上是修改 offset 值后重新查詢。為了防止用戶瘋狂點(diǎn)擊下一頁導(dǎo)致頁碼越界我會(huì)查一下總記錄數(shù)計(jì)算總頁數(shù)后禁用邊界按鈕??傆涗洈?shù)查詢和列表查詢可以封裝在一個(gè) PageResult 對象里一次性返回。4.3 成績統(tǒng)計(jì)模塊一條 SQL 算出平均分與及格率成績統(tǒng)計(jì)是區(qū)別于「單表增刪改查」的亮點(diǎn)功能。我用一條聚合查詢完成每門課程的平均分、最高分、最低分和及格人數(shù)。SQL 如下SELECT c.course_name, COUNT(sc.id) AS total_cnt, AVG(sc.score) AS avg_score, MAX(sc.score) AS max_score, MIN(sc.score) AS min_score, SUM(CASE WHEN sc.score 60 THEN 1 ELSE 0 END) AS pass_cnt FROM t_course c LEFT JOIN t_score sc ON c.id sc.course_id GROUP BY c.id, c.course_name ORDER BY c.id;這條 SQL 的巧妙之處在于用LEFT JOIN保證沒有成績的課程也能顯示用CASE WHEN統(tǒng)計(jì)及格人數(shù)避免在 Java 代碼里遍歷每條成績再去判斷。你會(huì)發(fā)現(xiàn)SQL 寫得越復(fù)雜Java 代碼就越簡單導(dǎo)師會(huì)覺得你的數(shù)據(jù)庫功底扎實(shí)。查詢結(jié)果直接封裝成 ScoreStatistic 實(shí)體界面用 JTable 展示并在下方顯示一個(gè)「導(dǎo)出」按鈕。導(dǎo)出功能也是一個(gè)加分項(xiàng)。我用 BufferedWriter 以 UTF-8 編碼將查詢結(jié)果寫入 CSV 文件如果要導(dǎo)出 Excel可以用 Apache POI 的HSSFWorkbook生成.xls文件。CSV 的好處是 Excel 可以直接打開代碼量最少。導(dǎo)出文件默認(rèn)放到用戶桌面文件名帶時(shí)間戳避免覆蓋。這里有個(gè)細(xì)節(jié)Swing 的JFileChooser可以選保存路徑我在答辯時(shí)通常演示「先查詢成績再導(dǎo)出 CSV再用 Excel 打開」視覺效果很直觀。4.4 密碼重置與用戶管理管理員專屬操作用戶管理模塊與前幾個(gè)模塊不同它面向管理員角色功能包括新增用戶、刪除用戶、重置密碼。新增用戶時(shí)密碼初始化為123456并建議用戶首次登錄后修改。重置密碼的邏輯很簡單用 UserDA O的updatePassword方法把新密碼經(jīng)過 MD5 加鹽后寫回?cái)?shù)據(jù)庫。為了安全我在 Service 層增加了「禁止刪除當(dāng)前登錄用戶」的判斷避免管理員誤刪自己的賬號(hào)。這個(gè)模塊容易忽略的是「批處理」場景。如果管理員要批量導(dǎo)入學(xué)生通常的做法是讀取 Excel 文件逐條調(diào)用addStudent()。但逐條插入會(huì)頻繁打開關(guān)閉連接性能很差。我建議使用 JDBC 批處理在循環(huán)里調(diào)用ps.addBatch()每 500 條執(zhí)行一次executeBatch()最后統(tǒng)一提交。這樣插入 1000 條學(xué)生記錄的時(shí)間可以從十幾秒縮短到兩秒左右。課設(shè)答辯時(shí)提到這個(gè)優(yōu)化會(huì)明顯抬高技術(shù)印象分。5. 常見問題與避坑指南課設(shè)里最容易翻車的五個(gè)雷區(qū)5.1 亂碼問題界面顯示中文正常數(shù)據(jù)庫里卻變成問號(hào)這是最常見的翻車現(xiàn)場?,F(xiàn)象是 JTable 里剛插入的學(xué)生姓名顯示正常但重啟程序后查詢出來全是???。原因往往是連接 URL 里沒有設(shè)置字符編碼導(dǎo)致 JDBC 驅(qū)動(dòng)使用默認(rèn)編碼與 MySQL 通信。解決方案分兩步一是在 JDBC URL 里加上characterEncodingutf8我們前面 DBUtil 中已經(jīng)寫了二是創(chuàng)建數(shù)據(jù)庫時(shí)指定字符集CREATE DATABASE smis DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;另外IDE 的源碼文件編碼也要統(tǒng)一為 UTF-8。如果已經(jīng)出現(xiàn)亂碼數(shù)據(jù)可以把表字段的字符集改掉或者刪掉重建。從根源上說最好在寫代碼前就把這三處編碼規(guī)范定下來而不是最后再補(bǔ)。5.2 數(shù)據(jù)庫驅(qū)動(dòng)加載失敗ClassNotFoundException現(xiàn)象是程序一運(yùn)行就拋出java.lang.ClassNotFoundException: com.mysql.jdbc.Driver。原因無非兩種jar 包沒有放到構(gòu)建路徑下或者驅(qū)動(dòng)類名寫錯(cuò)。MySQL 5.x 和 8.x 的驅(qū)動(dòng)類名有區(qū)別——5.x 是com.mysql.jdbc.Driver8.x 是com.mysql.cj.jdbc.Driver。我的排查順序是先看項(xiàng)目屬性里的 Libraries 是否包含 mysql-connector jar再看 jar 包版本與驅(qū)動(dòng)類名是否匹配。在 IDEA 里還可以打開Project Structure - Modules - Dependencies檢查包是否帶號(hào)。另外如果你的代碼是直接運(yùn)行在命令行別忘記在java -cp里把 jar 包路徑寫進(jìn)去。這個(gè)坑雖然低級但幾乎每年都有同學(xué)卡住半小時(shí)。5.3 事務(wù)失效成績批量錄入時(shí)出現(xiàn)「半成功」?fàn)顟B(tài)當(dāng)你實(shí)現(xiàn)「一個(gè)學(xué)生多科成績一次提交」的功能時(shí)如果循環(huán)里某條插入失敗前面的插入已經(jīng)提交數(shù)據(jù)庫就會(huì)處于部分成功狀態(tài)。原因在于默認(rèn)的自動(dòng)提交模式讓每條 SQL 獨(dú)立成事務(wù)。正確做法是在 Service 層方法上開啟事務(wù)public boolean addScores(ListScore scores) { Connection conn null; try { conn DBUtil.getConnection(); conn.setAutoCommit(false); ScoreDao dao new ScoreDao(); for (Score s : scores) { dao.insert(conn, s); // DAO 接收外部連接 } conn.commit(); return true; } catch (Exception e) { conn.rollback(); return false; } finally { DBUtil.close(conn, null, null); } }注意這里 DAO 的方法簽名要支持傳入外部連接這樣才屬于同一個(gè)事務(wù)。很多同學(xué)的 DAO 方法里自己getConnection()導(dǎo)致外層事務(wù)控制失效。我通常會(huì)在代碼注釋里提醒不要在 DAO 內(nèi)部獲取新連接除非你確認(rèn)不需要事務(wù)。5.4 JTable 刷新不更新修改數(shù)據(jù)后界面還是舊內(nèi)容現(xiàn)象是執(zhí)行完新增或刪除操作后表格沒有變化要點(diǎn)擊「查詢」按鈕才看到新數(shù)據(jù)。原因是 Swing 的 TableModel 沒有通知界面數(shù)據(jù)已變。JTable 本身不會(huì)自動(dòng)感知數(shù)據(jù)源變化。解決方案是每次增刪改成功后重新查詢數(shù)據(jù)并調(diào)用tableModel.setDataVector()或者直接把新 List 傳給自定義的 TableModel 再執(zhí)行table.fireTableDataChanged()。簡單做法是封裝一個(gè)refreshTableData()方法里面重新查庫并更新模型。這里有個(gè)小細(xì)節(jié)如果查詢是在子線程里執(zhí)行的需要調(diào)用SwingUtilities.invokeLater來更新界面否則可能出現(xiàn)并發(fā)修改異常。5.5 未關(guān)閉資源導(dǎo)致連接耗盡操作幾次后卡死現(xiàn)象是系統(tǒng)運(yùn)行十幾分鐘后點(diǎn)擊任何按鈕都無響應(yīng)控制臺(tái)可能報(bào)Too many connections。原因就是每個(gè) DAO 方法里創(chuàng)建了連接但忘記關(guān)閉。雖然程序退出時(shí)連接會(huì)釋放但在連續(xù)操作期間連接數(shù)會(huì)一直增加。解決辦法是嚴(yán)格遵循「誰打開誰關(guān)閉」原則而且關(guān)閉要放在 finally 塊中。如果你用 Java 7 以上的環(huán)境使用 try-with-resources 最穩(wěn)妥try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { // 執(zhí)行 } catch (SQLException e) { // 日志 }不要嫌麻煩這是從資源管理層面體現(xiàn)工程質(zhì)量的關(guān)鍵點(diǎn)。一旦養(yǎng)成這個(gè)習(xí)慣你的代碼在任何評審面前都不會(huì)因?yàn)榈图壻Y源泄漏丟分。6. 驗(yàn)收演示與答辯技巧讓導(dǎo)師 5 分鐘內(nèi)認(rèn)可你的系統(tǒng)6.1 準(zhǔn)備一份「演示腳本」而不是打開軟件亂點(diǎn)答辯現(xiàn)場最怕冷場和不知道下一步點(diǎn)什么。我強(qiáng)烈建議提前寫一頁演示腳本把關(guān)鍵操作按順序固定下來。我的演示順序通常是先啟動(dòng)程序展示登錄界面輸入管理員賬號(hào)登錄成功后展示主窗口菜單說明當(dāng)前角色是管理員因此能看到「用戶管理」菜單接著進(jìn)入學(xué)生管理演示新增學(xué)生、修改學(xué)生信息、按學(xué)號(hào)搜索、刪除學(xué)生然后進(jìn)入成績管理給某學(xué)生錄入三科成績界面自動(dòng)算出總分和平均分再打開成績統(tǒng)計(jì)頁面展示柱狀圖或表格點(diǎn)擊導(dǎo)出 CSV用文本方式打開文件確認(rèn)生成成功。這個(gè)流程大約 5 分鐘覆蓋了最核心的功能。導(dǎo)師中途可能會(huì)打斷提問你不用慌按「功能模塊 技術(shù)實(shí)現(xiàn)」兩層來回答。比如他問「分頁是怎么實(shí)現(xiàn)的」你先答「用 SQL 里的 LIMIT 和 OFFSET一次只查 10 條」再答「界面層維護(hù)當(dāng)前頁碼和總頁數(shù)翻頁時(shí)重新調(diào)用查詢方法」。6.2 答辯高頻問題與參考回答口徑結(jié)合歷年課設(shè)評審我將高頻問題及回答思路整理如下可能被問到的問題建議回答要點(diǎn)為什么不直接用框架課設(shè)重點(diǎn)是體現(xiàn) Java 基礎(chǔ)Swing JDBC 更直接展示底層原理框架可作為展望提一下。密碼安全怎么考慮明文密碼不落庫MD5 加鹽后再存驗(yàn)證時(shí)比對哈希值。如果兩個(gè)人同時(shí)修改同一學(xué)生信息怎么辦目前采用樂觀鎖思路提交時(shí)比對修改時(shí)間戳后續(xù)可以升級為版本號(hào)字段。數(shù)據(jù)庫表為什么這么設(shè)計(jì)滿足第三范式減少冗余成績表用聯(lián)合唯一鍵保證數(shù)據(jù)唯一。性能瓶頸在哪目前主要瓶頸是每操作一次都要建連接如果數(shù)據(jù)量大可以引入連接池如 HikariCP。回答時(shí)注意不要在導(dǎo)師面前說「這里不太清楚」你可以誠實(shí)說「這個(gè)場景我在課設(shè)里簡化了如果實(shí)際生產(chǎn)會(huì)……」這反而顯得你有工程意識(shí)。6.3 最后 2 分鐘主動(dòng)講一個(gè)「改進(jìn)方向」并演示一個(gè)難點(diǎn)答辯時(shí)間充裕時(shí)我會(huì)留出最后一分鐘講自己踩過的坑和思考。比如我可以說「在成績導(dǎo)入功能里一開始我用逐條插入后來發(fā)現(xiàn) 1000 條數(shù)據(jù)要花很久所以改成了 JDBC 批處理用 addBatch 每 500 條提交一次。這里有一個(gè)關(guān)鍵點(diǎn)就是事務(wù)控制要放到外層不然批量失敗時(shí)沒法回滾?!惯@時(shí)可以順手切到代碼展示conn.setAutoCommit(false)那一行。導(dǎo)師聽到你主動(dòng)講優(yōu)化基本會(huì)認(rèn)為你已經(jīng)超出了「完成作業(yè)」的層次。還有一個(gè)技巧把程序運(yùn)行中的一個(gè)小錯(cuò)誤保留在演示里然后現(xiàn)場修復(fù)。比如故意在演示時(shí)點(diǎn)擊導(dǎo)出到帶非法字符的路徑彈窗報(bào)錯(cuò)你再修改文件名校驗(yàn)邏輯。這種「現(xiàn)場排錯(cuò)」的效果往往比完美演示更可信因?yàn)樗C明你完全掌握了自己寫的代碼。6.4 交付物清單與最后自查提交前我通常按下面清單檢查一遍項(xiàng)目源碼是否包含完整包結(jié)構(gòu)數(shù)據(jù)庫建表腳本是否單獨(dú)放在doc目錄是否有 README 說明運(yùn)行環(huán)境、賬號(hào)密碼、啟動(dòng)步驟是否有演示截圖或錄屏答辯 PPT 是否包含架構(gòu)圖、功能截圖、核心代碼片段。其中最重要的是 README——導(dǎo)師可能直接用你的項(xiàng)目在另一臺(tái)電腦上跑如果你不能讓它在十分鐘內(nèi)跑起來前面所有工作量都會(huì)打折扣。從那次課設(shè)以后我每次提交代碼都會(huì)強(qiáng)制跑一遍「從空數(shù)據(jù)庫開始建表 → 啟動(dòng)程序 → 登錄 → 完成一次增刪改查 → 導(dǎo)出文件」的完整流程確認(rèn)沒有依賴本機(jī)特殊配置。自己做的東西最后這一步如果偷懶答辯時(shí)大概率翻車。希望這個(gè)方案能幫你省掉那些無謂的折騰把精力放在真正能拿分的地方。代碼里遇到具體問題歡迎帶著報(bào)錯(cuò)信息來交流我盡量給出可運(yùn)行的解法。本文還有配套的精品資源點(diǎn)擊獲取