Throwable
├── Error (嚴重錯誤,程序無法恢復)
│ ├── OutOfMemoryError
│ ├── StackOverflowError
│ └── NoClassDefFoundError
└── Exception (可恢復異常)
├── RuntimeException (非受檢異常,編譯器不強制處理)
│ ├── NullPointerException
│ ├── IllegalArgumentException
│ └── ArithmeticException
└── 受檢異常 (編譯器強制 try-catch 或 throws)
├── IOException
├── SQLException
└── ClassNotFoundException
public class ExceptionDemo {
public static int divide(int a, int b) {
return a / b; // 可能拋出 ArithmeticException
}
public static void main(String[] args) {
try {
int result = divide(5, 0); // 異常拋出點
System.out.println("Result: " + result); // 不會執行
} catch (ArithmeticException e) {
e.printStackTrace(); // 打印堆疊追蹤
System.out.println("捕獲除零異常: " + e.getMessage());
}
System.out.println("程序繼續執行..."); // 正常繼續
}
}
關鍵點:異常拋出後,當前方法剩餘代碼**不再執行**,直接跳轉到匹配的 catch 塊。
try {
riskyOperation();
return "success";
} catch (Exception e) {
log.error("失敗", e);
return "error";
} finally {
cleanup(); // 無論有無異常、有無 return,都會執行
}
唯一不執行情況:System.exit()、JVM 崩潰、無限循環/死鎖在 try/catch 中。
// 錯誤:編譯失敗,ArithmeticException 被 Exception 遮蔽
try { ... }
catch (Exception e) { ... }
catch (ArithmeticException e) { ... }
// 正確:具體異常優先
try { ... }
catch (ArithmeticException e) { ... }
catch (IOException e) { ... }
catch (Exception e) { ... } // 兜底
// 強制調用者處理
public void readFile(String path) throws IOException {
Files.readAllLines(Paths.get(path)); // 拋出 IOException
}
// 鏈式傳播
public void process() throws IOException {
readFile("data.txt"); // 不捕獲,繼續拋出
}
// 最終由 JVM 處理(打印堆疊並終止)
public static void main(String[] args) throws Exception {
process();
}
// 業務異常:繼承 RuntimeException(非受檢)或 Exception(受檢)
public class InsufficientBalanceException extends RuntimeException {
private final BigDecimal balance;
private final BigDecimal required;
public InsufficientBalanceException(BigDecimal balance, BigDecimal required) {
super(String.format("餘額不足:當前 %s,需 %s", balance, required));
this.balance = balance;
this.required = required;
}
// 提供業務數據供上層決策
public BigDecimal getBalance() { return balance; }
public BigDecimal getRequired() { return required; }
}
try { ... }
catch (IOException | SQLException e) { // 同一塊處理多種異常
log.error("IO/DB 錯誤", e);
}
// 實現 AutoCloseable 的資源自動關閉,即使異常也安全
try (FileInputStream fis = new FileInputStream("file.txt");
BufferedReader br = new BufferedReader(new InputStreamReader(fis))) {
return br.readLine();
} // 編譯器自動生成 finally 關閉代碼
try (Resource r = new Resource()) {
throw new Exception("主異常");
} catch (Exception e) {
// e.getSuppressed() 獲取資源關閉時拋出的被抑制異常
for (Throwable t : e.getSuppressed()) {
log.warn("被抑制異常", t);
}
}
| 原則 | 說明 |
|---|---|
| 具體捕獲 | 捕獲具體異常類,避免 catch (Exception) |
| 異常轉換 | 底層異常包裝為業務異常,保留原因鏈 new BizEx(cause) |
| 不吞沒異常 | 空 catch 塊是大忌,至少記錄日誌 |
| 資源安全 | IO/連接/鎖 必用 try-with-resources |
| 文檔異常 | Javadoc @throws 標註方法可能拋出的異常 |
| 失敗快速 | 參數驗證失敗立即拋出 IllegalArgumentException |
| 區分類型 | 編程錯誤用 RuntimeException,環境/業務錯誤用受檢異常 |