// C 風格
FILE *fp = fopen(filename, "rw");
if (fp == NULL) {
printf("cannot open file");
exit(0);
}
// 問題:程序員易忘記檢查、錯誤碼語義不明確、調用鏈需層層透傳、無法攜帶上下文信息
Java 異常機制將錯誤處理職責從調用者轉移給方法設計者,強制編譯器檢查受檢異常,確保錯誤不被忽視。
Throwable
├── Error // 嚴重系統錯誤,不應捕獲 (OOM, StackOverflow, ClassCircularityError)
└── Exception
├── RuntimeException (非受檢異常) // 邏輯錯誤:NPE, IndexOutOfBounds, IllegalArgument...
│ └── 子類...
└── 受檢異常 (Checked Exception) // 可恢復業務異常:IOException, SQLException, ClassNotFound...
└── 子類...
核心規則:
throws ExceptionType 或 try-catch 處理。@throws 聲明。Exception (受檢) 或 RuntimeException (非受檢),提供構造器鏈保留堆棧信息。int isLegal(int dt) throws LowZeroException {
if (dt >= 0) return dt; // 正常出口:返回業務值
throw new LowZeroException(); // 異常出口:拋出異常對象
}
對比 C:return -1 混淆業務值與錯誤碼。Java **類型系統層面分離正常返回類型與異常類型**,調用者無法誤將異常當作正常值使用。
int myMethod(int dt) {
int data = 0;
try {
data = isLegal(dt); // 可能拋出 LowZeroException
} catch (LowZeroException e) {
System.out.println("發生數據錯誤!");
e.printStackTrace(); // 打印異常傳播鏈
} finally {
// 清理資源,無論是否異常必執行
}
return data;
}
int myMethod2(int dt) throws LowZeroException {
return myMethod(dt); // 未捕獲,聲明 throws 向上傳播
}
public static void main(String[] args) {
try {
myMethod2(-1);
} catch (LowZeroException e) {
e.printStackTrace();
}
}
// 輸出堆棧軌跡:
// LowZeroException
// at Example.isLegal(Example.java:10)
// at Example.myMethod(Example.java:20)
// at Example.myMethod2(Example.java:30)
// at Example.main(Example.java:40)
關鍵點:
catch 或到達線程入口 (main/run) 由 JVM 默認處理 (打印堆棧並終止線程)。catch 塊是異常對象的**生命終結點**,處理後異常對象可被 GC。printStackTrace() 記錄完整傳播路徑,包含方法名、文件名、行號,**是線上排查核心線索**。| 策略 | 代碼形式 | 適用場景 |
|---|---|---|
| 就地處理 | try { risky() } catch (Ex e) { handle } | 知曉如何恢復、有備選邏輯、邊界方法 (Controller、Command) |
| 向上傳播 | void foo() throws Ex { risky(); } | 當前層無法處理、需上層統一決策、框架統一異常處理器攔截 |