首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 防禦性編程 10 條鐵律:資深開發者的偏執智慧

     編輯:Java 相關知識

前言

經歷 20 年代碼洗禮,深知「凡是可能出錯的事,終將出錯」(墨菲定律)。防禦性編程不是缺乏自信,而是對不確定性的敬畏。以下 10 條鐵律,源自無數線上故障的血淚教訓。

鐵律一:字符串字面量放左邊,防 NPE

// ❌ 危險:variable 為 null 拋 NPE
if (variable.equals("literal")) { ... }

// ✅ 安全:字面量永不為 null
if ("literal".equals(variable)) { ... }

零成本規避最常見的空指針異常,無任何副作用。

鐵律二:不信任早期 JDK API 的返回值

// File.list() 可能返回 null(非目錄時)
File[] files = dir.listFiles(); // 更推薦使用 listFiles()
if (files != null) {  // 必須非空判斷
    for (File f : files) { ... }
}

Javadoc 明確聲明:非目錄返回 null。永遠不要假設 API 行為符合直覺。

鐵律三:拒絕魔法數字 -1,用 >= 0 判斷存在性

// ❌ 依賴 -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 變量時同樣有效。

鐵律五:集合/數組雙重檢查:非空 + 長度 > 0

// ❌ 僅判長度,array 為 null 直接 NPE
if (array.length > 0) { ... }

// ✅ 短路求值:先非空,再長度
if (array != null && array.length > 0) { ... }

來源不明的集合(API 返回、反序列化、配置注入)必須雙重檢查。

鐵律六:默認 final 方法,除非確需覆寫

// ❌ 開放覆寫,子類可隨意破壞邏輯
public void criticalOperation() { ... }

// ✅ 封鎖覆寫,保護核心邏輯
public final void criticalOperation() { ... }

開閉原則針對「擴展」,不代表「盲目開放修改」。保護不變約束,才是真正的開放。

鐵律七:變量參數默認 final,防誤修改

// ❌ 參數可被重賦值,掩蓋原始值
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 會匹配錯誤重載,內部分發消除歧義。

鐵律九:switch 必須有 default 拋異常

// ❌ 缺失 default,新增 enum 值時靜默失效
switch (status) {
    case SUCCESS: ...; break;
    case FAIL: ...; break;
}

// ✅ 失快速,暴露未處理情況
switch (status) {
    case SUCCESS: ...; break;
    case FAIL: ...; break;
    default: throw new IllegalStateException("未知狀態: " + status);
}

枚舉擴展、業務新增狀態時,異常比邏輯漏洞早發現得多。

鐵律十:switch case 用大括號隔離作用域

// ❌ 變量跨 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 個月後的自己維護——請對未來的自己偏執一點。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved