String day = "MONDAY";
switch (day) {
case "MONDAY": case "TUESDAY": case "WEDNESDAY":
case "THURSDAY": case "FRIDAY":
System.out.println("Workday"); break;
case "SATURDAY": case "SUNDAY":
System.out.println("Weekend"); break;
default: throw new IllegalArgumentException("Invalid day: " + day);
}
// 實現原理:編譯器將 String switch 轉為 hashCode() + equals() 兩級判斷
int mask = 0b1010_1100_1111_0000; // 0b/0B 前綴,_ 分隔提高可讀性
long creditCard = 1234_5678_9012_3456L; // 長整數同樣支援
// _ 不能出現在首尾、小數點旁、型別後綴前
// JDK 7 前:需多個 catch 塊重複代碼
// JDK 7+:單一 catch 塊捕獲多種異常 (用 | 分隔)
try {
// ... IO 操作
} catch (IOException | SQLException ex) {
logger.error("資料存取失敗", ex);
throw ex;
}
// 限制:捕獲的異常類型不能有父子繼承關係 (否則編譯錯誤)
// 異常參數隱含 final,不可在 catch 塊中重新賦值
痛點:finally 中關閉資源拋出異常會掩蓋 try 中的原始異常。
// JDK 7 前手動處理被壓制異常 (繁瑣)
FileInputStream in = null;
IOException primary = null;
try {
in = new FileInputStream("data.txt");
// 讀取...
} catch (IOException e) {
primary = e;
} finally {
if (in != null) {
try { in.close(); }
catch (IOException suppressed) {
if (primary != null) primary.addSuppressed(suppressed);
else primary = suppressed;
}
}
if (primary != null) throw primary;
}
JDK 7 解決方案:Throwable.addSuppressed() + getSuppressed() 自動記錄;try-with-resources 自動關閉實現 AutoCloseable 的資源。
// 現代寫法:簡潔、安全、自動處理被壓制異常
try (FileInputStream in = new FileInputStream("data.txt");
BufferedReader br = new BufferedReader(new InputStreamReader(in))) {
String line = br.readLine();
// 處理...
} catch (IOException e) {
// e.getSuppressed() 可獲取關閉資源時產生的被壓制異常
throw e;
}
// 編譯器自動生成 finally 區塊調用 close(),異常鏈完整保留
// 編譯器警告:泛型可變參數可能導致堆污染
@SafeVarargs // 開發者承諾:方法體內不會對泛型參數進行不安全操作 (如存入不同類型)
public static void safeMethod(T... args) { ... }
// 限制:只能用於 final/static 方法或構造器
// 原則:只有確信類型安全時才加註解,否則掩蓋真實風險
Java 內建 javax.script 包,支援 JavaScript (Nashorn, JDK 8-14)、Groovy、JRuby、Jython、Lua 等腳本語言,實現規則引擎、動態配置、用戶腳本擴展、模板渲染等場景。
ScriptEngineManager:引擎工廠,發現類路徑下的腳本引擎實現 (SPI 機制)。ScriptEngine:執行腳本、綁定變量、編譯腳本、調用函數的核心介面。Bindings (extends Map):名值對存儲,實現 Java ↔ 腳本雙向數據交換。ScriptContext:執行上下文,包含多作用域 Bindings、輸入/輸出流、屬性。Compilable:可選介面,支援腳本預編譯 (CompiledScript),重複執行效能大增。Invocable:可選介面,支援直接調用腳本函數/方法、獲取腳本實現的 Java 介面代理。
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("JavaScript"); // 或 "nashorn", "graal.js"
if (engine == null) throw new IllegalStateException("找不到 JS 引擎");
engine.eval("print('Hello from JS');"); // 直接執行
// 1. Java 向腳本傳值
engine.put("user", new User("Alice", 25));
engine.put("items", Arrays.asList("Apple", "Banana"));
// 2. 腳本讀取 Java 變量、寫入結果
engine.eval("var total = items.length; print('User: ' + user.name + ', Items: ' + total);");
// 3. Java 獲取腳本產生的結果
Object total = engine.get("total"); // 類型自動轉換 (JS Number → Java Double/Long)
System.out.println("Total from script: " + total);
// 引擎預設 Bindings (ENGINE_SCOPE) 共享,可能造成線程安全問題
// 創建獨立 Bindings 實現請求級隔離
Bindings requestBindings = engine.createBindings();
requestBindings.put("requestId", UUID.randomUUID().toString());
engine.eval("print('Processing ' + requestId);", requestBindings); // 僅此次 eval 生效
ScriptContext ctx = engine.getContext();
// 重定向腳本輸出到檔案
ctx.setWriter(new FileWriter("script-output.log"));
engine.eval("print('這行會寫入檔案');");
// 多作用域屬性:ENGINE_SCOPE (引擎級) 優先於 GLOBAL_SCOPE (全域)
ctx.setAttribute("config", "engineValue", ScriptContext.ENGINE_SCOPE);
ctx.setAttribute("config", "globalValue", ScriptContext.GLOBAL_SCOPE);
Object val = ctx.getAttribute("config"); // 返回 "engineValue" (優先級高)
if (engine instanceof Compilable) {
CompiledScript compiled = ((Compilable) engine).compile("function calc(x) { return x * x; }");
for (int i = 0; i < 10000; i++) {
compiled.eval(); // 無需重複解析,直接執行字節碼
}
}
engine.eval("function greet(name) { return 'Hello, ' + name; }");
if (engine instanceof Invocable) {
Invocable inv = (Invocable) engine;
Object result = inv.invokeFunction("greet", "World"); // 調用全局函數
System.out.println(result); // Hello, World
// 調用對象方法
engine.eval("var obj = { sayHi: function(n) { return 'Hi, ' + n; } };");
Object obj = engine.get("obj");
Object result2 = inv.invokeMethod(obj, "sayHi", "Alice");
System.out.println(result2); // Hi, Alice
}
public interface Greeter { String greet(String name); }
// 腳本中實現介面
engine.eval("function greet(name) { return 'Welcome, ' + name; }");
if (engine instanceof Invocable) {
Greeter greeter = ((Invocable) engine).getInterface(Greeter.class);
System.out.println(greeter.greet("Bob")); // Welcome, Bob
// Java 代碼完全感知不到實現來自腳本,解耦業務邏輯與實現技術
}
| 引擎 | 狀態 | 特點 | 推薦場景 |
|---|---|---|---|
| Nashorn (jdk.scripting.nashorn) | JDK 15 移除 | JS ES5/6 部分支援,無 WASM | 維舊專案 (JDK 8-14) |
| GraalVM JavaScript (GraalJS) | 活躍維護 | ES2023 完整支援、Node.js 相容、高效能、多語言互操作 | 新專案首選 (需引入 graal-sdk 依賴) |
| Rhino | 維護模式 | 舊版 JS,純 Java 實現 | 極簡環境、Android (舊版) |
| Groovy | 活躍 | Java 語法超集、DSL 強、編譯靜態/動態 | 規則引擎、構建腳本、測試 |
Java 7 以 語法糖 (String switch, 二進制字面量, 數值分隔符)、異常處理現代化 (多重捕獲, try-with-resources, 被壓制異常)、類型安全增強 (@SafeVarargs) 大幅提升開發體驗;腳本引擎 API (JSR 223) 則為 Java 應用注入動態性,支援業務規則外部化、用戶腳本擴展、多語言協作。現代專案建議:語法特性全面採用、異常處理統一遷移至 try-with-resources、腳本引擎選 GraalJS 或 Groovy,並嚴格控制腳本執行權限 (沙箱、超時、資源限制) 確保安全。