經歷 20 年代碼洗禮,深知「凡是可能出錯的事,終將出錯」(墨菲定律)。防禦性編程不是缺乏自信,而是對不確定性的敬畏。以下 10 條鐵律,源自無數線上故障的血淚教訓。
// ❌ 危險:variable 為 null 拋 NPE
if (variable.equals("literal")) { ... }
// ✅ 安全:字面量永不為 null
if ("literal".equals(variable)) { ... }
零成本規避最常見的空指針異常,無任何副作用。
// File.list() 可能返回 null(非目錄時)
File[] files = dir.listFiles(); // 更推薦使用 listFiles()
if (files != null) { // 必須非空判斷
for (File f : files) { ... }
}
Javadoc 明確聲明:非目錄返回 null。永遠不要假設 API 行為符合直覺。
// ❌ 依賴 -1 約定
if (str.indexOf(ch) != -1) { ... }
// ✅ 語義明確:索引有效即 >= 0
if (str.indexOf(ch) >= 0) { ... }
未來 JDK 若改用 -2 表示「不存在且大小寫不敏感」?語義判斷永遠比魔法數字穩健。
// ❌ 易手滑寫成 =,編譯不報錯,邏輯全錯
if (variable = 5) { ... }
// ✅ 編譯報錯:5 = variable 不合法
if (5 == variable) { ... }
經典 C/C++ 遺風,Java 雖限制條件必須是 boolean,但涉及 boolean 變量時同樣有效。
// ❌ 僅判長度,array 為 null 直接 NPE
if (array.length > 0) { ... }
// ✅ 短路求值:先非空,再長度
if (array != null && array.length > 0) { ... }
來源不明的集合(API 返回、反序列化、配置注入)必須雙重檢查。
// ❌ 開放覆寫,子類可隨意破壞邏輯
public void criticalOperation() { ... }
// ✅ 封鎖覆寫,保護核心邏輯
public final void criticalOperation() { ... }
開閉原則針對「擴展」,不代表「盲目開放修改」。保護不變約束,才是真正的開放。
// ❌ 參數可被重賦值,掩蓋原始值
void process(String input) {
input = "Oops"; // 參數變了,調試時誤以為是原始輸入
}
// ✅ 編譯期鎖定
void process(final String input) {
final String cleaned = sanitize(input); // 衍生變量也 final
}
final 強制不可變思維,減少心智負擔,利於 JIT 優化。
// ❌ 擦除後簽名相同,編譯報錯
void handle(T value) { ... }
void handle(List<T> values) { ... }
// ✅ 統一入口,內部分發
void handle(T value) {
if (value instanceof List) handleList((List<?>) value);
else handleSingle(value);
}
用戶傳入 (Object) list 會匹配錯誤重載,內部分發消除歧義。
// ❌ 缺失 default,新增 enum 值時靜默失效
switch (status) {
case SUCCESS: ...; break;
case FAIL: ...; break;
}
// ✅ 失快速,暴露未處理情況
switch (status) {
case SUCCESS: ...; break;
case FAIL: ...; break;
default: throw new IllegalStateException("未知狀態: " + status);
}
枚舉擴展、業務新增狀態時,異常比邏輯漏洞早發現得多。
// ❌ 變量跨 case 洩漏,編譯報錯
switch (type) {
case 1: int x = 1; break;
case 2: int x = 2; break; // 編譯錯誤:x 已定義
}
// ✅ 大括號創建獨立作用域
switch (type) {
case 1: { final int x = 1; break; }
case 2: { final int x = 2; break; }
default: throw new IllegalStateException();
}
case 本質是標號,非塊級作用域,大括號是唯一隔離手段。
這 10 條看似「偏執」,實則是 將運行時錯誤前移至編譯期、將隱性 Bug 顯性化、將隱性假設顯性化 的工程智慧。隨項目規模增長,這些微小約束的復利效應將節省無數排查小時。代碼寫給機器執行,更寫給 6 個月後的自己維護——請對未來的自己偏執一點。