資深開發者總結的防禦性編程精華,涵蓋空指針預防、早期 JDK API 陷阱、魔術數字消除、意外賦值規避、集合安全檢查、final 設計原則,助你寫出健壯、可維護的生產級代碼。
// ❌ 危險:variable 為 null 拋 NPE
if (variable.equals("literal")) { ... }
// ✅ 安全:字面量呼叫 equals,null 安全返回 false
if ("literal".equals(variable)) { ... }
原則:`equals`、`compareTo` 等非靜態方法,已知不為 null 的物件(字面量、常數、枚舉)放左邊。
// ❌ File.list():目錄回傳陣列,非目錄/IO錯誤回傳 null
String[] files = dir.list();
for (String f : files) { ... } // 可能 NPE
// ✅ 防禦性檢查
File[] files = dir.listFiles(); // 回傳 File[],空目錄回傳空陣列而非 null
if (files != null) {
for (File f : files) { ... }
}
// 或用 Files.list(Path) (NIO.2) 回傳 Stream,更現代
// ❌ 硬編碼 -1:indexOf 未找到、read() 結束
if (str.indexOf("x") != -1) { ... }
while ((b = in.read()) != -1) { ... }
// ✅ 定義語義常數
private static final int NOT_FOUND = -1;
private static final int EOF = -1;
if (str.indexOf("x") != NOT_FOUND) { ... }
while ((b = in.read()) != EOF) { ... }
效益:語義自文檔化、未來 JDK 變更僅需修改常數定義、IDE 重構安全。
// ❌ 易手滑寫成 =:意外賦值且條件永遠為 true
if (x = 5) { ... }
// ✅ Yoda 條件:常數在左,誤寫 = 編譯報錯
if (5 == x) { ... }
// ✅ 現代 IDE + 編譯器警示:-Xlint:assignment 或 SonarQube 規則
// ❌ 僅檢查 null,空集合仍進入迴圈浪費
if (list != null) { for (Item i : list) ... }
// ✅ 標準防禦模式
if (list != null && !list.isEmpty()) { ... }
// 或 Java 8+
if (CollectionUtils.isNotEmpty(list)) { ... } // Spring/Apache Commons
public class BaseService {
// 明確不想被覆寫的方法加 final
public final void auditLog(String msg) { ... }
// 故意開放擴展的方法不加 final
protected void doBusiness() { ... }
}
效益:防止子類意外破壞核心邏輯、JIT 編譯器可內聯優化、語義明確傳達設計意圖。
將這些最佳實踐內化為肌肉記憶,能顯著降低線上故障率,提升代碼審查通過率。