Java 異常處理基於異常對象與拋出-捕獲模型。發生異常時創建異常對象,沿調用棧向上傳播,直到遇到匹配的 catch 塊或導致線程終止。
Throwable
├── Error (錯誤) - JVM 內部/資源耗盡,不可恢復
│ ├── OutOfMemoryError
│ ├── StackOverflowError
│ └── ...
└── Exception (異常)
├── RuntimeException (運行時異常) - 邏輯錯誤,可避免
│ ├── NullPointerException
│ ├── IllegalArgumentException
│ ├── IndexOutOfBoundsException
│ └── ...
└── Checked Exception (受檢異常) - 環境/外部因素,必須處理
├── IOException
├── SQLException
├── ClassNotFoundException
└── ...
| 類別 | 處理方式 | 示例 |
|---|---|---|
| Error | 直接讓程序崩潰,記錄日誌,人工介入 | OOM、StackOverflow |
| RuntimeException | 修正代碼邏輯預防,一般不捕獲 | NPE、越界、類轉換 |
| Checked Exception | 必須捕獲或聲明拋出,編譯器強制 | IO、SQL、網絡 |
try {
// 可能拋出異常的代碼
riskyOperation();
} catch (IOException e) {
// 處理 IO 異常
log.error("IO 失敗", e);
throw new BusinessException("數據讀取失敗", e);
} catch (SQLException e) {
// 處理 SQL 異常
log.error("數據庫錯誤", e);
throw new BusinessException("數據庫操作失敗", e);
} finally {
// 清理資源,無論是否異常都執行
closeResources();
}
catch 按聲明順序匹配,子類在前,父類在後finally 必執行 (除非 System.exit()、JVM 崩潰、無限循環)return 在 finally 中會覆蓋 try/catch 的返回值
// 保留原始堆棧信息
try {
Files.readAllLines(path);
} catch (IOException e) {
// 包裝為業務異常,保留原因鏈
throw new DataAccessException("讀取配置文件失敗: " + path, e);
}
// 自定義異常
public class DataAccessException extends RuntimeException {
public DataAccessException(String message, Throwable cause) {
super(message, cause);
}
}
原則:不丟棄原始異常,通過構造函數 cause 參數保留完整堆棧鏈。
| 關鍵字 | 位置 | 作用 |
|---|---|---|
| throw | 方法體內 | 拋出具體異常對象 |
| throws | 方法簽名 | 聲明可能拋出的異常類型 |
public void readConfig(String path) throws IOException {
if (path == null) {
throw new IllegalArgumentException("路徑不能為空"); // throw
}
Files.readAllLines(Paths.get(path)); // 可能拋 IOException,由 throws 聲明
}
RuntimeException (非受檢),減少樣板代碼Exception (受檢),強制調用者處理cause 的構造函數
public class BusinessException extends RuntimeException {
private final String errorCode;
private final Map context;
public BusinessException(String errorCode, String message, Throwable cause) {
super(message, cause);
this.errorCode = errorCode;
this.context = new HashMap<>();
}
public BusinessException addContext(String key, Object value) {
context.put(key, value);
return this;
}
}
// 使用
throw new BusinessException("USER_NOT_FOUND", "用戶不存在", e)
.addContext("userId", userId)
.addContext("operation", "queryProfile");
try {
// ...
} catch (IOException | SQLException | TimeoutException e) {
log.error("基礎設施異常", e);
throw new InfrastructureException(e);
}
// 實現 AutoCloseable 的資源自動 close
try (Connection conn = dataSource.getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
ResultSet rs = ps.executeQuery()) {
// 使用資源
} // 自動按相反順序 close,即使發生異常
| 反模式 | 問題 | 正確做法 |
|---|---|---|
| catch (Exception e) {} | 吞沒異常,難以排查 | 具體捕獲,或重新拋出 |
| catch (Exception e) { log.error(e); } | 丟失堆棧,僅記錄信息 | log.error("msg", e) 保留堆棧 |
| 用異常控制業務流程 | 性能差、語義不清 | 用條件判斷、Optional、Result 模式 |
| 在 finally 中拋出異常 | 掩蓋 try 中的原始異常 | finally 中捕獲並記錄,不拋出 |
| 過度使用 Checked Exception | 污染方法簽名,增加耦合 | 業務異常用 RuntimeException |
causetry-with-resources 管理所有 AutoCloseableRuntimeException 子類,攜帶錯誤碼/上下文log.error("上下文信息", e) 完整堆棧@throws 說明拋出條件Java 異常處理是錯誤信息傳遞與資源安全釋放的雙重機制。理解 Error/RuntimeException/Checked Exception 三層體系、掌握 try-catch-finally/try-with-resources 語法、遵循「具體捕獲、保留鏈、非受檢業務異常」原則,能構建健壯、可維護、可診斷的系統。異常不是錯誤的終點,而是系統狀態變化的信號——正確處理它們,讓錯誤可見、可控、可恢復。