本文摘自《編寫高質量代碼:改善 Java 程序的 151 個建議》第 8 章,聚焦異常處理四大核心建議,結合源碼分析、反模式識別與最佳實踐,幫助開發者建立正確的異常觀。
class Person {
public Person(int age) {
if (age < 18) throw new RuntimeException("未成年");
}
}
Person p = new Person(17); // 編譯通過,運行崩潰,調用方無感知
調用方以為對象創建成功,實際引用為 null,後續調用拋 NPE,極難排查。class Base { public Base() throws IOException { throw new IOException(); } }
class Sub extends Base { public Sub() throws Exception { } } // 編譯錯誤:子類異常範圍比父類寬
// 構造器無覆寫概念,子類必須聲明拋出父類所有異常 + 自己的異常
// 導致裡氏替換原則失效,靈活性大幅降低
更糟的是,子類無法在 super() 之後處理父類異常(語法要求 super() 必須首行)。static Person create(int age) { if(age<18) throw...; return new Person(age); }init() 拋異常Person.builder().age(17).build() 內部校驗public Throwable() {
fillInStackTrace(); // 本地方法,捕獲當前線程棧幀
}
public StackTraceElement[] getStackTrace() { ... }
class Foo {
public static boolean method() {
StackTraceElement[] sts = new Throwable().getStackTrace();
for (StackTraceElement st : sts) {
if ("methodA".equals(st.getMethodName())) return true;
}
return false; // 或拋異常阻止非預期調用
}
}
class Invoker {
public static void methodA() { System.out.println(Foo.method()); } // true
public static void methodB() { System.out.println(Foo.method()); } // false
}
// 判斷枚舉是否包含某值
public static > boolean contains(Class clz, String name) {
try { Enum.valueOf(clz, name); return true; }
catch (RuntimeException e) { return false; } // 濫用異常控制流
}
public static > boolean contains(Class clz, String name) {
for (T t : clz.getEnumConstants()) {
if (t.name().equals(name)) return true;
}
return false;
}
// 反模式:依賴異常
FileInputStream fis = new FileInputStream(file); // 可能拋 FileNotFoundException
// 正確:先判斷
if (file.exists() && !file.isDirectory()) {
try { FileInputStream fis = new FileInputStream(file); }
catch (FileNotFoundException e) { ... } // 理論不會發生,作為雙重保險
}
fillInStackTrace)用例:用戶登錄
主流程:輸入帳號密碼 → 驗證成功 → 進入系統
異常流程:
E1: 帳號不存在 → 提示 "帳號錯誤"
E2: 密碼錯誤 → 提示 "密碼錯誤"
E3: 連續 3 次失敗 → 鎖定帳號 30 分鐘
// 代碼直接映射
public void login() {
try { /* 正常驗證 */ }
catch (InvalidAccountException e) { /* E1 */ }
catch (InvalidPasswordException e) { /* E2 */ }
catch (TooManyAttemptsException e) { /* E3 */ }
}
| 鐵律 | 核心內容 |
|---|---|
| 構造器不拋異常 | 改用工廠/構建器/初始化方法 |
| 善用棧跡蹤 | 實現調用者感知、安全防護、動態行為 |
| 異常只做異常事 | 業務判斷用 if/for,預防性檢查前置 |
| 性能不是藉口 | 毫秒級開換取架構清晰、排查高效、類型安全 |
異常機制是 Java 面向對象錯誤處理的基石,正確使用它,是從「寫出能跑的代碼」邁向「寫出可維護、可診斷、健壯的系統」的關鍵一步。