異常處理是程式設計中至關重要且極具挑戰性的一環。從 C 語言時代的 if...else... 手動錯誤檢查,到 Java 內建的結構化異常處理框架,開發體驗有了質的飛躍。Java 將所有異常統一以類別形式表達,透過繼承體系區分不同錯誤類型,並定義了嚴謹的處理規範。自 1.4 版本引入異常鏈機制後,錯誤追蹤更加直觀高效。本文將系統梳理 Java 異常核心知識,助你構建健壯、可維護的程式碼。
並非所有錯誤都是異常。語法錯誤(如缺少分號)屬於 java.lang.Error,編譯期即可發現;邏輯錯誤(如除數為零)則拋出 java.lang.ArithmeticException,屬於運行時異常。開發中應優先預防錯誤,對不可避免的異常採取針對性處理。
Java 以物件封裝異常資訊。所有異常類別繼承自 Throwable,分為兩大來源:
NullPointerException、IOException 等Exception 或其子類別實現業務邏輯異常throw 關鍵字,常用於向呼叫者通報業務異常Java 異常處理圍繞 try、catch、throw、throws、finally 五大關鍵字展開:
try {
// 監控區塊:可能拋出異常的程式碼
} catch (ExceptionType1 e1) {
// 處理異常類型 1
} catch (ExceptionType2 e2) {
// 處理異常類型 2
} finally {
// 無論是否發生異常,必須執行的清理邏輯
}
關鍵特性:
catch 依序匹配,僅執行首個匹配區塊getMessage()、printStackTrace() 等方法finally 在 return 前執行,僅 System.exit() 可阻止其執行java.lang.Object
└── java.lang.Throwable
├── java.lang.Error // 嚴重錯誤,程式無法恢復(如 OutOfMemoryError)
│ └── java.lang.ThreadDeath
└── java.lang.Exception // 程式可處理的異常
├── java.lang.RuntimeException // 運行時異常(非受檢)
│ ├── NullPointerException
│ ├── ArithmeticException
│ └── ...
└── IOException // 受檢異常
├── FileNotFoundException
└── ...
| 類別 | 說明 | 編譯器檢查 |
|---|---|---|
Throwable | 所有錯誤/異常的超類別 | - |
Error | 嚴重系統錯誤,不應捕獲處理 | 不檢查 |
Exception | 應用程式合理捕獲的條件 | 受檢異常需處理 |
RuntimeException | 運行期常見錯誤,可透過改進程式碼避免 | 不檢查 |
ThreadDeath | Thread.stop() 觸發,捕獲後必須重新拋出 | - |
try-catch-finally 完整處理throws,內部用 throw 拋出,由呼叫者處理若層層拋出至 main() 仍無處理器,JVM 將:
printStackTrace() 輸出完整棧資訊原則:越早處理異常,資源消耗越小,影響範圍越窄。切勿將可自行處理的異常拋給呼叫者。
finally 區塊鐵律return 執行,位置無關finally 中修改返回值或使用 returnSystem.exit()try 不能單獨存在,三種合法組合:try-catch-finally、try-catch、try-finallycatch 僅執行首個匹配項(含子類別匹配)throw 後不可有其他語句try {
// IO 操作
} catch (IOException e) {
// 處理 IO 異常
} catch (SQLException e) {
// 處理 SQL 異常
}
public class BusinessException extends Exception {
private final int errorCode;
public BusinessException(String message, int errorCode) {
super(message);
this.errorCode = errorCode;
}
public int getErrorCode() { return errorCode; }
}
將底層技術異常轉為業務語義異常,提升可讀性:
public void transferFunds(Account from, Account to, BigDecimal amount) throws BusinessException {
try {
dao.updateBalances(from, to, amount);
} catch (SQLException e) {
log.error("資料庫異常", e);
throw new BusinessException("轉帳失敗:資料庫錯誤", ErrorCode.DB_ERROR);
}
}
// ❌ 無意義捕獲再拋出
try {
// ...
} catch (MyException e) {
throw e; // 直接 throws MyException 即可
}
| 維度 | 運行時異常 (RuntimeException 及子類) | 受檢異常 (其他 Exception 子類) |
|---|---|---|
| 編譯器檢查 | 不強制捕獲/聲明 | 必須 try-catch 或 throws |
| 典型範例 | NullPointerException、ArithmeticException、IndexOutOfBoundsException | IOException、SQLException、ClassNotFoundException |
| 錯誤性質 | 程式邏輯缺陷,應修正程式碼避免 | 外部環境不可控因素,需預案處理 |
| 處理建議 | 修正 Bug,必要時捕獲防崩潰 | 業務邏輯中妥善處理或向上拋出 |
public class SafeCalculator {
public static void main(String[] args) {
System.out.println("程式啟動");
try {
int result = divide(10, 0);
System.out.println("結果:" + result);
} catch (ArithmeticException e) {
System.err.println("捕獲除零異常:" + e.getMessage());
}
System.out.println("程式繼續執行,未因異常終止");
}
static int divide(int a, int b) {
return a / b; // b=0 時拋出 ArithmeticException
}
}
Error 及子類(如 OutOfMemoryError、StackOverflowError、VirtualMachineError)代表 JVM 層面災難,程式無法恢復,不建議捕獲,亦不建議自訂 Error 子類。開發重點應放在 Exception 體系。
總結:掌握異常體系、善用 try-catch-finally、合理區分受檢/非受檢異常、設計語義清晰的自訂異常、堅持資源在 finally 中釋放,是寫出健壯 Java 程式的基石。