許多開發者習慣在異常情況下返回 null(如 Iterator.next()、Map.get()),而非拋出異常。調用者若未顯式檢查 null,隨後解引用即觸發 NPE。這種「用 null 當標誌」的做法稱為 Null Flag 錯誤模式。
實現 Iterator 接口時,next() 捕獲 IOException 後返回 null。但 Iterator 規範要求無元素時拋出 NoSuchElementException。調用者依賴規範編寫代碼,遇到 null 而非異常,極易導致 NPE,且堆棧無法指示真正錯誤源頭(IO 異常被吞噬)。
public Object next() {
try {
String result = internal.readLine();
if (result == null) throw new NoSuchElementException();
return result;
} catch (IOException e) {
throw new NoSuchElementException(e.toString()); // 保留原始異常信息
}
}
優勢:
1. 符合接口契約,調用者可正確捕獲異常。
2. 異常堆棧直指故障點,保留 IO 異常上下文。
3. 編譯器強制調用者處理檢查型異常(若改為檢查型),更早發現問題。
誤以為「不拋異常更健壯」。實則異常機制正是為了將控制權快速傳遞至合適處理點,而 null 只是將錯誤隱藏在普通返回值中,導致診斷極難。原則:盡早拋出異常,僅從非錯誤性異常中恢復。
對 Hashtable.get()、BufferedReader.readLine() 等不可變更的 API,調用前必須顯式檢查(如 containsKey())。但這仍是權宜之計,根治之道是在自有代碼中堅決杜絕 Null Flag。