Java21正式發(fā)布,史詩級(jí)增強(qiáng)!虛擬線程、分代 ZGC 正式來襲!!
閱讀本文大概需要 7.5 分鐘。
來自:萬能的大雄
導(dǎo)讀:Java21來了,全新的版本號(hào),當(dāng)然還有很多性能和技術(shù)的增強(qiáng)改
9 月 19 日,Oracle 宣布 Java 21 正式發(fā)布。自從 Java 改為 6 個(gè)月發(fā)布一個(gè)版本以來,此為如期交付的第 12 個(gè)版本。
根據(jù)規(guī)劃,Java 21 是長期支持版本,Oracle 將至少提供 8 年支持。根據(jù)客戶反饋和 Java 生態(tài)系統(tǒng)使用情況,Oracle 還宣布將 Java 11 的支持至少延長至 2032 年 1 月,也就是至少再提供 8 年的支持與更新。
從 Java 11 到 Java 21 的正式發(fā)布,一共修復(fù)了 24,196 個(gè) JIRA 問題,其中 17,288 個(gè)是由 Oracle 的工作人員完成的,而 6,908 個(gè)是由個(gè)人開發(fā)人員和其他組織的開發(fā)人員貢獻(xiàn)的。
從下圖可以看到,中國的騰訊、阿里巴巴、華為和龍芯做出了不少貢獻(xiàn)。
盤點(diǎn) Java 21 的新特性
除了大量的性能、穩(wěn)定性和安全性方面的更新,Java 21 還包括 15 個(gè) JEP(JDK Enhancement Proposal),其中包括 6 個(gè)預(yù)覽特性和 1 個(gè)孵化特性。
這些 JEP 可以分為六大類。下面我們具體來看看一下 Java 21 引入的部分新特性。
(一)Project Amber
JEP 430:字符串模板(預(yù)覽特性)
該 JEP 的目標(biāo)是:
-
對于需要在運(yùn)行時(shí)求值的字符串,簡化其表達(dá)方式; -
對于既包含文本(不管是單行文本還是多行文本)又包含表達(dá)式的復(fù)雜表達(dá)式,提高其可讀性; -
有的 Java 程序需要根據(jù)用戶提供的值來構(gòu)建字符串并傳遞給其他系統(tǒng),字符串模板支持對模板和其中嵌入的表達(dá)式的值進(jìn)行驗(yàn)證和轉(zhuǎn)換,從而提高這類程序的安全性; -
允許 Java 庫定義字符串模板中使用的格式化語法,保持靈活性; -
對于接受 Java 之外的語言(如 SQL、XML、JSON)編寫的字符串的 API,使之用起來更加方便; -
支持從文字文本和被嵌入的表達(dá)式計(jì)算非字符串值,而無需通過中間字符串表示進(jìn)行轉(zhuǎn)換。
String name = "Joan";
String info = STR."My name is \{name}"; // 模板表達(dá)式
assert info.equals("My name is Joan"); // 返回true
這里的 STR 是模板處理器,它是 Java 引入的一個(gè) public static final 的字段,會(huì)被自動(dòng)引入到每個(gè) Java 源代碼文件中。
因?yàn)檫@是個(gè)實(shí)驗(yàn)特性,所以編譯和運(yùn)行的時(shí)候要添加相關(guān)的參數(shù)。例如,假設(shè)代碼保存在 Test.java 文件中,則編譯運(yùn)行方式為:
javac --release 21 --enable-preview Test.java
java --enable-preview Test
該特性可以提高 Java 程序的可讀性和可維護(hù)性,編寫起來也更方便。
JEP 440:record 模式
該特性將模式匹配擴(kuò)展到可以解構(gòu) record 類的實(shí)例,從而實(shí)現(xiàn)更復(fù)雜的數(shù)據(jù)查詢。添加嵌套模式,使數(shù)據(jù)查詢可以更方便地組合起來。
在 Java 16 中,要提取一個(gè) record 實(shí)例的數(shù)據(jù),需要這樣操作:
record Point(int x, int y) {}
static void printSum(Object obj) {
if (obj instanceof Point p) {
int x = p.x();
int y = p.y();
System.out.println(x+y);
}
}
而在 Java 21 中,可以直接這樣實(shí)現(xiàn):
static void printSum(Object obj) {
if (obj instanceof Point(int x, int y)) {
System.out.println(x+y);
}
}
JEP 441:用于 switch 的模式匹配
該特性為 switch 表達(dá)式和語句引入了模式匹配。在 switch 中支持對表達(dá)式進(jìn)行多個(gè)模式的測試,每個(gè)模式都有特定的操作,從而可以簡潔而安全地表示復(fù)雜的面向數(shù)據(jù)的查詢。
我們可能希望使用模式來針對同一變量測試多種可能性,并在每種情況下執(zhí)行特定的操作。例如:
static String formatter(Object obj) {
String formatted = "unknown";
if (obj instanceof Integer i) {
formatted = String.format("int %d", i);
} else if (obj instanceof Long l) {
formatted = String.format("long %d", l);
} else if (obj instanceof Double d) {
formatted = String.format("double %f", d);
} else if (obj instanceof String s) {
formatted = String.format("String %s", s);
}
return formatted;
}
而在 Java 21 中,可以這樣實(shí)現(xiàn):
static String formatterPatternSwitch(Object obj) {
return switch (obj) {
case Integer i -> String.format("int %d", i);
case Long l -> String.format("long %d", l);
case Double d -> String.format("double %f", d);
case String s -> String.format("String %s", s);
default -> obj.toString();
};
}
在 Java 21 之前,如果 switch 后面的表達(dá)式的值為 null,則會(huì)拋出 NullPointerException。
為安全起見,我們會(huì)在 switch 語句之外執(zhí)行檢測。
static void testFooBarOld(String s) {
if (s == null) {
System.out.println("Oops!");
return;
}
switch (s) {
case "Foo", "Bar" -> System.out.println("Great");
default -> System.out.println("Ok");
}
}
現(xiàn)在Java21可以這樣處理:
static void testFooBarNew(String s) {
switch (s) {
case null -> System.out.println("Oops");
case "Foo", "Bar" -> System.out.println("Great");
default -> System.out.println("Ok");
}
}
JEP 443:無名模式和無名變量(預(yù)覽特性)
無名模式可以在不指定 record 組件的名稱或類型的情況下匹配組件,無名變量可以用于初始化,但不能被使用。它們都用下劃線字符表示。
例如:
int acc = 0;
for (Order _ : orders) {
if (acc < LIMIT) {
... acc++ ...
}
}
再看如下例子:
String s = ...
try {
int i = Integer.parseInt(s);
... i ...
} catch (NumberFormatException _) {
System.out.println("Bad number: " + s);
}
JEP 445:無名的類和實(shí)例 main 方法(預(yù)覽特性)
方便學(xué)生學(xué)習(xí),無需理解很多為編寫大型程序而設(shè)計(jì)的語言特性即可上手。
之前,要寫個(gè)最簡單的程序也要寫這么多代碼:
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
現(xiàn)在可以簡化為這樣,似乎在向Python,PHP等腳本語言看齊:
class HelloWorld {
void main() {
System.out.println("Hello, World!");
}
}
Java 甚至還引入了無名的類,將類的聲明隱藏掉:
void main() {
System.out.println("Hello, World!");
}
(二)Project Loom
JEP 444:虛擬線程
這是很重要的一個(gè)特性,將虛擬線程引入到了Java平臺(tái)中。虛擬線程是輕量級(jí)線程,大大降低了編寫、維護(hù)和觀察高吞吐量并發(fā)應(yīng)用程序的工作量。
該特性的目標(biāo)是:使采用簡單的每個(gè)請求一個(gè)線程的方式編寫的服務(wù)器應(yīng)用程序能夠充分利用硬件資源進(jìn)行擴(kuò)展;
使現(xiàn)有的使用 java.lang.Thread API 的代碼能夠以最小的修改支持虛擬線程;
使現(xiàn)有的 JDK 工具能夠輕松進(jìn)行虛擬線程的故障排除、調(diào)試和性能分析。
虛擬線程是 java.lang.Thread 類的一個(gè)實(shí)例,它在底層的操作系統(tǒng)線程上運(yùn)行 Java 代碼,但并不會(huì)在整個(gè)生命周期中占用這個(gè)操作系統(tǒng)線程。這意味著許多虛擬線程可以在同一個(gè)操作系統(tǒng)線程上運(yùn)行其 Java 代碼,有效地共享該線程。平臺(tái)線程會(huì)獨(dú)占一個(gè)寶貴的操作系統(tǒng)線程,虛擬線程則不會(huì)。虛擬線程的數(shù)量可以遠(yuǎn)遠(yuǎn)大于操作系統(tǒng)線程的數(shù)量。虛擬線程是由 JDK 提供的輕量級(jí)線程實(shí)現(xiàn),而不是由操作系統(tǒng)提供的。
例如,為每個(gè)請求創(chuàng)建一個(gè)新的虛擬線程來進(jìn)行處理的代碼:
void handle(Request request, Response response) {
var url1 = ...
var url2 = ...
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
var future1 = executor.submit(() -> fetchURL(url1));
var future2 = executor.submit(() -> fetchURL(url2));
response.send(future1.get() + future2.get());
} catch (ExecutionException | InterruptedException e) {
response.fail(e);
}
}
String fetchURL(URL url) throws IOException {
try (var in = url.openStream()) {
return new String(in.readAllBytes(), StandardCharsets.UTF_8);
}
}
JEP 446:作用域值(預(yù)覽特性)
引入了作用域值,這些值可以安全高效地共享給方法,而不需要使用方法參數(shù)。特別是在使用大量虛擬線程時(shí),它們優(yōu)于線程局部變量。它是 ScopedValue<T> 類型的變量。
例如:
final static ScopedValue<...> V = ScopedValue.newInstance();
// In some method
ScopedValue.where(V, <value>)
.run(() -> { ... V.get() ... call methods ... });
// In a method called directly or indirectly from the lambda expression
... V.get() ...
JEP 453:結(jié)構(gòu)化并發(fā)(預(yù)覽特性)
引入結(jié)構(gòu)化并發(fā) API,簡化并發(fā)編程。結(jié)構(gòu)化并發(fā)將在不同線程中運(yùn)行的相關(guān)任務(wù)組視為一個(gè)工作單元,從而簡化錯(cuò)誤處理和取消操作,進(jìn)而可靠性,并增強(qiáng)可觀測性。結(jié)構(gòu)化并發(fā) API 的主要類是位于 java.util.concurrent 包中的 StructuredTaskScope。
(三)Project Panama
JEP 442:外部的函數(shù)和內(nèi)存 API(第 3 次預(yù)覽)
Foreign Function & Memory API (FFM API) 位于 java.lang.foreign 包中,它定義了一系列類和接口,使Java程序能夠與Java運(yùn)行時(shí)之外的代碼和數(shù)據(jù)進(jìn)行互操作。通過有效地調(diào)用外部函數(shù)(即JVM之外的代碼),并安全地訪問外部內(nèi)存(即并非由JVM管理的內(nèi)存),該 API 使 Java 程序能夠調(diào)用本地庫并處理本地?cái)?shù)據(jù),而不會(huì)出現(xiàn) JNI 的脆弱性和危險(xiǎn)性。
JEP 448:向量 API(第6次 孵化特性)
引入VectorSpecies<E>,用于表達(dá)向量計(jì)算,這些計(jì)算可以在受支持的 CPU 架構(gòu)上在運(yùn)行時(shí)可靠地編譯為最佳的向量指令,從而實(shí)現(xiàn)優(yōu)于等效標(biāo)量計(jì)算的性能。
(四)核心類庫
JEP 431:有順序的集合
引入新的接口 SequencedCollection<E> 來表示具有明確定義的遍歷順序的集合。每個(gè)這樣的集合都有明確定義的第一個(gè)元素、第二個(gè)元素等,一直到最后一個(gè)元素。它還提供了統(tǒng)一的 API 來訪問它的第一個(gè)和最后一個(gè)元素,以及以相反的順序處理其元素。
(五)性能更新
JEP 439:分代 ZGC
擴(kuò)展 ZGC,使之在不同的代中維護(hù)年輕的對象和年老的對象,從而提高應(yīng)用程序的性能。這將允許 ZGC 更頻繁地收集年輕對象,因?yàn)樗鼈兏菀自缧r(shí)候被回收。
JEP 452:密鑰封裝機(jī)制 API
引入了一個(gè)新類 KEM,用于密鑰封裝機(jī)制,這是一種使用公鑰加密來保護(hù)對稱密鑰的加密技術(shù)。
(六)維護(hù)與棄用
JEP 449:棄用 32 位 x86 平臺(tái)構(gòu)建版本,將來移除
棄用 Windows 32 位 x86 構(gòu)建版本,并計(jì)劃在未來的版本中移除。
JEP 451:準(zhǔn)備禁止動(dòng)態(tài)加載代理
在將代理動(dòng)態(tài)加載到運(yùn)行中的 JVM 時(shí)發(fā)出警告。這些警告旨在讓用戶有所準(zhǔn)備,以便在將來的發(fā)布版本中,默認(rèn)禁止動(dòng)態(tài)加載代理。在任何發(fā)布版本中,通過服務(wù)工具(serviceability tool)在啟動(dòng)時(shí)加載代理不會(huì)引發(fā)警告。
Java 21 的新特性就介紹到這里,趕快下載體驗(yàn)吧!
JDK下載地址:
https://www.oracle.com/java/technologies/downloads/
推薦閱讀:
互聯(lián)網(wǎng)初中高級(jí)大廠面試題(9個(gè)G) 內(nèi)容包含Java基礎(chǔ)、JavaWeb、MySQL性能優(yōu)化、JVM、鎖、百萬并發(fā)、消息隊(duì)列、高性能緩存、反射、Spring全家桶原理、微服務(wù)、Zookeeper......等技術(shù)棧!
?戳閱讀原文領(lǐng)取! 朕已閱

