異常處理是 Java 核心機制,卻常被濫用或忽視。本文劃分好、壞、丑三個層次,深度剖析 Checked/Unchecked 異常選型邏輯、捕獲策略、日誌記錄與 API 設計原則,助你寫出健壯、可維護的異常處理代碼。
| 分類 | 繼承體系 | 編譯器檢查 | 典型代表 | 處理義務 |
|---|---|---|---|---|
| Checked 異常 | Exception(非 RuntimeException) | ✅ 強制 catch 或 throws | IOException、SQLException、ParseException | 調用者必須處理 |
| Unchecked 異常 | RuntimeException、Error | ❌ 不強制 | NullPointerException、IllegalArgumentException、OutOfMemoryError | 屬於編程錯誤或系統級故障,應修代碼而非捕獲 |
public Image loadImage(String path) throws IOException, MalformedURLException {
// 調用者明知可能失敗,必須處理
}
try {
FileInputStream fis = new FileInputStream(file);
// 業務邏輯
} catch (FileNotFoundException e) {
// 具體處理:記錄日誌、返回默認圖、拋出業務異常
log.warn("配置文件缺失,使用默認配置", e);
return loadDefaultImage();
}
public class BusinessException extends Exception {
private final ErrorCode code;
public BusinessException(ErrorCode code, String msg, Throwable cause) {
super(msg, cause);
this.code = code;
}
}
// Service 層轉換
try {
dao.insert(order);
} catch (SQLException e) {
throw new BusinessException(ErrorCode.DB_ERROR, "訂單保存失敗", e);
}
log.error("訂單支付失敗,orderId={}", orderId, e); // 佔位符避免字符串拼接開銷
// ❌ 反模式:繼承 RuntimeException 只為不寫 throws
public void save(User u) {
if (u.getName() == null) throw new MyRuntimeException("名稱不能為空");
}
後果:調用者不知可能拋異常,運行時才暴雷,違背「失敗快速」原則。
// ❌ 反模式:捕獲 Throwable/Exception 只打印堆棧
try { risky(); } catch (Throwable t) { t.printStackTrace(); }
後果:異常被靜默吞噬,上層不知失敗,導致數據不一致、靜默 Bug。
// ❌ 反模式:每行代碼包一層 try-catch
try { a(); } catch (Exception e) { log(e); }
try { b(); } catch (Exception e) { log(e); }
建議:將邏輯完整的業務單元包在一個 try 塊中,失敗即整體回滾。
// ❌ 丑陋:大量創建異常對象佔用堆內存
for (int i=0; i<10000; i++) throw new MyException("msg" + i);
異常對象創建昂貴(填充堆棧追蹤),高頻拋擲會觸發 Full GC 甚至 OOM。
// ❌ 隱患:close() 拋異常導致原始異常丟失(Java 7+ try-with-resources 已解決)
InputStream is = null;
try { is = new FileInputStream(f); }
finally { if (is != null) is.close(); }
JVM 內存耗盡屬於 Error,非 Checked,但高內存壓力場景必須主動監控並處理(如緩存淘汰、降級熔斷)。
Exception(Checked)或 RuntimeException(Unchecked),攜帶錯誤碼、上下文信息。try-with-resources 或 Suppressed 機制)。好的異常處理是契約:方法簽名誠實告知風險,調用者知情處理,日誌留存證據。壞的是掩蓋:catch-all、濫用 RuntimeException、過度細粒度。丑的是設計缺陷:異常對象濫創、資源洩漏、原始異常丟失。遵循黃金法則,異常即成為系統健壯性的基石。