Java 25 中的新特性
1.概述
Java 25 即將發布!這個短期版本計劃於 2025 年 9 月發布,它將全面增強 Java 語言、標準函式庫、API 和執行時間的功能。
在本教程中,我們將探討截至 2025 年 6 月 Java 25 中引入的所有新功能和變更。讓我們透過一個簡單的程式碼範例和詳細的解釋來理解它們。
2. 語言和編譯器特性
Java 25 引入了一系列語言和編譯器增強功能,旨在提升語言的表達能力和簡潔性。這些功能提升了開發者在日常任務和高階模組化場景中的體驗。
2.1. 模式中的原始類型(JEP 507 - 第三次預覽)
現在,模式比對可以處理switch
和instanceof
語句中的原始類型。例如:
static void test(Object obj) {
if (obj instanceof int i) {
System.out.println("It's an int: " + i);
}
}
JEP 507 將原始類型引入 Java 的模式匹配框架,使此類表達式更加直觀,並減少了樣板程式碼。該 JEP 是旨在統一整個語言類型模式的更廣泛努力的一部分。
2.2. 模組導入聲明(JEP 511 - 預覽)
JEP 511 引入了模組導入聲明,讓我們可以使用模組透過import
語句聲明其依賴項,從而提高模組的可讀性。傳統上,模組中的依賴項只能在module-info.java
描述符中使用requires
指令宣告。 JEP 511 引入了一種在 Java 檔案頂部使用import module
語句宣告模組相依性的方法,類似於傳統的import
語句。這提高了清晰度,並使工具能夠在開發過程中更準確地推斷依賴項。例如:
import module java.base;
//...
public class Main {
public static void main(String[] args) {
Date d = new Date();
System.out.println("Resolved Date: " + d);
}
}
但是,我們必須小心處理歧義引用。讓我們看一個範例程式碼來演示這一點:
import module java.base; // exports java.util, which includes java.util.Date
import module java.sql; // exports java.sql, which also includes java.sql.Date
public class Main {
public static void main(String[] args) {
Date d = Date.valueOf("2025-06-15");
System.out.println("Resolved Date: " + d);
}
}
當嘗試編譯此類時,將出現以下訊息:
error: reference to Date is ambiguous
Date d = Date.valueOf("2025-06-15");
^
both class java.sql.Date in java.sql and class java.util.Date in java.util match
error: reference to Date is ambiguous
解決方案是新增我們在本例中想要使用的特定類別的導入:
import module java.base;
import module java.sql;
import java.sql.Date;
public class Main {
public static void main(String[] args) {
Date d = Date.valueOf("2025-06-15");
System.out.println("Resolved Date: " + d);
}
}
最後,這項變更也允許我們從星號導入切換到模組導入:
// These imports could be coalesced:
import javax.xml.*;
import javax.xml.parsers.*;
import javax.xml.stream.*;
相反,只需使用:
import module java.xml;
雖然不建議濫用 import star 和 module 導入,但它確實使導入和依賴定義更加簡潔,更加簡潔。
2.3. 緊湊來源檔案(JEP 512)和實例主要方法
現在 Java 支援頂級實例main
方法和無類別的緊湊檔案。這意味著以下聲明現在有效:
void main() {
System.out.println("Hello from Java 25!");
}
JEP 512 是基於 Java 21 中引入的簡化啟動器建置。它允許我們編寫無需類別聲明的快速腳本或演示。這些緊湊的源文件非常適合教學、腳本編寫和快速原型設計,降低了新開發者的入門門檻,並縮短了學習曲線。
2.4. 靈活的建構子主體(JEP 513 – 最終版)
靈活的構造函數體(JEP 513)允許多個構造函數委託給一個公共的初始化體,使其變得如此簡單:
class Person {
final int age;
Person(int age) {
this.age = age;
}
}
class Employee extends Person {
final String name;
Employee(String name, int age) {
if (age < 18 || age > 67)
throw new IllegalArgumentException("Age must be between 18 and 67");
super(age); // super() is no longer required as the first statement in Java 25
this.name = name;
}
public static void main(String[] args) {
var emp = new Employee("Alice", 35);
System.out.println("Person age set: " + emp.age);
}
}
在 JEP 513 之前,Java 建構子必須先呼叫super(…)
或this(…)
作為第一個語句,這常常迫使我們重複驗證或初始化邏輯,或將其推入靜態輔助方法中。使用 JEP 513,我們可以在建構函式呼叫之前包含程式碼,讓參數驗證或共用設定在一個地方乾淨俐落地完成,從而提高可讀性、快速失敗行為和物件完整性,而不會違反 Java 的建構規則。
3. API 增強功能
Java 25 也對現有 API 進行了一系列改進,此外還繼續開發仍處於預覽、最終和早期階段的新 API。
3.1. 作用域值(JEP 506 – 最終版)
JEP 506 提供了輕量級、不可變、線程安全的ThreadLocal
替代方案。它們旨在與虛擬線程配合使用:
import java.lang.ScopedValue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ScopedUserExample {
static final ScopedValue<String> USER = ScopedValue.newInstance();
public static void main(String[] args) {
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
executor.submit(() -> ScopedValue.where(USER, "Alice").run(() -> {
System.out.println("Thread: " + Thread.currentThread());
System.out.println("User: " + USER.get());
}));
executor.submit(() -> ScopedValue.where(USER, "Bob").run(() -> {
System.out.println("Thread: " + Thread.currentThread());
System.out.println("User: " + USER.get());
}));
// Optional delay to ensure output appears before main exits
Thread.sleep(200);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
作用域值旨在以安全、高效且不可變的方式跨呼叫鏈傳遞上下文。它們尤其適用於虛擬線程和結構化並發,透過避免記憶體洩漏和同步開銷,為ThreadLocal
提供了一種高效的替代方案。
注意:當我們將 Scoped Value 與虛擬執行緒結合使用時,存取 Scoped Value 的邏輯必須包裝在ScopedValue.where(…).run(…)
作用域內。在作用域內向執行器提交任務是不夠的。任務本身必須在作用域內建立才能保留綁定。
3.2. 結構化並發(JEP 505 - 第五次預覽)
JEP 505 旨在透過將相關執行緒視為具有適當生命週期管理的單一單元來簡化並發。第五個預覽版透過將建構函式和單獨的策略方法替換為單一靜態工廠方法StructuredTaskScope.open()
來改進 API。這種方法提高了定義自訂連線和錯誤處理行為的一致性和靈活性。接下來,我們將使用新的語法:
import java.util.concurrent.StructuredTaskScope;
public class StructuredExample {
static String fetchUser() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return "Alice";
}
static String fetchOrder() {
try {
Thread.sleep(150);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return "Order#42";
}
public static void main(String[] args) throws Exception {
try (var scope = StructuredTaskScope.<String>open()) {
var userTask = scope.fork(() -> fetchUser());
var orderTask = scope.fork(() -> fetchOrder());
scope.join();
System.out.println(userTask.get() + " - " + orderTask.get());
}
}
}
結構化並發幫助我們管理邏輯上相關的多個並發任務。它保證子執行緒以群組為單位完成或取消,從而提高多執行緒應用程式的可靠性和可讀性。
3.3.穩定價值 API(JEP 502 – 預覽版)
穩定值 API(JEP 502)將類似Optional-
的語意擴展為上下文穩定的不可變值:
import java.lang.StableValue;
public class StableExample {
public static void main(String[] args) {
// Create a new unset StableValue
var greeting = StableValue.<String>of();
String message = greeting.orElseSet(() -> "Hello from StableValue!");
System.out.println(message);
}
}
穩定值提供了一個 API,用於在執行緒或運算之間安全地共享不可變、上下文穩定的值。它們在涉及快取、惰性求值或穩定範圍內的一致性讀取的情況下非常方便,並且與結構化並發很好地整合。
3.4. 加密物件的 PEM 編碼(JEP 470 - 預覽)
JEP 470 增加了透過標準 API 讀寫 PEM 格式加密金鑰和憑證的支援。新的 API 對此類操作進行了抽象,使其變得非常簡單:
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
public class PEMExample {
public static void main(String[] args) {
String pem = """
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDgjDohS0RHP395oJxciVaeks9N
KNY5m9V1IkBBwYsMGyxskrW5sapgi9qlGSYOma9kkko1xlBs17qG8TTg38faxgGJ
sLT2BAmdVFwuWdRtzq6ONn2YPHYj5s5pqx6vU5baz58/STQXNIhn21QoPjXgQCnj
Pp0OxnacWeRSnAIOmQIDAQAB
-----END PUBLIC KEY-----
""";
try {
String base64 = pem.replaceAll("-----.*-----", "").replaceAll("\\s", "");
byte[] keyBytes = Base64.getDecoder().decode(base64);
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
KeyFactory factory = KeyFactory.getInstance("RSA");
PublicKey key = factory.generatePublic(spec);
System.out.println("Loaded key: " + key.getAlgorithm());
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
e.printStackTrace();
}
}
}
現在,我們可以利用 Java 安全 API 直接處理 PEM 編碼的對象,例如 X.509 憑證和 RSA 金鑰,無需第三方程式庫或手動轉換。這提高了與基於 OpenSSL 的系統的互通性,並簡化了安全通訊。
3.5. Vector API(JEP 508 – 第十個孵化器)
JEP 508 提供了一個 API 來表達式向量計算,這些計算可以可靠地編譯為最佳向量硬體指令:
import jdk.incubator.vector.*;
public class VectorExample {
public static void main(String[] args) {
float[] left = {1f, 2f, 3f, 4f};
float[] right = {5f, 6f, 7f, 8f};
FloatVector a = FloatVector.fromArray(FloatVector.SPECIES_128, left, 0);
FloatVector b = FloatVector.fromArray(FloatVector.SPECIES_128, right, 0);
FloatVector c = a.add(b);
float[] result = new float[FloatVector.SPECIES_128.length()];
c.intoArray(result, 0);
System.out.println("Vector result: " + java.util.Arrays.toString(result));
}
}
需要: –enable-preview –add-modules jdk.incubator.vector
Vector API 支援在現代中央處理器 (CPU) 上高效執行的資料平行運算。它利用 SIMD 指令,幫助 Java 程式碼實現與手動調整的原生程式碼相當的效能,並且正在孵化階段持續發展。
3.6. 金鑰派生函數 API(JEP 510 – 最終版)
Java 25 引入了基於密碼的金鑰衍生函數的標準 API,例如 PBKDF2 和 scrypt:
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
public class KeyDerivationExample {
public static void main(String[] args) throws Exception {
char[] password = "hunter2".toCharArray();
byte[] salt = "somesalt".getBytes();
PBEKeySpec spec = new PBEKeySpec(password, salt, 65536, 256);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
SecretKey key = factory.generateSecret(spec);
System.out.println("Derived key format: " + key.getFormat());
}
}
JEP 510 標準化了對廣泛使用的加密原語的訪問,用於從用戶密碼中獲取加密金鑰,減少對第三方庫的依賴並實現開箱即用的安全實現。
4. 其他變更
Java 25 的變更還包括刪除配置、基於平台的更新和增強。
4.1. 刪除 32 位元 x86 埠(JEP 503 – Final)
JEP 503 移除了 OpenJDK 對舊版 32 位元 x86 架構的支援。此 JEP 旨在消除日益不相關的平台的維護開銷。 64 位元 x86 和 ARM64 移植仍將獲得全面支援。
4.2. JFR CPU 時間分析(JEP 509 - 實驗)
JEP 509 為 Java Flight Recorder (JFR) 增加了基於 CPU 時間的分析支援。此功能使我們能夠記錄和分析特定方法或執行緒所花費的 CPU 時間,從而改善效能診斷,尤其是在多執行緒和 I/O 密集型工作負載下。將新的JDK.CPULoad
和相關的 JFR 事件與自訂記錄結合使用:
java
-XX:StartFlightRecording=filename=cpu-time.jfr,duration=10s,settings=profile
--enable-preview
MyApp
然後在 JDK Mission Control 或 VisualVM 中分析CPU time.JFR
文件,觀察每個方法和執行緒的 CPU 使用率。
4.3. 提前命令行人體工程學(JEP 514 - 最終版)
提前命令行人體工學 (JEP 514) 透過符合人體工學的 AOT(提前)編譯命令列選項提升了啟動效能。 Java 25 簡化了 jaotc 和基於 GraalVM 的 AOT 工具的使用,從而為靜態部署和基於原生鏡像的場景提供了更佳的啟動效能。
4.4. 提前方法分析(JEP 515 - 最終版)
JEP-515 在 AOT 編譯期間啟用方法層級效能分析,以便做出更明智的最佳化決策。它透過將方法使用頻率納入編譯策略來提升 AOT 編譯 Java 程式的效能。此外,它還能幫助 AOT 引擎做出更智慧的內聯和優化決策。
4.5. JFR 合作採樣(JEP 518 – 實驗)
JEP 518 允許應用程式向 Java Flight Recorder 建議安全採樣點。協同採樣透過將取樣與應用程式定義的安全點對齊來降低開銷,從而提高準確性,同時最大限度地減少對效能敏感程式碼的干擾。
4.6. 緊湊對象頭(JEP 519 - 預覽)
JEP 519 減少了 64 位元架構上的物件頭大小。此變更透過在物件頭中使用緊湊的佈局來處理同步和身份數據,從而縮小了 Java 物件的記憶體佔用。這尤其有利於大型堆和微服務環境。
4.7. JFR 方法計時與追蹤(JEP 520 - 實驗)
JEP 520 透過提供詳細的每個方法的計時和呼叫追蹤資訊來增強 JFR。我們開發人員可以更細緻地了解哪些方法耗時最多,從而進行更深入的分析並更好地識別瓶頸。
4.8.世代謝南多厄(JEP 521 – 最終)
JEP 521 為 Shenandoah 垃圾收集器添加了分代支援。分代 GC 透過將年輕代的收集與長壽命物件分開進行最佳化,從而提高了吞吐量和停頓時間效能。這使得 Shenandoah 在效率方面與 G1 和 ZGC 等收集器保持一致。
5. 開發人員需要了解的內容
正如我們剛剛看到的,Java 25 中的許多功能仍處於預覽或孵化階段。要使用這些功能編譯和執行程式碼,我們必須啟用它們。我們已經在預覽程式碼片段中這樣做了,但值得更好地理解它:
-
–enable-preview
:所有預覽功能都需要,否則會出現編譯錯誤 -
–add-modules
<name>
:孵化模組所需,例如我們之前使用的jdk.incubator.vector
-
–release 25
:建議在編譯期間針對 Java 25 平台
請注意,預覽版和孵化版 API 可能會在未來版本中變更或移除。我們應該避免在生產環境中使用它們,或密切關注 JDK 官方文件和發行說明,以查找錯誤修復或問題。考慮到這一點,為了編譯和運行包含這些功能的程式碼,我們需要:
# At compile time do
javac --enable-preview --release 25 --add-modules jdk.incubator.vector MyClass.java
# At runtime do
java --enable-preview --add-modules jdk.incubator.vector MyApp
這樣,我們提示 Java 虛擬機器 (JVM) 允許在編譯和執行時使用這些功能。
6. 結論
Java 25 延續了平台在現代化和高效方面的穩定發展。它完善了許多預覽功能,引入了新的 API,並在從語言語法到運行時診斷和記憶體管理等各個層面提升了效能。
此版本還彰顯了 Java 對現代硬體的承諾,包括效能分析和附加功能。儘管 Java 25 是一個短期版本,但它提供了寶貴的優勢和新功能,值得開發者採用並提供回饋,因為這將塑造未來的 LTS 版本,例如 Java 27。 Java 25 的完整變更清單位於JDK 發行說明中。