Java 異常處理機制強大而微妙,掌握其流向與最佳實踐,是寫出可維護、可診斷、生產級代碼的必修課。本文系統梳理四大核心處理模式、三大常見反模式,並提煉四條黃金法則。
public void methodA() {
try {
methodB(); // 調用內層方法
} catch (ExceptionType e) {
// 統一處理
}
}
public void methodB() throws ExceptionType {
if (condition) {
// 正常邏輯
} else {
throw new ExceptionType(args); // 顯式拋出
}
}
機制:JVM 沿調用棧向上尋找匹配的 catch 塊。methodA 捕獲並處理 methodB 拋出的異常。適用場景:內層無法/不應決定如何處理,外層擁有完整上下文判斷力時。
public void methodA() {
try {
// 可能拋異常的操作
} catch (ExceptionType e) {
// 當前方法內完整處理
}
}
機制:try-catch 封裝風險代碼,異常不向外傳播。適用場景:當前層級擁有足夠信息完成處理(重試、降級、記錄、轉換為返回值)。
public void methodB() throws ExceptionType {
try {
// 風險代碼
} catch (AnotherExceptionType e) {
// 處理 AnotherExceptionType,不向外拋
}
// 若產生 ExceptionType,無 catch 捕獲 → 向外拋出
}
關鍵點:方法簽名聲明拋出的異常類型,決定了哪些異常可向外傳播。未聲明的檢查型異常若在 try 中產生且未捕獲,編譯報錯。
public void methodB() throws ExceptionType {
try {
// 風險代碼
} finally {
// 必須執行的清理:關閉連接、釋放鎖、寫日誌
}
}
機制:異常拋出前,finally 塊保證執行。外層捕獲異常後繼續處理。核心價值:資源釋放不因異常中斷而遺漏。
public void methodB() throws ExceptionType {
try {
// ...
} catch (ExceptionType e) {
// 捕獲了自己聲明拋出的異常!
}
}
後果:外層永遠收不到該異常,throws 聲明形同虛設,破壞異常傳播鏈,隱藏錯誤上下文。
public Object methodB() {
try {
return SOME_VALUE;
} finally {
return ANOTHER_VALUE; // 覆蓋 try 的返回值!
}
}
後果:finally 的 return 會覆蓋 try/catch 的返回值,導致正常流程結果丟失,極難排查。鐵律:finally 塊中絕不使用 return/throw/break/continue 等改變控制流的語句。
try {
// ...
} catch (Exception e) { // 父類在前
// 所有異常都被捕獲在這裡
} catch (SubException e) { // 永遠執行不到
// ...
}
後果:Exception 是所有檢查型異常父類,置前導致後續具體 catch 塊永遠不可達,編譯器會報錯「unreachable catch block」。鐵律:catch 塊按從具體到通用順序排列,catch (Exception) 或 catch (Throwable) 必須置於最後。
throw new BusinessException("訂單創建失敗", e)),保留原始堆棧。Exception)可合併處理多異常,但絕不使用空 catch 塊吞噬異常。至少記錄日誌、拋出運行時異常、或轉換為錯誤碼返回。異常處理不是「try-catch 套路」,而是錯誤信息流向的架構設計。清晰的責任劃分(誰拋出、誰處理、誰包裝、誰記錄)、嚴謹的控制流防護、語義豐富的異常體系,共同構成系統可觀測性與可恢復性的基石。