Java 透過面向對象方式處理運行時錯誤。所有異常繼承自 java.lang.Throwable,分為兩大分支:
Throwable
├── Error (嚴重錯誤,程式無法恢復,如 OutOfMemoryError、StackOverflowError)
└── Exception (異常,程式可捕獲處理)
├── RuntimeException (運行時異常/非檢查異常,編譯器不強制處理)
│ ├── NullPointerException
│ ├── ArrayIndexOutOfBoundsException
│ ├── IllegalArgumentException
│ └── ...
└── Checked Exception (檢查異常/非運行時異常,必須捕獲或聲明拋出)
├── IOException
├── SQLException
├── ClassNotFoundException
└── ...
ErrorRuntimeExceptionthrow 業務異常(屬檢查異常),通知調用方處理catch 塊 → 執行處理邏輯try {
// 可能拋出異常的代碼
} catch (ExceptionType1 e1) {
// 處理 ExceptionType1 及其子類
} catch (ExceptionType2 e2) {
// 處理 ExceptionType2 及其子類
} finally {
// 無論是否異常、是否捕獲、是否 return,都會執行(極少數例外:System.exit、JVM 崩潰)
}
| 場景 | 執行順序 |
|---|---|
| try 正常結束 | try → finally → 後續代碼 |
| try 拋出異常,catch 匹配 | try(中斷) → catch → finally → 後續代碼 |
| try 拋出異常,catch 不匹配 | try(中斷) → finally → 向上拋給調用者 |
| catch 內再拋異常 | try → catch(中斷) → finally → 向上拋新異常 |
| finally 內拋異常 | 原異常被「吞沒」,新異常向上拋(需極度謹慎) |
catch (IOException | SQLException e)AutoCloseable 的資源,首選方案// 自動關閉 inputStream、reader、bufferedReader,無需 finally
try (InputStream is = new FileInputStream("data.txt");
InputStreamReader isr = new InputStreamReader(is, StandardCharsets.UTF_8);
BufferedReader br = new BufferedReader(isr)) {
String line;
while ((line = br.readLine()) != null) {
process(line);
}
} catch (IOException e) {
log.error("讀取失敗", e);
throw new BusinessException("讀取配置失敗", e);
}
public void readConfig(String path) throws IOException, ParseException {
// 方法內可能拋出的檢查異常必須聲明(Error、RuntimeException 除外)
}
try-catch 或繼續 throwsthrows Exception 破壞異常語義public void setAge(int age) {
if (age < 0 || age > 150) {
throw new IllegalArgumentException("年齡必須在 0-150 之間,實際: " + age);
}
this.age = age;
}
// 業務檢查異常(需捕獲)
public class BusinessException extends Exception {
private final String errorCode;
public BusinessException(String errorCode, String message) {
super(message);
this.errorCode = errorCode;
}
public BusinessException(String errorCode, String message, Throwable cause) {
super(message, cause);
this.errorCode = errorCode;
}
// getter...
}
// 業務運行時異常(不強制捕獲,適合控制器/服務層向上拋)
public class BusinessRuntimeException extends RuntimeException {
// 結構同上,繼承 RuntimeException
}
cause,勿 new Exception(e.getMessage()) 丟失堆疊| 反模式 | 問題 | 修正 |
|---|---|---|
catch (Exception e) {} 吞沒異常 | 錯誤無聲失效,極難排查 | 至少記錄日誌,或轉拋業務異常 |
throw new Exception("msg") 丟失原始堆疊 | 根因不可追溯 | throw new BizException("msg", e) |
| 用異常控制正常業務流程 | 效能差、可讀性差 | 用條件判斷、Optional、Result 模式 |
throws Exception 一籮筐 | 調用者無法針對性處理 | 精確聲明具體檢查異常 |
| finally 內拋出異常 | 掩蓋原始異常 | finally 內再 try-catch 記錄日誌 |
try-catch 處理並恢復(如重試、降級、預設值)throws 精確聲明拋出RuntimeException 向上拋,由全局異常處理器統一捕獲、記錄、返回友好錯誤try-with-resources 或 finally 釋放Java 異常機制的核心價值在於強制錯誤顯性化與資源安全釋放。掌握 檢查異常 vs 非檢查異常 的選型邏輯、try-with-resources 的資源管理優勢、異常鏈保留 的排查關鍵、以及全局統一處理 的架構模式,即可構建健壯、可維護、可觀測的 Java 應用。