Java 23 到來!

OpenJDK Java 23 page

Java 23 於 9/17 正式到來了!本篇文章簡介了此版本中的最新功能,其內容依照 The Arrival of Java 23 的編排方式呈現。除了翻譯該篇文章中的部分內容外,也加上了各功能的補充說明。

由於本篇的目的是摘要新功能,因此不會有太深入的細節。大家可以快速地綜覽下列各項目裡的重點,並且看完本篇後對新版本能有大致的了解和掌握。之後老喬會再花些時間將各個功能逐一地介紹,到時候可以再挑選有興趣的功能做深入地研究學習。

前言

新版 Java 帶來了 12 項重大改進,其中包含 8 個預覽功能和 1 個孵化器功能。這些改進涵蓋了 Java 語言、API、性能以及 JDK 內建工具等各個層面。除了 Java 平台本身的強化外,Oracle JDK 中還納入了 Oracle GraalVM JIT 編譯器,成為 Oracle JDK 可用 JIT 編譯器的一部分。Graal JIT 能夠進一步提升 Java 應用程式的啟動速度和預熱時間,讓程式跑得更快更順暢。

本版是一個令人振奮的版本,它展現了 Java 持續進步的活力,為開發者帶來了更多可能性。無論你是 Java 新手還是老鳥,都值得深入探索各式新功能,讓你的程式設計之路更加順暢!

語言改進

JEP 455:模式、instanceofswitch 中的基礎型別匹配(預覽)

本功能解除了基礎型別在模式匹配中存在的幾個限制。處理基礎型別和物件型別的程式碼將會被簡化,不再需要特殊處理,使得這兩種型別可以用相同的方式應對。由於現在可以自動處理條件式轉型(值必須被檢查以確保它對目標型別有效),因此允許 instanceof 接受基礎型別的話可以進一步減少程式碼數量。

它增強了 Java 的模式匹配功能,並擴展 instanceofswitch,使其能夠在所有模式的上下文中使用基礎型別的模式匹配:

  • 基礎型別模式的增強:允許在模式匹配中適用更廣泛的候選基礎型別,例如 record Test(double d) 可以使用 x instanceof Test(int i) 去判定 d 值是否能轉型成 int
  • instanceof 的擴展:允許 instanceof 運算符與基礎型別一起使用,例如 if (x instanceof int i) { ... }
  • switch 的擴展:允許 switch 表達式處理所有基礎型別,包括 booleanfloatdoublelong,例如 case byte b -> ...

JEP 476:模組匯入宣告(預覽)

它允許開發者透過單一陳述句去匯入模組中的所有公開型別,以簡化模組化函式庫的重複使用。有了這項功能,初學者便能更輕鬆地使用第三方函式庫和基礎 Java 類別,而無需學習它們在套件階層中的位置。它提供了簡潔的方式,將匯入類別與套件的語句簡化成匯入模組,以減少樣板程式碼撰寫:

  • 引入新的 import module 語句,允許一次性匯入模組中的所有型別。例如:import module java.base; 將匯入 java.base 模組中導出的所有套件
  • 新語句可以與現有的 import 語句共存,並且不會影響現有程式碼的行為

JEP 477:隱式宣告類別和實例 Main 方法(第三次預覽)

本功能讓初學者能夠輕鬆地撰寫第一個 Java 程式,而不需要先全部理解較為複雜的程式語法和功能。簡易的程式可以使用精簡的宣告方式運行,並且在需要時能夠無縫擴展去使用更高級的功能。經驗豐富的開發人員也可以享受簡潔地編寫小型程式的樂趣。

  • 隱式宣告類別:如果一個 Java 原始檔中沒有使用 class 關鍵字明顯地宣告任何類別,編譯器會自動為該檔案建立一個隱式類別。類別名稱與檔案名稱相同(但不包括 .java 副檔名),並且它會是 final 且直接繼承自 Object
  • 實例 main 方法:允許在隱式宣告類別中定義一個非靜態的 main 方法,其方法簽章為 void main()void main(String[] args)
  • 自動匯入:隱式宣告類別會自動 import module java.base 模組中的所有型別(請參考上一節模組匯入宣告的內容)
  • 新增輸入輸出方法:新增並且 import static java.io.IO.*; 以提供 println(Object o)print(Object o)String readln(String prompt) 方法

JEP 482:彈性建構式主體(第二次預覽)

開發人員可以更自由地操作建構函式的行為,本功能允許一些特定的語句在明確的建構式呼叫(即 super(..)this(..))之前出現。因為它允許將判斷邏輯放在呼叫父類別的建構式之前,從而避免了將某些檢查和初始化邏輯分解到靜態方法和中間建構式中的必要。例如:如果建構式可以在呼叫父類別建構式之前先行驗證參數,那麼當參數無效時就能夠拋出異常並避免不必要的父類別實例化。

  • 提高可靠性:在呼叫父類別建構式或其他建構式之前檢查初始化欄位,確保衍生類別的狀態在父類別建構式執行時就已經準備就緒,以避免潛在的錯誤。尤其在方法覆寫的情況下,能確保子類別的欄位狀態在父類別方法執行前已經正確初始化
  • 程式碼更清晰:將欄位初始化邏輯集中在建構函式開頭,使程式碼結構更清晰,易於閱讀和維護

它讓 Java 建構式更加靈活,允許在調用其他建構式之前先初始化操作,從而提高程式碼的可靠性和可維護性。然而,開發人員在使用此功能時需要注意其潛在的複雜性和錯誤風險,並確保相容現有的程式碼。

函式庫

JEP 466:類別檔案 API(第二次預覽)

本 JEP 提供了一個用於解析、生成和轉換 Java 類別檔案的 API,並會依照 Java 虛擬機器規格所定義的格式做定期更新。此 API 可以緩解 Java 每六個月發布週期所帶來的窘境,因為快速發布迫使支援操作類別檔的函式庫需要快速反應以支援每個新的 Java 類別檔案版本。當此功能最終確定後,它可以讓 JDK 本身擺脫對第三方 ASM 程式庫的依賴。

它提供了標準化的 Java API 去簡化處理類別檔案的任務,使開發人員能夠更輕鬆地解析、修改、生成和轉換 Java 的類別檔案:

  • 解析:將類別檔案中的資訊(例如常數池、欄位、方法、屬性等)表示為不可變的 Java 物件,方便開發人員進行存取和操作
  • 生成:提供 Builder 來協助開發人員逐步構建類別檔案,並將其寫入輸出流或檔案
  • 轉換:允許開發人員修改現有的類別檔案,例如替換或刪除其中的元素

JEP 469:向量 API(第八次孵化)

開發人員可以使用 Vector API 去描述和表示向量計算。在支援的 CPU 架構上,於運行時能夠可靠地編譯成最佳向量指令,從而實現優於等效標量計算的性能。預計這個 API 將持續孵化(最多只進行少量更改)直到來自 Project Valhalla 的必要功能(基於值的類別和物件)成為預覽功能為止。

  • 引入一個 API 來清晰簡潔地表達各種向量計算,包括在循環內組成的向量操作序列,可能還包括控制流
  • 平台獨立:API 應該獨立於 CPU 架構,並能夠在支援向量指令的多個架構上實現
  • 提供了一組類別和方法,用於建立、操作和訪問向量
  • 支援各種向量操作,例如:加減乘除、比較、邏輯運算等

JEP 473:串流聚集器(第二次預覽)

這個功能讓 Stream API 能夠支援自定義的中間操作,使得串流管道能夠以現有的內建操作轉換資料。Stream gatherers 為中間操作提供了與終端收集器(collector)同樣多的靈活性,總共有五個內建的聚集器。它的目標如下:

  • 引入新的中間串流操作 Stream::gather(Gatherer),透過使用者定義的實體(聚集器 gatherer)來處理串流的元素
  • Gatherer 代表串流元素的轉換,它的介面是 java.util.stream.Gatherer,可以一對一、一對多、多對一或多對多的方式轉換
  • 可以構建高效、並行的串流,實現幾乎任何中間操作
  • Stream::gather(Gatherer) 之於中間操作,就像 Stream::collect(Collector) 之於終端操作

JEP 480:結構化並行處理(第三次預覽)

結構化並行處理能讓開發者將一組相關任務(運行在不同執行緒上)視為單一的工作單元,從而簡化錯誤處理和取消操作、提升可靠性,並加強可觀察性。這個 API 提倡一種並行編程風格,可以消除因取消和關閉而產生的常見風險,如執行緒洩漏和取消延遲。

  • 簡化並行程式設計,特別是在使用虛擬執行緒時
  • 提供一種結構化、可預測且易於推理的並行程式模型
  • 引入 StructuredTaskScope 類別以允許開發者將一個任務發展成一組並行的子任務且同時能彼此協調:
    • fork() 方法:創建和啟動子任務,並返回子任務的結果
    • join() 方法:等待所有子任務完成,並處理任何異常
    • 支援自動取消子任務:如果父任務被取消,則所有子任務也會被自動取消
    • 確保子任務在其父任務完成之前完成,並集中處理異常
  • 支援範圍值(JEP 481 Scoped Values)的繼承:子任務可以繼承父任務的範圍值綁定,從而簡化資料共享

JEP 481:範圍值(第三次預覽)

允許一個方法在執行緒內安全且有效率地與它的被調用者(無論是直接或間接調用)以及子執行緒共享不可變的資料。範圍值(Scoped values)比執行緒局部變數(thread-local variables)更容易理解,特別是和虛擬執行緒(JEP 444)和結構化並行處理(JEP 480)一起使用時。它們具有更低的空間和時間成本。

  • 引入 ScopedValue 類型,作為共享資料的容器
  • 提供 ScopedValue.runWhere(key, value, op) 方法(等價於 ScopedValue.where(key, value).run(op) 語句 ),用於將 ScopedValue 與特定值綁定,並在該綁定範圍內執行程式碼
  • 子執行緒可以繼承父執行緒的 ScopedValue 綁定

性能

JEP 474:ZGC 預設啟動分代模式

本功能將 ZGC 的預設模式切換為分代模式。在 JDK 21 引入分代 ZGC 的功能之後,根據各方的使用反饋和內部測試,都證實了它在大部分場景中的表現都比未分代 ZGC 更好。因此我們打算在未來的 JDK 版本中移除未分代模式的 ZGC:

  • ZGenerational 選項的預設值從 false 改為 true,使分代 ZGC 成為預設模式
  • 棄用 ZGenerational 選項,從而棄用未分代模式
  • 如果透過命令列選項明確啟用未分代模式,則會發出警告訊息

內建工具

JEP 467:Markdown 文件註解

這個功能允許使用 Markdown 來撰寫 JavaDoc 文件註釋,而不再僅限於 HTML 和 JavaDoc @-tags 的混合使用。使用新的格式能讓原始碼中的文件更容易閱讀和理解。

  • 允許開發人員使用 Markdown 語法(如標題、列表、連結、強調等)來編寫 JavaDoc 註釋
  • 引入 /// 作為 Markdown 註釋的開頭,以區別於傳統的 /** ... */ 註釋
  • 在 Markdown 註釋中仍然可以使用 HTML 和 JavaDoc 標籤,以保留現有的功能
  • 可使用 Markdown 連結語法來連結到程式元素(例如類別、方法、欄位等)
  • 支援使用 GitHub Flavored Markdown (GFM) 的表格語法 (|) 來建立表格

管理工作

JEP 471:棄用 sun.misc.Unsafe 中的記憶體存取方法

在 JDK 9 (JEP 260) 、 JDK 16 (JEP 396) 和 JDK 17 (JEP 403) 中,一些早期的方法已受到使用限制,但仍然可以使用少部分不受支援的內部 API。主要是因為它們已被廣泛使用,並且沒有其他替代的 API。JDK 的目標是最終為它們提供新的支援 API,然後限制存取這些不受支援的內部 API。隨著 JDK 22 中引入 Foreign Function and Memory API 能有效地存取外部記憶體,因此本功能用來宣告未來將移除 sun.misc.unsafe 中用於外部記憶體存取的不安全方法。

Java 23 總結

本版帶來了許多好用的新功能,包括不少預覽功能。它並非長期支援(LTS)的版本之一,因此只會有六個月的支援。老喬的建議是可以下載並學習各種功能操作,但或許不適合用在需要長期穩定運行的正式產品域環境上。畢竟等到明年三月 Java 24 釋出後,它就不再處於維護階段了。

本篇文章的內容為老喬原創、二創或翻譯而來。雖已善盡校對、順稿與查核義務,但人非聖賢,多少仍會有疏漏之處難以避免。如果大家有任何問題、建議或指教,都歡迎在底下留言與老喬討論!

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

16 + two =

返回頂端