本文繼續解讀《編寫高質量代碼:改善 Java 程序的 151 個建議》第一章中第 16 至 20 條建議,涵蓋腳本語言應用、動態編譯、instanceof 陷阱、斷言最佳實踐以及常量替換風險等核心主題。
腳本語言(如 Groovy、JavaScript、Ruby)具備三大特性:運行期解釋執行、動態類型、語法靈活。這彌補了 Java 強編譯、靜態類型、部署流程繁瑣的短板,實現「業務邏輯熱更新、無需重啟 JVM」。
Java 6 引入 JSR-223 規範,提供統一的 javax.script API。只要腳本語言實現該規範,即可在 JVM 上運行。Java 默認內置 Nashorn(Java 8)或 Rhino(Java 6/7)JavaScript 引擎。
// model.js
function formula(var1, var2) { return var1 + var2 * factor; }
Java 端通過 ScriptEngine 加載腳本,利用 Bindings 注入上下文變量 factor,並以 Invocable 調用腳本函數。修改 model.js 將乘法改為減法後,無需重啟 JVM 即可生效,完美模擬線上業務變更場景。
JDK 自帶 jrunscript 可直接運行腳本文件,跨平台、無需啟動完整 JVM,適合構建腳本、部署腳本等自動化場景。
Java 6 提供 javax.tools.JavaCompiler,支持運行時編譯字符串、文件、數據庫中的源碼。核心步驟:獲取編譯器 → 構建 JavaFileObject → 設置編譯選項(如 -d 輸出目錄) → 執行編譯任務 → 反射加載類並調用方法。
instanceof 非預期結果| 表達式 | 結果 | 說明 |
|---|---|---|
"String" instanceof Object | true | 字符串字面量是 Object 實例 |
new String() instanceof String | true | 基本語義 |
new Object() instanceof String | false | 父類實例不是子類實例 |
'A' instanceof Character | 編譯錯誤 | 基本類型不能直接用 instanceof |
null instanceof String | false | null 不是任何類的實例 |
(String) null instanceof String | false | 強轉後仍為 null |
new Date() instanceof String | 編譯錯誤 | 無繼承關系的類型不可判斷 |
泛型擦除後 T instanceof Date | false | 運行期 T 擦除為 Object |
instanceof 使用。assert booleanExpression; assert booleanExpression : "error message";
-ea 或 -enableassertions 開啟。AssertionError(繼承自 Error),屬不可恢復的嚴重錯誤。if (arg == null) throw new IllegalArgumentException()。assert list.remove(item),關閉斷言後邏輯不執行,引發嚴重 Bug。default: assert false : "不可能到達";assert var1 == var2 * 2。final 修飾的基本類型與字符串屬於「穩定態」,編譯器會在編譯期將其值內聯到調用方的字節碼中(而非符號引用)。
Constant.MAX_AGE = 150,編譯 Client 類,字節碼寫死 150。180,僅重新編譯 Constant 類,替換 Constant.class 到生產環境。Client 仍輸出 150!因為 Client.class 字節碼裡寫死的是舊值。若常量為對象引用(非基本類型/String),屬「軟 final」,編譯器保留符號引用,替換類文件後可獲取新值。
嚴禁單個 .class 文件熱替換發布! IDE 自動編譯掩蓋了此風險,生產環境必須全量構建發布,確保所有依賴類同步更新。