Java 7 (代號 Dolphin) 於 2011 年釋出,雖然不如 Java 8 的 Lambda/Stream 那樣劃時代,但它引入的 invokedynamic 字節碼指令為後來的 Lambda、動態語言支撐奠定了 JVM 基礎。同時,語法糖(Switch 字串、二進制字面量、數字分隔符)、Try-with-resources、多重捕獲異常、NIO.2、腳本引擎 API (JSR 223) 等特性極大提升了開發效率與系統健壯性。本文將逐一深度解析並給出生產級實戰代碼。
String day = "MONDAY";
switch (day) {
case "MONDAY": case "TUESDAY": case "WEDNESDAY":
case "THURSDAY": case "FRIDAY":
System.out.println("工作日"); break;
case "SATURDAY": case "SUNDAY":
System.out.println("週末"); break;
default: throw new IllegalArgumentException("Invalid day: " + day);
}
// 編譯器實現:先比較 hashCode(),再 equals() 確認,效能接近枚舉 switch
int flags = 0b1010_1100_1111_0000; // 0b/0B 前綴,_ 分隔提升可讀性
long creditCard = 1234_5678_9012_3456L;
double pi = 3.141_592_653_589_793;
// 限制:_ 不能在首尾、小數點旁、型別後綴前、0x/0b 前綴後
Map> map = new HashMap<>(); // Java 7+
// Java 6 需寫 new HashMap>()
try {
Files.readAllBytes(Paths.get("a.txt"));
} catch (IOException | InterruptedException e) { // 用 | 分隔,e 為 final 隱含
log.error("讀取失敗", e);
throw new RuntimeException(e);
}
// 限制:捕獲的異常類之間不能有繼承關係(否則編譯錯誤)
// Java 7+: 實作 AutoCloseable 的資源自動關閉,關閉順序與宣告相反
try (FileInputStream in = new FileInputStream("src.txt");
FileOutputStream out = new FileOutputStream("dest.txt")) {
byte[] buf = new byte[8192];
int len;
while ((len = in.read(buf)) > 0) out.write(buf, 0, len);
} // 自動調用 out.close() 再 in.close(),即使發生異常也保證關閉
// 等價 Java 6 繁瑣代碼(finally 中巢狀 try-catch)省略...
public void readFile(String path) throws IOException {
IOException primary = null;
try (FileInputStream in = new FileInputStream(path)) {
in.read();
} catch (IOException e) {
primary = e; // 保存主要異常
throw e;
} finally {
// 若 close() 也拋異常,不會掩蓋 primary
// try-with-resources 內部自動處理:primary.addSuppressed(suppressed)
}
}
// 手動控制範例(不使用 try-with-resources 時)
public void manualSuppress() throws IOException {
FileInputStream in = null;
IOException primary = null;
try {
in = new FileInputStream("a.txt");
in.read();
} 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;
}
}
// 獲取被壓制異常:primary.getSuppressed() 返回所有被壓制異常陣列
public void rethrowExample(String mode) throws IOException, SQLException {
try {
if ("file".equals(mode)) throw new IOException("IO 錯誤");
else throw new SQLException("SQL 錯誤");
} catch (final IOException | SQLException e) { // 參數隱含 final
log.error("捕獲", e);
throw e; // Java 7: 編譯器精確推導實際拋出型別,無需拋出 Exception
}
}
Path、Files、FileSystem、WatchServicePath path = Paths.get("/data", "logs", "app.log"); // 路徑操作
Files.createDirectories(path.getParent()); // 遞歸建目錄
Files.write(path, "content".getBytes(StandardCharsets.UTF_8),
StandardOpenOption.CREATE, StandardOpenOption.APPEND);
List lines = Files.readAllLines(path, StandardCharsets.UTF_8);
// 目錄遍歷(支援深度、過濾、符號鏈接控制)
Files.walkFileTree(Paths.get("/data"), new SimpleFileVisitor() {
@Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
if (file.toString().endsWith(".log")) System.out.println(file);
return FileVisitResult.CONTINUE;
}
});
// 檔案變更監聽(生產級熱加載/日誌收集基石)
WatchService watcher = FileSystems.getDefault().newWatchService();
Paths.get("/data").register(watcher, StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_DELETE);
while (true) {
WatchKey key = watcher.take();
for (WatchEvent> event : key.pollEvents()) {
System.out.println(event.kind() + " : " + event.context());
}
key.reset();
}
invokedynamic 是 Java 7 新增的字節碼指令,允許在運行時動態決定方法調用目標,打破了 invokevirtual 等靜態綁定限制。它是 Lambda 表達式、方法引用、動態語言 (Groovy/JRuby/Jython) 在 JVM 上高效運行的核心基礎。應用開發者極少直接接觸,但理解其存在有助於理解 JVM 如何支撐多語言生態。
ScriptEngineManager:引擎工廠,發現類路徑下的腳本引擎(Nashorn JS、Groovy、Python 等)。ScriptEngine:執行腳本、綁定變量、編譯腳本、調用函數的核心介面。Bindings (extends Map):命名空間,實現 Java ↔ Script 雙向數據傳遞。ScriptContext:執行上下文,包含 ENGINE_SCOPE (引擎級) 與 GLOBAL_SCOPE (全局級) 兩層 Bindings。ScriptEngineManager mgr = new ScriptEngineManager();
ScriptEngine js = mgr.getEngineByName("JavaScript"); // 或 "nashorn", "js"
js.put("name", "World"); // Java → Script
js.eval("var message = 'Hello, ' + name; print(message);"); // Script 執行
Object msg = js.get("message"); // Script → Java
System.out.println("Java 獲取: " + msg);
Bindings custom = new SimpleBindings();
custom.put("hobby", "coding");
js.eval("print('I like ' + hobby);", custom); // 僅此次 eval 生效,不污染引擎默認 Bindings
ScriptContext ctx = js.getContext();
// 輸出重定向到文件
ctx.setWriter(new FileWriter("script.log"));
js.eval("print('這行會寫入文件');");
// 屬性作用域:ENGINE_SCOPE 優先級高於 GLOBAL_SCOPE
ctx.setAttribute("key", "globalVal", ScriptContext.GLOBAL_SCOPE);
ctx.setAttribute("key", "engineVal", ScriptContext.ENGINE_SCOPE);
System.out.println(ctx.getAttribute("key")); // 輸出 engineVal
// Bindings 作用域同理
Bindings global = js.createBindings();
global.put("x", 1);
ctx.setBindings(global, ScriptContext.GLOBAL_SCOPE);
Bindings engine = js.createBindings();
engine.put("x", 2);
ctx.setBindings(engine, ScriptContext.ENGINE_SCOPE);
js.eval("print(x);"); // 輸出 2
if (js instanceof Compilable) {
CompiledScript compiled = ((Compilable) js).compile("function sum(a,b){return a+b;}");
for (int i = 0; i < 10000; i++) {
compiled.eval(); // 避免重複解析,僅執行字節碼
}
}
// Java 調用 Script 頂層函數
Invocable inv = (Invocable) js;
js.eval("function greet(name) { return 'Hello, ' + name; }");
String result = (String) inv.invokeFunction("greet", "Java"); // 返回 "Hello, Java"
// Java 調用 Script 物件方法
js.eval("var obj = { calc: function(a,b) { return a*b; } }");
Object obj = js.get("obj");
Integer product = (Integer) inv.invokeMethod(obj, "calc", 6, 7); // 42
// Script 實現 Java 介面 (極其強大:業務規則外部化、插件化)
public interface Greeter { String greet(String name); }
js.eval("function greet(name) { return 'Hi, ' + name; }");
Greeter greeter = inv.getInterface(Greeter.class);
System.out.println(greeter.greet("Script")); // 透明調用,Java 代碼無感知
// 規則腳本 (存儲於資料庫/配置中心,熱加載)
String ruleScript = "function approve(loan) { return loan.amount < 100000 && loan.score > 600; }";
public boolean evaluate(LoanApplication loan) {
ScriptEngine js = mgr.getEngineByName("JavaScript");
js.eval(ruleScript);
Invocable inv = (Invocable) js;
return (Boolean) inv.invokeFunction("approve", loan); // loan 物件自動轉換
}
// 優點:無需重新部署 Java 服務即可變更風控規則,非程式員可維護
| 特性 | Java 7 狀態 | 現代替代/演進 |
|---|---|---|
| Switch String | 引入 | Java 12+ Switch Expression (yield) |
| Try-with-resources | 引入 | Java 9+ 可使用已宣告 final 變量 |
| 多重捕獲/被壓制異常 | 引入 | 持續增強,Java 21+ 未命名模式變量 |
| NIO.2 | 引入 | 核心檔案 API,持續擴充 (Java 11+ Path.of) |
| InvokeDynamic | 引入 | Lambda/MethodRef/Record/Dynamic Language 基石 |
| 腳本引擎 (Nashorn) | 內建 JS | Java 15 移除 Nashorn,建議遷移 GraalVM JS / GraalPy / Wasm |
遷移提醒:若專案仍依賴 javax.script + Nashorn,請規劃遷移至 GraalVM Polyglot API,獲得更好效能、ES2024+ 支援、沙箱隔離與原生映像相容性。