課程設(shè)計與源碼實(shí)戰(zhàn):從環(huán)境部署到答辯指南)
簡介一份基于Java與MySQL的圖書管理系統(tǒng)項(xiàng)目面向計算機(jī)相關(guān)專業(yè)正在完成課程設(shè)計、期末大作業(yè)的學(xué)生也適合需要練習(xí)SwingJDBC開發(fā)的入門學(xué)習(xí)者。項(xiàng)目經(jīng)過嚴(yán)格調(diào)試源碼和數(shù)據(jù)庫腳本配套完整下載后可直接導(dǎo)入Eclipse/IDEA運(yùn)行配合附帶的運(yùn)行截圖即可快速核對功能省去從零搭建和排錯的時間。壓縮包內(nèi)共133個文件主要包含16個Java源碼文件、1個SQL建庫腳本、項(xiàng)目工程配置文件、54個class編譯文件以及58張PNG界面截圖整體大小僅3.38MB輕量且目錄清晰。從內(nèi)容預(yù)覽看系統(tǒng)已實(shí)現(xiàn)登錄窗口、主界面、圖書類型管理、圖書添加、圖書查詢及數(shù)據(jù)訪問層等模塊覆蓋典型圖書管理業(yè)務(wù)分層結(jié)構(gòu)便于理解。目前已有227人學(xué)習(xí)下載對需要快速交付課設(shè)或提升Java數(shù)據(jù)庫編程能力的讀者是一份高性價比的直接參考。1. 圖書管理系統(tǒng)大作業(yè)的開箱體驗(yàn)JavaMySQL為什么是課設(shè)常青樹如果你是計算機(jī)專業(yè)的學(xué)生正在為Java課程設(shè)計或數(shù)據(jù)庫期末大作業(yè)發(fā)愁這套基于JavaMySQL的圖書管理系統(tǒng)源碼值得打開看一眼。它覆蓋了登錄、圖書管理、圖書類型管理、分頁搜索這些課設(shè)必考功能壓縮包里既有源碼也有數(shù)據(jù)庫腳本配置好環(huán)境就能直接跑。和那些前后端分離的SpringBoot工程不同這是經(jīng)典的SwingJDBCMySQL組合代碼量適中、邏輯直觀正好踩在課程設(shè)計的評分點(diǎn)上給到95分以上不奇怪。它適合兩類人做課程設(shè)計想?yún)⒖荚O(shè)計思路的學(xué)生以及剛學(xué)完JDBC需要一個完整項(xiàng)目練手的學(xué)習(xí)者。先別急著雙擊運(yùn)行我們把結(jié)構(gòu)拆開、環(huán)境配上、流程跑通最后再聊答辯怎么準(zhǔn)備。2. 源碼結(jié)構(gòu)拆解從類名反推系統(tǒng)的三個業(yè)務(wù)分層2.1 壓縮包里的class與java源碼先分清哪份是你需要的拿到壓縮包第一件事不是雙擊運(yùn)行而是先看目錄結(jié)構(gòu)。這個項(xiàng)目解壓后你大概率會看到兩類文件并存以.java結(jié)尾的源碼文件以及以.class結(jié)尾的編譯產(chǎn)物。為什么會有.class因?yàn)樽髡咴诮桓肚霸贗DE里編譯運(yùn)行過IDE順手把字節(jié)碼輸出到了源碼目錄里。對課程設(shè)計來說.java才是要交給老師、寫進(jìn)報告、貼進(jìn)答辯PPT的東西.class直接忽略即可。不過換個角度想.class能存在說明這套代碼至少在作者機(jī)器上編譯通過過一次對你來說是個好消息。真正需要關(guān)注的類名都在項(xiàng)目正文里列出來了LogOnFrm、MainFrm、BookManageInterfrm、BookTypeManageInterFrm、BookAddInterFrm、BookTypeAddInterFrm、BookPageSearchFrm、BookDao、JavaInterFrm。命名規(guī)律非常明顯以Frm結(jié)尾的是帶邊框的獨(dú)立窗口以Interfrm結(jié)尾的是嵌在窗口內(nèi)部的面板或標(biāo)簽頁。這種命名方式在老式Swing課設(shè)里非常常見一眼就能看出哪個類是入口、哪個類是功能面板、哪個類是數(shù)據(jù)操作層。2.2 類名到功能的映射一張表看懂系統(tǒng)邊界對照類名和這類課設(shè)的通用實(shí)現(xiàn)我梳理了下面這張職責(zé)對照表類名類型職責(zé)LogOnFrm窗口類登錄界面系統(tǒng)唯一入口MainFrm窗口類登錄成功后的主界面承載菜單與面板切換BookManageInterfrm內(nèi)部面板圖書列表展示、編輯、刪除操作入口BookTypeManageInterFrm內(nèi)部面板圖書類型列表展示與維護(hù)入口BookAddInterFrm內(nèi)部面板新增圖書表單面板BookTypeAddInterFrm內(nèi)部面板新增圖書類型表單面板BookPageSearchFrm窗口或面板分頁搜索與結(jié)果展示BookDao數(shù)據(jù)訪問類圖書相關(guān)所有SQL操作與結(jié)果集封裝JavaInterFrm內(nèi)部面板按圖書類別篩選或附加功能頁這張表傳達(dá)了兩個關(guān)鍵信息。第一系統(tǒng)雖然功能不多但界面類和邏輯類是分離的所有Frm/Interfrm只負(fù)責(zé)事件監(jiān)聽、界面布局和數(shù)據(jù)展示不直接寫SQLSQL全部收攏在BookDao里。第二命名上Interfrm和Frm的區(qū)別意味著主窗口MainFrm很可能是用標(biāo)簽頁或卡片布局把多個Interfrm面板組合起來的這比一個窗口堆所有按鈕的做法清爽得多。課程設(shè)計評分老師看的就是這個。同樣功能的兩個項(xiàng)目一個把所有代碼寫在JFrame里另一個分了界面層、數(shù)據(jù)訪問層、實(shí)體類層分?jǐn)?shù)差距往往在十分以上。別覺得分層是形式主義老師看中的是你有沒有工程化思維——哪怕只是一個只有幾千行的課設(shè)分層清楚就意味著后續(xù)加功能時不用大改這就是軟件工程課里反復(fù)強(qiáng)調(diào)的低耦合。2.3 BookDao的核心寫碼方式JDBC查詢?nèi)绾巫兂蓪ο罅斜鞡ookDao是這套系統(tǒng)里含金量最高的一個類。它負(fù)責(zé)把MySQL里的圖書表數(shù)據(jù)查出來轉(zhuǎn)換成Java對象再交給界面層展示。先看一個典型的查詢方法這類課設(shè)的BookDao基本都是這個寫法// BookDao.java查詢?nèi)繄D書帶圖書類型名稱 public ListBook findAll() { ListBook list new ArrayList(); String sql SELECT b.id, b.bookName, b.author, b.price, t.typeName FROM t_book b LEFT JOIN t_booktype t ON b.typeId t.id; try (Connection conn DBHelper.getConnection(); PreparedStatement ps conn.prepareStatement(sql); ResultSet rs ps.executeQuery()) { while (rs.next()) { Book book new Book(); book.setId(rs.getInt(id)); book.setBookName(rs.getString(bookName)); book.setAuthor(rs.getString(author)); book.setPrice(rs.getDouble(price)); book.setTypeName(rs.getString(typeName)); list.add(book); } } catch (SQLException e) { e.printStackTrace(); } return list; }代碼邏輯拆開看并不復(fù)雜先用LEFT JOIN把圖書表和圖書類型表關(guān)聯(lián)起來一條SQL同時拿到圖書字段和類型名稱然后通過PreparedStatement執(zhí)行查詢從ResultSet結(jié)果集里逐行取值封裝成Book對象最后把多個Book對象放進(jìn)List集合返回給界面層。這里有兩個細(xì)節(jié)值得注意一是用了try-with-resources寫法Connection、PreparedStatement、ResultSet三個資源在try結(jié)束時自動關(guān)閉省去了手寫finally關(guān)閉的樣板代碼二是用了LEFT JOIN而不是INNER JOIN這樣即使某本書沒有關(guān)聯(lián)類型查詢結(jié)果也不會丟記錄。參數(shù)上getInt、getString、getDouble分別對應(yīng)數(shù)據(jù)庫的int、varchar、double類型字段順序無所謂只要列名寫對就行。界面層拿到ListBook之后直接丟給JTable的TableModel做展示這就是BookManageInterfrm里最核心的一段數(shù)據(jù)流。理解了BookDao后面不管是改查詢條件還是加分頁功能思路都會順很多。3. 環(huán)境部署與數(shù)據(jù)庫導(dǎo)入讓SQL腳本在你機(jī)器上跑通的完整流程3.1 環(huán)境版本怎么選JDK 8加MySQL 5.7是課設(shè)最穩(wěn)組合很多同學(xué)卡在第一步不是代碼問題而是環(huán)境版本不對。這套系統(tǒng)和大多數(shù)Java課設(shè)一樣用的是老一套JDBC驅(qū)動邏輯。我建議的組合是JDK 8配上MySQL 5.7這兩個版本搭配最省心驅(qū)動類名用com.mysql.jdbc.Driver就能直接識別不需要處理新版驅(qū)動的一堆時區(qū)、SSL參數(shù)。如果你機(jī)器上裝的是MySQL 8.0問題也不大但要注意兩個變化驅(qū)動類名要改成com.mysql.cj.jdbc.Driver連接URL要額外加上serverTimezoneAsia/Shanghai否則會報時區(qū)錯誤。這里我把兩種版本的關(guān)鍵差異整理成一條對比版本驅(qū)動類名URL中的附加參數(shù)MySQL 5.xcom.mysql.jdbc.Driver一般不需要MySQL 8.0com.mysql.cj.jdbc.DriverserverTimezoneAsia/ShanghaiuseSSLfalseJDK建議直接用8別一上來就上17或21。不是說新版不行而是Swing課設(shè)里依賴的老庫比如某些版本的MySQL Connector/J在更高JDK下會碰到模塊化限制報一些你聽都沒聽過的錯誤。課設(shè)要的是穩(wěn)定交付不是追新版本。3.2 導(dǎo)入數(shù)據(jù)庫命令行與Navicat兩條路都走一遍數(shù)據(jù)庫腳本一般在壓縮包里的.sql文件中。導(dǎo)入前先建一個空庫再把腳本導(dǎo)進(jìn)去。命令行方式是最直接的打開終端進(jìn)入MySQL命令后執(zhí)行CREATE DATABASE IF NOT EXISTS book_db DEFAULT CHARSET utf8 COLLATE utf8_general_ci; USE book_db; SOURCE /your/path/book_db.sql;上面這段做了三件事建一個名為book_db的數(shù)據(jù)庫字符集指定成utf8切換到該庫執(zhí)行外部SQL腳本文件。SOURCE后面要寫你本機(jī)解壓后book_db.sql文件的絕對路徑路徑中有空格就用雙引號包起來。執(zhí)行成功后用SHOW TABLES;應(yīng)該能看到t_book、t_booktype、t_user這類表。用Navicat導(dǎo)入更直觀左側(cè)右鍵連接選新建數(shù)據(jù)庫名字和字符集填好然后選中新建的數(shù)據(jù)庫右鍵選擇運(yùn)行SQL文件選到book_db.sql執(zhí)行就行。導(dǎo)入完成后注意點(diǎn)開表看看數(shù)據(jù)行數(shù)圖書表里應(yīng)該有幾條作者預(yù)置的測試數(shù)據(jù)登錄表里也應(yīng)該有默認(rèn)用戶名和密碼。如果表是空的說明腳本沒執(zhí)行完整重新導(dǎo)入一次或者檢查腳本里是否有DROP TABLE語句把數(shù)據(jù)清掉了。3.3 修改連接配置DBHelper類里的四個參數(shù)代碼運(yùn)行前必須改數(shù)據(jù)庫連接配置這個配置幾乎都寫在一個DBHelper或DBUtil類里也可能直接寫在每個Dao類的static塊中。項(xiàng)目里的BookDao要連接MySQL連接參數(shù)就藏在這個工具類里。常見的寫法長這樣// DBHelper.java數(shù)據(jù)庫連接工具類 public class DBHelper { private static final String DRIVER com.mysql.jdbc.Driver; private static final String URL jdbc:mysql://localhost:3306/book_db ?useUnicodetruecharacterEncodingutf8; 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); } }改動主要集中在四個地方第一是URL里的端口號3306如果你MySQL改過端口這里要同步第二是URL路徑里的book_db必須和3.2節(jié)建的庫名一致第三是USER默認(rèn)root第四是PASSWORD要填你自己MySQL的密碼。URL中間還有一段useUnicodetruecharacterEncodingutf8這是控制中文不亂碼的關(guān)鍵參數(shù)建議保留。改完之后先在IDE里寫個簡單的main方法測一下連接public static void main(String[] args) { try { Connection conn DBHelper.getConnection(); System.out.println(數(shù)據(jù)庫連接成功 conn); conn.close(); } catch (SQLException e) { e.printStackTrace(); } }能打印出連接對象說明配置沒問題。這一步值得做它能把環(huán)境問題和代碼問題快速切開后面跑不起來時你不用東猜西猜。3.4 IDE導(dǎo)入項(xiàng)目與入口類啟動順序Eclipse和IDEA的導(dǎo)入方式略有不同Eclipse是File→Import→General→Existing Projects into Workspace選中解壓后的項(xiàng)目根目錄IDEA是File→New→Project from Existing Sources同樣指向項(xiàng)目目錄然后選Import project from external model用Eclipse方式導(dǎo)入。導(dǎo)入后先做一件事確認(rèn)項(xiàng)目里有沒有MySQL驅(qū)動jar包也就是mysql-connector-java那一系列jar。如果Build Path里沒有去Maven倉庫下載匹配版本的jar放到項(xiàng)目里并Add to Build Path。沒有驅(qū)動包第2.3節(jié)里的Class.forName(com.mysql.jdbc.Driver)第一行就報錯。入口類很明確運(yùn)行LogOnFrm不是MainFrm。我見過有人直接右鍵MainFrm啟動結(jié)果彈了個空窗口或者報了一堆空指針異常。原因很簡單MainFrm主界面依賴當(dāng)前登錄用戶信息這些信息是在登錄成功后寫入內(nèi)存的繞過登錄直接打開主界面很多初始化變量還沒賦值自然就崩了。正確順序是運(yùn)行LogOnFrm登錄成功后由它來打開MainFrm并把當(dāng)前用戶傳過去。啟動之后界面彈出窗口說明系統(tǒng)已經(jīng)在跑了。用預(yù)置的管理員賬號登錄如果提示用戶名或密碼錯誤先回MySQL確認(rèn)t_user表里的數(shù)據(jù)大概率是密碼加密方式或明文差異的問題這類課設(shè)一般都用明文比對改一下表里的密碼即可。4. 核心模塊實(shí)現(xiàn)細(xì)節(jié)登錄、圖書增刪和分頁搜索的寫碼思路4.1 登錄模塊LogOnFrm的主流程與兩個安全細(xì)節(jié)登錄是整個系統(tǒng)的門面也是評卷老師大概率會試的第一個功能。LogOnFrm的邏輯不復(fù)雜用戶在界面上輸入用戶名和密碼點(diǎn)擊登錄按鈕后系統(tǒng)從輸入框取值調(diào)用Dao層方法去數(shù)據(jù)庫比對比對成功就打開MainFrm失敗就在當(dāng)前窗口彈提示。核心校驗(yàn)代碼的骨架一般是這樣的// 登錄按鈕的事件監(jiān)聽器內(nèi)部 String username txtUsername.getText().trim(); String password new String(txtPassword.getPassword()); boolean ok bookDao.checkLogin(username, password); if (ok) { new MainFrm(username).setVisible(true); dispose(); } else { JOptionPane.showMessageDialog(this, 用戶名或密碼錯誤); }這里有兩個細(xì)節(jié)值得注意也是能拿來答辯時講的點(diǎn)。第一密碼框用的是getPassword()方法而不是getText()前者返回的是char數(shù)組可以通過手動清空數(shù)組內(nèi)容來避免密碼字符串滯留在內(nèi)存中這是Swing里密碼輸入的標(biāo)準(zhǔn)做法。第二getText()后面跟了trim()去掉用戶誤輸入的首尾空格防止admin 和admin比對不上這種低級問題。對應(yīng)的Dao層SQL就是一個簡單的等值查詢用COUNT判斷記錄數(shù)是否大于0// BookDao中的登錄校驗(yàn)方法 public boolean checkLogin(String username, String password) { String sql SELECT COUNT(*) FROM t_user WHERE username ? AND password ?; try (Connection conn DBHelper.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, username); ps.setString(2, password); try (ResultSet rs ps.executeQuery()) { if (rs.next()) { return rs.getInt(1) 0; } } } catch (SQLException e) { e.printStackTrace(); } return false; }這里用了PreparedStatement而不是直接拼接字符串原因是防SQL注入。用占位符?先把SQL骨架固定再將用戶輸入作為參數(shù)傳進(jìn)去MySQL驅(qū)動會自動轉(zhuǎn)義特殊字符。答辯時如果老師問你為什么用PreparedStatement不用Statement這個回答既準(zhǔn)確又能體現(xiàn)你上課聽講了。4.2 圖書新增與類型管理表單數(shù)據(jù)落到MySQL的完整鏈路BookAddInterFrm負(fù)責(zé)收集新書信息界面一般有書名字段、作者字段、價格字段、類型下拉框。用戶點(diǎn)保存后界面層收集數(shù)據(jù)組裝成Book對象傳給BookDao的insert方法。類型下拉框的數(shù)據(jù)則來自BookTypeManageInterFrm管理的那張類型表。新增圖書的核心SQL就是一條INSERT// BookDao中的新增方法 public boolean addBook(Book book) { String sql INSERT INTO t_book (bookName, author, price, typeId) VALUES (?, ?, ?, ?); try (Connection conn DBHelper.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, book.getBookName()); ps.setString(2, book.getAuthor()); ps.setDouble(3, book.getPrice()); ps.setInt(4, book.getTypeId()); return ps.executeUpdate() 0; } catch (SQLException e) { e.printStackTrace(); } return false; }executeUpdate()返回的是受影響的行數(shù)插入成功返回1失敗返回0或拋異常。這里有一個常見問題如果圖書表還有一個ISBN字段且設(shè)置了唯一約束同一個ISBN插入兩次就會拋DuplicateKeyException代碼里如果沒有捕獲這個異常并提示用戶用戶體驗(yàn)就是點(diǎn)按鈕沒反應(yīng)。規(guī)范的寫法是單獨(dú)捕獲這個異常彈窗提示該書編號已存在。圖書類型管理BookTypeAddInterFrm的邏輯幾乎一模一樣只是SQL從操作圖書表換成操作類型表。需要注意類型表和圖書表之間存在外鍵關(guān)聯(lián)如果試圖刪除一個已被圖書引用的類型數(shù)據(jù)庫會因外鍵約束而刪除失敗。代碼里應(yīng)該先查該類型下有沒有圖書有就提示用戶先處理圖書。很多課設(shè)沒有處理這個細(xì)節(jié)答辯時老師一旦刪一個關(guān)聯(lián)類型當(dāng)場翻車。4.3 分頁搜索LIMIT、偏移量、總記錄數(shù)三者怎么配合分頁是這套系統(tǒng)里最能體現(xiàn)功力的模塊也是BookPageSearchFrm存在的意義。搜索的核心由兩部分組成查詢當(dāng)前頁數(shù)據(jù)以及查詢滿足條件的總條數(shù)。先看查詢當(dāng)前頁數(shù)據(jù)的方法// BookDao中的分頁搜索方法 public ListBook searchBooks(String keyword, int pageNum, int pageSize) { ListBook list new ArrayList(); int offset (pageNum - 1) * pageSize; String sql SELECT b.id, b.bookName, b.author, b.price, t.typeName FROM t_book b LEFT JOIN t_booktype t ON b.typeId t.id WHERE b.bookName LIKE ? LIMIT ?, ?; try (Connection conn DBHelper.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, % keyword %); ps.setInt(2, offset); ps.setInt(3, pageSize); try (ResultSet rs ps.executeQuery()) { while (rs.next()) { Book book new Book(); book.setId(rs.getInt(id)); book.setBookName(rs.getString(bookName)); book.setAuthor(rs.getString(author)); book.setPrice(rs.getDouble(price)); book.setTypeName(rs.getString(typeName)); list.add(book); } } } catch (SQLException e) { e.printStackTrace(); } return list; }LIMIT后面跟兩個參數(shù)第一個是偏移量offset即從第幾條記錄開始取第二個是pageSize即取幾條。偏移量計算公式是(pageNum - 1) * pageSize比如每頁10條第一頁偏移0第二頁偏移10第三頁偏移20。這個公式幾乎每次都會被問到寫死在注釋里。光有當(dāng)前頁數(shù)據(jù)不夠分頁組件還需要知道總條數(shù)才能算出總頁數(shù)。所以需要配合一個COUNT查詢// 獲取符合搜索條件的總記錄數(shù) public int getSearchCount(String keyword) { String sql SELECT COUNT(*) FROM t_book WHERE bookName LIKE ?; try (Connection conn DBHelper.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, % keyword %); try (ResultSet rs ps.executeQuery()) { if (rs.next()) { return rs.getInt(1); } } } catch (SQLException e) { e.printStackTrace(); } return 0; }拿到總記錄數(shù)后總頁數(shù)(總條數(shù)pageSize-1)/pageSize這個向上取整的算法也要能解釋。還有兩個邊界情況要處理好關(guān)鍵詞為空時LIKE %會匹配全部數(shù)據(jù)這不算錯但最好在SQL里做空值判斷頁碼小于1時強(qiáng)制置為1超過最大頁數(shù)時回到最后一頁否則用戶亂點(diǎn)頁碼程序就崩了。5. 高頻率踩坑排查驅(qū)動、連接、亂碼與界面顯示的六個問題5.1 問題運(yùn)行報ClassNotFoundException: com.mysql.jdbc.Driver現(xiàn)象程序一啟動控制臺第一行異常就是找不到驅(qū)動類。原因MySQL驅(qū)動jar包沒有導(dǎo)入項(xiàng)目或者導(dǎo)入的版本驅(qū)動類名不對。解決下載mysql-connector-java并放到IDE的構(gòu)建路徑里如果用的是MySQL 8.0把驅(qū)動類名從com.mysql.jdbc.Driver改成com.mysql.cj.jdbc.Driver。5.2 問題報錯Communications link failure或者Connection refused現(xiàn)象程序啟動后只要一操作數(shù)據(jù)就彈出連接失敗錯誤信息里能看到localhost和3306。原因MySQL服務(wù)沒啟動或者3306端口被占用或者密碼與代碼里DBHelper配置不一致。解決先在命令行執(zhí)行netstat -ano | findstr 3306確認(rèn)端口監(jiān)聽狀態(tài)再用命令行工具mysql -u root -p手動連接一次能連上就說明服務(wù)正常問題在代碼配置重點(diǎn)核對用戶名和密碼連不上就去Windows服務(wù)里啟動MySQL服務(wù)。5.3 問題數(shù)據(jù)庫里的中文全部變成問號現(xiàn)象界面上顯示的中文正常但打開Navicat看表中文全是????或者亂碼。原因建庫時字符集不是utf8或者連接URL里沒加characterEncoding參數(shù)。解決如果還沒導(dǎo)入數(shù)據(jù)回到3.2節(jié)把建庫語句改成DEFAULT CHARSET utf8 COLLATE utf8_general_ci重新建庫如果已有數(shù)據(jù)用ALTER DATABASE book_db CHARACTER SET utf8調(diào)整再確認(rèn)每個表也是utf8。同時檢查DBHelper里URL是否帶上了useUnicodetruecharacterEncodingutf8這三個條件缺一個都會亂碼。5.4 問題報錯Unknown database或Table book_db.t_book doesnt exist現(xiàn)象連接成功但執(zhí)行SQL時報表或庫不存在。原因數(shù)據(jù)庫沒導(dǎo)入成功或者導(dǎo)錯庫了或者連接URL里的庫名和實(shí)際建的庫名不一致。解決反復(fù)確認(rèn)URL里寫的庫名和Navicat左側(cè)的庫名一字不差然后執(zhí)行SHOW TABLES;看看表在不在。很多這種錯誤其實(shí)是3.2節(jié)導(dǎo)SQL時選錯了數(shù)據(jù)庫把腳本導(dǎo)到了系統(tǒng)庫里。5.5 問題不通過登錄直接運(yùn)行MainFrm窗口空白或報空指針現(xiàn)象MainFrm能彈出來但面板是空的按鈕點(diǎn)擊沒反應(yīng)控制臺一堆NullPointerException。原因主界面依賴登錄用戶的上下文信息直接跨過LogOnFrm后很多變量還沒初始化。解決一律從LogOnFrm啟動。如果非要從MainFrm調(diào)試就在MainFrm構(gòu)造方法里臨時寫死一個默認(rèn)用戶對象但交付前要刪掉。5.6 問題界面按鈕錯位、窗口顯示不全現(xiàn)象窗口超出屏幕范圍或者按鈕擠壓在一起。原因不同的屏幕分辨率下Swing的絕對布局不會自適應(yīng)這是所有Swing課設(shè)的通病。解決先調(diào)整窗口尺寸setSize的初始值給主窗口設(shè)一個合理大小并setLocationRelativeTo(null)居中如果按鈕錯位嚴(yán)重考慮用GridBagLayout或BorderLayout替換絕對布局。答辯機(jī)器分辨率一般不會太極端設(shè)成1024x768最穩(wěn)。6. 課設(shè)答辯準(zhǔn)備演示腳本、邊界輸入驗(yàn)證與兩個加分?jǐn)U展很多同學(xué)代碼跑通了就覺得萬事大吉結(jié)果答辯現(xiàn)場演示時手忙腳亂先打開系統(tǒng)再打開數(shù)據(jù)庫然后開始一頓亂點(diǎn)老師還沒看清功能就已經(jīng)點(diǎn)到下一個頁面了。我建議按這個順序走一遍演示腳本熟練后控制在一分半鐘以內(nèi)。第一步啟動LogOnFrm故意輸入一次錯誤密碼展示錯誤提示然后輸入正確密碼登錄。這一步同時驗(yàn)證了登錄功能和容錯能力。第二步進(jìn)入MainFrm先打開BookTypeManageInterFrm新增一個類型比如計算機(jī)或文學(xué)然后回到圖書添加界面用剛建的類型添加一本測試書。第三步切到BookManageInterfrm或分頁搜索頁面用書名關(guān)鍵字搜索剛才添加的書展示分頁效果多翻兩頁看看頁碼變化。第四步選中一條記錄執(zhí)行編輯或刪除操作展示數(shù)據(jù)同步變化。最后正常退出系統(tǒng)。演示的同時主動說邊界輸入驗(yàn)證空用戶名登錄會怎樣、密碼錯誤會怎樣、搜索一個不存在的書會怎樣、價格填負(fù)數(shù)會不會被拒。這些細(xì)節(jié)老師不一定會試但你先說出來印象分會明顯不一樣。兩個加分?jǐn)U展我建議做任何一個都好講第一加一個借閱記錄表新建t_borrow表記錄圖書借出和歸還時間在圖書詳情里顯示在館/已借出狀態(tài)。這是從單表操作跨到多表事務(wù)的第一步技術(shù)上只多了一個Dao和一個面板但業(yè)務(wù)完整性上了一個臺階能順理成章講外鍵和JOIN查詢。第二在圖書列表上加一個導(dǎo)出Excel按鈕用POI把當(dāng)前展示的數(shù)據(jù)寫成一個.xls文件代碼量不大但視覺效果很直觀老師會覺得你不只是會CRUD還考慮到了實(shí)際使用場景。演示前有一個習(xí)慣我從那次課設(shè)之后就一直在堅(jiān)持拿到任何一套源碼先花十分鐘把Dao層的SQL逐條讀一遍確認(rèn)每張表的字段和代碼里的getString參數(shù)能對上再動手運(yùn)行。數(shù)據(jù)庫字段和Java代碼不一致是這類項(xiàng)目最隱蔽的翻車點(diǎn)提前排掉它后續(xù)不會踩進(jìn)黑匣子。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取