Java 23 :有何新變化?

如今 Java 23 已完成功能特性開發(fā)(在撰寫本文時(shí)處于第二階段降速期),是時(shí)候來了解一下這個(gè)新版本為我們開發(fā)者帶來的所有功能了。
 
首先,發(fā)生了一件據(jù)我所知在 Java 版本發(fā)布中從未發(fā)生過的事情:一個(gè)預(yù)覽功能被移除了!在 Java 21 中作為預(yù)覽功能出現(xiàn)的字符串模板功能已被移除。該功能將進(jìn)行全面重新設(shè)計(jì),因?yàn)樗l(fā)了很多爭(zhēng)議,似乎也沒有達(dá)到社區(qū)的期望。
 
定義預(yù)覽功能流程的 JEP 12 明確指出,預(yù)覽功能可以由功能所有者自行決定移除,無需新的 JEP。
 
最終,JEP 所有者必須決定預(yù)覽功能的命運(yùn)。如果決定移除預(yù)覽功能,那么所有者必須在 JBS 中提交一個(gè)問題,以便在下次 JDK 功能發(fā)布中移除該功能;無需新的 JEP。
這里就是這么做的。
 
有關(guān)字符串模板移除的更多信息,請(qǐng)參見 JDK - 8329949 問題單。
 
JEP 455—— 模式、instanceof 和 switch 中的基本類型(預(yù)覽)

這一預(yù)覽功能為 instanceof 和 switch 增加了對(duì)基本類型的支持,并增強(qiáng)了模式匹配以支持基本類型模式:在 instanceof 中、在 switch 語句中以及在記錄解構(gòu)中。
 
現(xiàn)在,switch 支持所有基本類型。
 
示例:
 
long l =...;
switch (l) {
    case 1L              ->...;
    case 2L              ->...;
    case 10_000_000_000L ->...;
    default ->...;
}
 
現(xiàn)在我們可以對(duì)所有基本類型使用 instanceof。
 
來自 JEP 的示例:
if (i instanceof byte) {  // i 的值適合存儲(chǔ)在一個(gè)字節(jié)中
   ... (byte)i...       // 需要傳統(tǒng)的強(qiáng)制類型轉(zhuǎn)換
}
但最有趣的是對(duì)模式匹配的支持。以下是一些現(xiàn)在可以在何處對(duì)基本類型使用模式匹配的示例。
 
來自 JEP 的在 switch 中對(duì)基本類型進(jìn)行模式匹配的示例:
 
switch (x.getStatus()) {
    case 0 -> "okay";
    case 1 -> "warning";
    case 2 -> "error";
    case int i -> "unknown status: " + i;
}
 
還可以通過 when 子句支持守衛(wèi):
 
switch (x.getStatus()) {
    case 0 -> "okay";
    case 1 -> "warning";
    case int i when i > 1 && i < 100 -> "client error: " + i;
    case int i when i > 100 -> "server error: " + i;
}
 
 
來自 JEP 的在 instanceof 中對(duì)基本類型進(jìn)行模式匹配的示例:
 
if (i instanceof byte b) {
   ... b...
}
 
來自 JEP 的在解構(gòu)記錄時(shí)對(duì)基本類型進(jìn)行模式匹配的示例:
 
// JSON 沒有區(qū)分整數(shù)和雙精度數(shù),所以 JSON 數(shù)字應(yīng)該是雙精度數(shù)。
record JsonNumber(double number) implements JsonValue { }
var number = new JsonNumber(30);
// 以前我們只能通過其確切的組件類型(long)來解構(gòu)這個(gè)記錄,
// 現(xiàn)在我們可以解構(gòu)并匹配不同的基本類型
if (json instanceof JsonObject(int number)) {
    //...
}
 
這種演變需要在模式匹配中實(shí)現(xiàn)轉(zhuǎn)換規(guī)則,以便一個(gè)基本類型與另一個(gè)基本類型匹配,如在前面的示例中,30 匹配了一個(gè) int,即使記錄組件被定義為 double,目標(biāo)類型必須被模式測(cè)試覆蓋。在這里,30 被一個(gè) int 覆蓋。未被覆蓋的值將被拒絕。
 
有關(guān) JEP 455 的更多信息。
 
JEP 467——Markdown 文檔注釋

該功能允許你使用 Markdown 而不僅僅是 HTML 和 JavaDoc 標(biāo)簽的混合來編寫 JavaDoc 文檔注釋。
 
編寫 HTML 代碼并不總是容易的,而且在沒有渲染的情況下可讀性也不高,JavaDoc 標(biāo)簽有時(shí)使用起來很復(fù)雜。Markdown 是一種在沒有渲染的情況下也具有可讀性且易于使用的語言。將其用于 JavaDoc 注釋是一個(gè)很好的選擇。Markdown 支持使用 HTML 標(biāo)簽,提供了極大的靈活性,同時(shí)如果需要,仍然支持特定于 JavaDoc 的標(biāo)簽。
 
Markdown 注釋以三個(gè)斜杠開頭:///。
 
以下是來自 JEP 的一個(gè)示例:
/**
 * 返回對(duì)象的哈希碼值。此方法是為了哈希表(如由{@link java.util.HashMap}提供的那些)的利益而支持的。
 * <p>
 * {@code hashCode}的一般約定是:
 * <ul>
 * <li>在 Java 應(yīng)用程序的一次執(zhí)行過程中,每當(dāng)對(duì)同一對(duì)象多次調(diào)用它時(shí),只要在對(duì)象的{@code equals}比較中使用的信息沒有被修改,{@code hashCode}方法就必須始終返回相同的整數(shù)。這個(gè)整數(shù)在同一應(yīng)用程序的不同執(zhí)行之間不需要保持一致。</li>
 * <li>如果根據(jù){@link #equals(Object)}方法兩個(gè)對(duì)象相等,那么對(duì)這兩個(gè)對(duì)象中的每一個(gè)調(diào)用{@code hashCode}方法必須產(chǎn)生相同的整數(shù)結(jié)果。</li>
 * <li>如果根據(jù){@link #equals(Object)}方法兩個(gè)對(duì)象不相等,那么對(duì)這兩個(gè)對(duì)象中的每一個(gè)調(diào)用{@code hashCode}方法并不要求必須產(chǎn)生不同的整數(shù)結(jié)果。然而,程序員應(yīng)該意識(shí)到,為不相等的對(duì)象產(chǎn)生不同的整數(shù)結(jié)果可能會(huì)提高哈希表的性能。</li>
 * </ul>
 *
 * @implSpec
 * 在合理可行的范圍內(nèi),由類{@code Object}定義的{@code hashCode}方法為不同的對(duì)象返回不同的整數(shù)。
 *
 * @return 這個(gè)對(duì)象的哈希碼值。
 * @see     java.lang.Object#equals(java.lang.Object)
 * @see     java.lang.System#identityHashCode
 */

用 Markdown 可以這樣寫:

/// 返回對(duì)象的哈希碼值。此方法是為了哈希表(如由[java.util.HashMap]提供的那些)的利益而支持的。
///
/// `hashCode`的一般約定是:
///
///   - 在 Java 應(yīng)用程序的一次執(zhí)行過程中,每當(dāng)對(duì)同一對(duì)象多次調(diào)用它時(shí),只要在對(duì)象的`equals`比較中使用的信息沒有被修改,`hashCode`方法就必須始終返回相同的整數(shù)。這個(gè)整數(shù)在同一應(yīng)用程序的不同執(zhí)行之間不需要保持一致。
///   - 如果根據(jù)[equals][#equals(Object)]方法兩個(gè)對(duì)象相等,那么對(duì)這兩個(gè)對(duì)象中的每一個(gè)調(diào)用`hashCode`方法必須產(chǎn)生相同的整數(shù)結(jié)果。
///   - 如果根據(jù)[equals][#equals(Object)]方法兩個(gè)對(duì)象不相等,那么對(duì)這兩個(gè)對(duì)象中的每一個(gè)調(diào)用`hashCode`方法并不要求必須產(chǎn)生不同的整數(shù)結(jié)果。然而,程序員應(yīng)該意識(shí)到,為不相等的對(duì)象產(chǎn)生不同的整數(shù)結(jié)果可能會(huì)提高哈希表的性能。
///
/// @implSpec
/// 在合理可行的范圍內(nèi),由類`Object`定義的`hashCode`方法為不同的對(duì)象返回不同的整數(shù)。
///
/// @return 這個(gè)對(duì)象的哈希碼值。
/// @see     java.lang.Object#equals(java.lang.Object)
/// @see     java.lang.System#identityHashCode
有關(guān) JEP 467 的更多信息。
 
JEP 471—— 棄用 sun.misc.Unsafe 中的內(nèi)存訪問方法以準(zhǔn)備移除

正如其名稱所示,Unsafe 是一個(gè)內(nèi)部且不受支持的 JDK 類,調(diào)用它是不安全的。由于歷史原因,許多底層框架使用 Unsafe 進(jìn)行更快的內(nèi)存訪問。由于有了 VarHandle API(JEP 193,自 Java 9 起)和外部函數(shù)與內(nèi)存 API(JEP 454,自 Java 22 起)功能,現(xiàn)在有了 Unsafe 內(nèi)存訪問方法的替代品,它們同樣強(qiáng)大,但更安全且更受支持??偣灿谐^ Unsafe 的 87 個(gè)方法中的 79 個(gè)受到影響,這使我們更接近可以棄用并刪除整個(gè)類的地步!
 
棄用這些方法清楚地表明是時(shí)候使用這些替代品了!然而,我們大多數(shù)人不應(yīng)該看到這些變化,因?yàn)槌嗽诳蚣芑驇熘?,Unsafe 很少在其他地方被使用。
 
這些方法將逐步被降級(jí)和棄用:
 
  • 階段 1:在 Java 23(這個(gè) JEP)中棄用。
  • 階段 2:在 Java 24 或 25 中如果在運(yùn)行時(shí)使用則記錄警告。
  • 階段 3:在 Java 26 或更高版本中默認(rèn)拋出異常(行為可通過命令行選項(xiàng)修改)。
  • 階段 4 和 5:在 Java 26 之后移除這些方法(先移除堆上內(nèi)存訪問方法,然后移除堆外內(nèi)存訪問方法)。
 
有關(guān) JEP 471 的更多信息。
 
JEP 474——ZGC:默認(rèn)情況下的分代模式

ZGC 是一種垃圾收集器,旨在支持非常大的堆(數(shù) TB)且暫停時(shí)間非常短(毫秒級(jí))。
 
通過 JEP 439 在 Java 21 中添加分代堆使其能夠在消耗更少資源的同時(shí)支持不同的工作負(fù)載。
 
現(xiàn)在分代模式是默認(rèn)模式。
 
有關(guān) JEP 474 的更多信息。
 
JEP 476—— 模塊導(dǎo)入聲明(預(yù)覽)

在 Java 中,你可以導(dǎo)入:
  • 用語句import java.util.*;導(dǎo)入一個(gè)包中的所有類。
  • 用語句import java.util.Map;導(dǎo)入單個(gè)類。
  • 用語句import static org.junit.jupiter.api.Assertions.*;導(dǎo)入一個(gè)類的所有靜態(tài)方法和變量。
  • 用語句import static org.junit.jupiter.api.Assertions.assertTrue;導(dǎo)入單個(gè)靜態(tài)方法或變量。

然而,以前不可能用一個(gè)語句導(dǎo)入一個(gè)模塊的所有類?,F(xiàn)在可以用import module java.base;語句來實(shí)現(xiàn),這個(gè)語句可以在一個(gè)語句中導(dǎo)入從java.base模塊導(dǎo)出的所有包中的所有類,以及java.base模塊間接需要的模塊中的類。
 
有關(guān) JEP 476 的更多信息。
 
退出預(yù)覽的功能

在 Java 23 中,沒有以前處于預(yù)覽(或孵化模塊)的功能退出預(yù)覽或孵化。
 
當(dāng)然,除了我在介紹中提到的字符串模板功能,它已從預(yù)覽中移除且不知去向。
 
仍處于預(yù)覽的功能

以下功能仍處于預(yù)覽(或在孵化模塊中)。
 
  • JEP 466—— 類文件 API:第二次預(yù)覽,用于解析、生成和轉(zhuǎn)換 Java 類文件的標(biāo)準(zhǔn) API。根據(jù)用戶反饋進(jìn)行了改進(jìn)。在 Java 23 中,JDK 繼續(xù)向使用這個(gè)新 API 遷移。
  • JEP 469—— 向量 API:第八次孵化,用于表示向量計(jì)算的 API,在運(yùn)行時(shí)編譯為支持的 CPU 架構(gòu)的向量指令。沒有變化:JEP 同意向量 API 將繼續(xù)處于孵化狀態(tài),直到 Valhalla 項(xiàng)目的功能作為預(yù)覽可用。這是預(yù)期的,因?yàn)橄蛄?API 將能夠利用 Valhalla 項(xiàng)目預(yù)期的性能和內(nèi)存表示改進(jìn)。
  • JEP 473—— 流收集器:第二次預(yù)覽,通過支持自定義中間操作增強(qiáng)了 Stream API。沒有變化。
  • JEP 477—— 隱式聲明的類和實(shí)例主方法:第三次預(yù)覽,通過允許在隱式類(無需聲明)和void main()實(shí)例方法中定義簡(jiǎn)單程序,簡(jiǎn)化了簡(jiǎn)單程序的編寫。有兩個(gè)變化:隱式類自動(dòng)導(dǎo)入新的java.io.IO類的三個(gè)靜態(tài)方法print(Object)println(Object)readln(Object),并且它們根據(jù)需要自動(dòng)導(dǎo)入java.base模塊中的包中的類。
  • JEP 480—— 結(jié)構(gòu)化并發(fā):第三次預(yù)覽,一個(gè)新的 API,通過允許將多個(gè)并發(fā)任務(wù)視為單個(gè)處理單元,簡(jiǎn)化了多線程代碼的編寫。沒有變化。
  • JEP 481—— 作用域值:第三次預(yù)覽,允許在線程內(nèi)部和線程之間共享不可變數(shù)據(jù)。有一個(gè)小變化。
  • JEP 482—— 靈活的構(gòu)造函數(shù)體:第二次預(yù)覽,一個(gè)允許在父構(gòu)造函數(shù)之前調(diào)用指令的功能,只要它們不訪問當(dāng)前正在創(chuàng)建的實(shí)例。構(gòu)造函數(shù)現(xiàn)在可以在顯式調(diào)用構(gòu)造函數(shù)之前初始化同一類的字段。
 
雜項(xiàng)

對(duì) JDK 進(jìn)行了各種添加:
 
  • 隨著 JEP 477—— 隱式聲明的類和實(shí)例主方法的推出,除了新的IO類之外,相同的三個(gè)方法也被添加到了Console類中:print(Object)、println(Object)readln(Object)。
  • Console類增加了三個(gè)使用帶有區(qū)域設(shè)置格式化的字符串的新方法:format(Locale, String, Object)、printf(Locale, String, Object)readLine(Locale, String, Object)
  • Console.readPassword(Locale, String, Object):與Console.readPassword(String, Object)相同,但接受一個(gè)區(qū)域設(shè)置作為參數(shù)用于字符串本地化。
  • Inet4Address.ofPosixLiteral(String):根據(jù)以 POSIX 兼容形式inet_addr提供的 IPv4 地址的文本表示創(chuàng)建一個(gè)Inet4Address
  • java.text.NumberFormat及其后代增加了setStrict(boolean)isScript()方法,可用于更改格式化模式;默認(rèn)模式是嚴(yán)格模式。
  • Instant.until(Instant):計(jì)算到另一個(gè)Instant的持續(xù)時(shí)間。
 
以下方法已被移除,它們已被標(biāo)記為要?jiǎng)h除,并在以前的版本中已被降級(jí)為拋出異常:
 
  • Thread.resume()Thread.suspend()
  • ThreadGroup.resume()、ThreadGroup.stop()ThreadGroup.suspend()
 
所有新的 JDK 23 API 可以在《Java 版本年鑒 ——Java 23 中的新 API》中找到。
 
內(nèi)部變化、性能和安全性

并行垃圾收集器(Parallel GC)對(duì)其完全垃圾收集算法進(jìn)行了重新實(shí)現(xiàn),以使用更經(jīng)典的并行標(biāo)記 - 清除 - 壓縮算法。這與 G1 垃圾收集器使用的算法相同,在某些特定情況下優(yōu)化了性能,在使用并行垃圾收集器時(shí)減少了 1.5% 的堆使用量。在垃圾收集器方面還進(jìn)行了其他更改,可以在 Thomas Schatzl 的這篇文章中找到:《JDK 23 G1/Parallel/Serial GC 更改》。
 
我還沒有注意到其他值得注意的變化,但如果我發(fā)現(xiàn)更多變化,我會(huì)更新這篇文章。
 
JFR 事件

沒有新的 Java 飛行記錄器(JFR)事件。
 
你可以在頁面 “JFR 事件” 上找到此版本的 Java 支持的所有 JFR 事件。
 
結(jié)論

這個(gè)新版本的 Java 在新功能方面相當(dāng)稀少,目前正在開發(fā)的功能中很少有退出預(yù)覽的。
 
盡管在模式匹配中對(duì)基本類型的支持以及在 JavaDoc 中對(duì) Markdown 的支持是非常有趣的改進(jìn),但字符串模板的消失且沒有替代品意味著對(duì)這個(gè)期待已久的功能的支持還遙遙無期。
 
要查找 Java 23 中的所有更改,請(qǐng)參考發(fā)行說明。若你想提升Java技能,可關(guān)注我們的Java培訓(xùn)課程。