經典反面教材代碼
OutputStreamWriter out = ...;
java.sql.Connection conn = ...;
try {
Statement stat = conn.createStatement();
ResultSet rs = stat.executeQuery("select uid, name from user");
while (rs.next()) {
out.println("ID:" + rs.getString("uid") + ",姓名:" + rs.getString("name"));
}
conn.close(); // ⑶
out.close();
} catch (Exception ex) { // ⑵
ex.printStackTrace(); // ⑴、⑷
}
六大核心問題逐一拆解
- 吞噬異常:
printStackTrace() 僅供調試,生產環境必須「處理、轉換、重拋」或「不捕獲」。
- 捕獲過寬:
catch(Exception) 吞並所有異常,應拆分為 SQLException、IOException 等具體類型分塊處理。
- 資源洩漏:正常流程才關閉資源,異常拋出時連接/流未釋放。必須用
finally 或 try-with-resources 保證釋放。
- 錯誤信息貧乏:僅打印堆疊,缺乏業務上下文(如當前處理的用戶 ID、SQL 片段),排查困難。
- try 塊過大:將無關代碼塞入同一 try,導致異常來源模糊。應縮小範圍,精準捕獲。
- 數據不完整輸出:循環中途異常導致輸出截斷,下游系統收到殘缺數據卻無感知。需標記不完整或緩衝後一次性輸出。
重構後的標準範式
try (Connection conn = ...; OutputStreamWriter out = ...) {
try (Statement stat = conn.createStatement();
ResultSet rs = stat.executeQuery("select uid, name from user")) {
while (rs.next()) {
out.println("ID:" + rs.getString("uid") + ",姓名:" + rs.getString("name"));
}
}
} catch (SQLException sqlex) {
out.println("警告:數據不完整");
throw new ApplicationException("讀取數據時出現 SQL 錯誤", sqlex);
} catch (IOException ioex) {
throw new ApplicationException("寫入數據時出現 IO 錯誤", ioex);
}
核心原則速查表
| 原則 | 做法 |
| 捕獲即處理 | 不吞噬,不僅打印堆疊 |
| 精準捕獲 | 多 catch 塊,各司其職 |
| 資源必釋放 | try-with-resources / finally |
| 豐富上下文 | 包裝業務異常,附帶關鍵參數 |
| 縮小 try | 只包含可能拋出目標異常的代碼 |
| 守護數據完整 | 緩衝輸出或標記截斷 |