異常處理是 Java 程序健壯性的基石。本文系統講解默認異常處理器、try-catch-finally 完整語法、多重 catch 順序規則、嵌套 try 與方法調用隱式嵌套、finally 執行保證等核心機制,附帶字節碼級流程圖與最佳實踐。
public class UncaughtDemo {
public static void main(String[] args) {
int a = 1 / 0; // 拋出 ArithmeticException
}
}
// 控制台輸出:
// Exception in thread "main" java.lang.ArithmeticException: / by zero
// at UncaughtDemo.main(UncaughtDemo.java:3)
UncaughtExceptionHandler,默認處理器打印堆棧並終止線程(主線程終止 → JVM 退出)。try {
// 可能拋異常的代碼
riskyOperation();
} catch (IOException | SQLException e) { // JDK 7+ 多異常合併捕獲
// 處理異常
log.error("IO/DB 錯誤", e);
throw new RuntimeException(e); // 可轉型拋出
} catch (Exception e) { // 父類在後
log.error("其他錯誤", e);
} finally {
// 無論是否異常、是否 return、是否異常中異常,**必執行**(極少數除外)
releaseResources();
}
try {
A(); // 正常
B(); // 拋出 Exception
C(); // 跳過
} catch (Exception e) {
D(); // 捕獲塊
} finally {
E(); // finally 塊
}
F(); // 後續代碼
// 實際執行順序:A → B → [異常拋出] → D → E → F
// 若 catch 中 return:A → B → D → E → [返回]
// 若 try 中 return:A → B → [異常] → D → E → [返回]
// 若 finally 中 return/throw:**覆蓋** try/catch 的返回/異常(極度不推薦)
子類異常必須在父類異常之前捕獲,否則編譯錯誤「unreachable catch block」。
try { ... }
catch (FileNotFoundException e) { } // 子類:先捕獲
catch (IOException e) { } // 父類:後捕獲
// catch (Exception e) { } // 更上層父類
// catch (Throwable e) { } // 頂層
try {
// 外層邏輯
try {
// 內層高風險操作
parseInput();
} catch (ParseException e) {
// 內層處理:轉換為業務異常
throw new BusinessException("輸入格式錯誤", e);
}
} catch (BusinessException e) {
// 外層統一處理
}
void outer() {
try {
inner(); // 內層方法有自己的 try-catch
} catch (Exception e) { ... }
}
void inner() {
try { risky(); }
catch (SpecificException e) { handle(); } // 捕獲處理
// 若 inner 不捕獲,異常自動傳播給 outer
}
異常傳播規則:若當前 catch 不匹配,異常沿調用棧向上尋找匹配的 catch,直到線程頂層或 JVM 默認處理器。
| 場景 | finally 是否執行 |
|---|---|
| 正常返回 | ✅ 是 |
| catch 中 return/throw | ✅ 是 |
| try 中 return/throw | ✅ 是(在返回/拋出前執行) |
System.exit(0) | ❌ 否(JVM 直接終止) |
| 守護線程中 JVM 退出 | ❌ 否(非守護線程結束 JVM 退出) |
| 死循環/死鎖 | ❌ 否(線程不再推進) |
| finally 中 throw/return | ⚠️ 執行但**覆蓋**外層異常/返回(極度危險) |
// 自動關閉實現 AutoCloseable 的資源,順序:後聲明先關閉
try (Connection conn = DriverManager.getConnection(url);
PreparedStatement ps = conn.prepareStatement(sql);
ResultSet rs = ps.executeQuery()) {
while (rs.next()) { ... }
} // 此處自動調用 rs.close() → ps.close() → conn.close()
// 等價於手寫 finally 但更簡潔、異常抑制機制更完善
SQLException),業務層轉業務異常(BusinessException),控制層統一處理返回友好提示。catch (Exception e) { } 禁止;至少記錄日誌 log.error("", e)。throw new BizEx("msg", cause) 保留原始堆棧。RuntimeException 子類)。異常處理不是「寫幾個 catch 就完事」,而是設計異常體系、規範傳播路徑、保證資源釋放的系統工程。內化本文流程圖與最佳實踐,能讓你的代碼在風雨中依然穩健前行。