優秀的程序員不僅寫出能跑通的代碼,更寫出經得起邊界條件、並發衝突、版本演進考驗的代碼。本文總結 10 個看似「偏執」實則極具實戰價值的 Java 防禦性編程技巧,助你構建高質量系統。
// Bad: user.getName().equals("admin") → NPE 風險
// Good: "admin".equals(user.getName()) → 永不拋 NPE
// Better: Objects.equals(user.getName(), "admin") // Java 7+ 推薦
原理:字面量不為 null,調用其 equals 方法安全。同理適用于 == 比較枚舉/常量。
File dir = new File("/path");
if (dir.isDirectory()) { // 先確認是目錄
String[] files = dir.list(); // Javadoc 明確說明:非目錄返回 null
if (files != null) { // 二次防空
for (String f : files) { /* ... */ }
}
}
// Bad: str.indexOf(c) != -1 // Good: str.indexOf(c) >= 0 // Reason: 未來規範可能變更返回值(如 -2 表示忽略大小寫未找到),>=0 語義永恆不變。
// Bad: if (flag = true) { ... } // 編譯通過,邏輯錯誤
// Good: if (true == flag) { ... } // 誤寫成 = 編譯直接報錯
// Best: if (flag) { ... } // 布爾變量直接判斷最清晰
// Bad: if (list.size() > 0) ... // Good: if (list != null && !list.isEmpty()) ... // Best: CollectionUtils.isNotEmpty(list) // Apache Commons / Spring 工具類
// Bad: public void process() { ... } // 任意子類可覆蓋,破壞邏輯
// Good: public final void process() { ... } // 明確禁止覆蓋
// Principle: 開閉原則對「擴展」開放,對「修改」關閉。final 是保護核心邏輯不被意外修改的最後一道防線。
// Good: public void handle(final String input) { final String processed = input.trim(); ... }
// Reason: 防止中途意外重賦值導致邏輯錯誤,編譯器強制保證引用不變,利於 JIT 優化。
// Bad: 重載模糊
void handle(Object o) { ... }
void handle(List<String> list) { ... }
// 調用者傳入 (Object) Arrays.asList("a") → 進入 Object 重載,而非 List 重載!
// Good: 統一入口 + 內部分發
void handle(Object o) {
if (o instanceof List) handleList((List<?>) o);
else handleSingle(o);
}
void handleList(List<?> list) { ... }
void handleSingle(Object o) { ... }
// Bad: 無 default,新增枚舉值時靜默不處理
// Good: default: throw new IllegalStateException("Unexpected value: " + value);
// Best: 使用 Java 12+ Switch Expression 編譯期強制覆蓋所有情況
String result = switch (status) {
case SUCCESS -> "OK";
case FAIL -> "ERROR";
default -> throw new IllegalStateException("Unknown: " + status);
};