Java 每半年就會更新一次新特性,再不掌握就要落伍了:Java15 的新特性
你好,我是看山。
本文收錄在 《從小工到專家的 Java 進階之旅》 系列專欄中。
從 2017 年開始,Java 版本更新策略從原來的每兩年一個新版本,改為每六個月一個新版本,以快速驗證新特性,推動 Java 的發展。從 《JVM Ecosystem Report 2021》 中可以看出,目前開發環境中有近半的環境使用 Java8,有近半的人轉移到了 Java11,隨著 Java17 的發布,相信比例會有所變化。
因此,準備出一個系列,配合示例講解,闡述各個版本的新特性。
概述
Java15 是在 2020 年 9 月發布的一個短期版本,新增特性如下:
JEP 339:Edwards-Curve 數字簽名算法
JEP 360:密封的類和接口(預覽功能)
JEP 371:隱藏類
JEP 372:移除 Nashorn JavaScript 引擎
JEP 373:重新實現 DatagramSocket 接口
JEP 374:禁用偏向鎖
JEP 375:instanceof 匹配模式(第二版預覽功能)
JEP 377:ZGC:可伸縮低延遲垃圾收集器
JEP 378:文本塊
JEP 379:Shenandoah:低暫停時間垃圾收集器
JEP 381:移除 Solaris 和 SPARC 端口 API
JEP 383:外部存儲器訪問 API(第二版孵化功能)
JEP 384:Record 類型(第二版預覽功能)
JEP 385:廢除 RMI Activation
接下來我們一起看看這些特性。
Edwards-Curve 數字簽名算法(JEP 339)
Edwards-Curve 數字簽名算法(EdDSA),一種根據 RFC 8032 規范所描述的 Edwards-Curve 數字簽名算法(EdDSA)實現加密簽名。
EdDSA 是一種現代的橢圓曲線方案,與 JDK 中的現有簽名方案相比,EdDSA 具有更高的安全性和性能,因此備受關注。它已經在 OpenSSL 和 BoringSSL 等加密庫中得到支持,目前在區塊鏈領域用的比較多。
我們看下官方給的例子:
byte[] msg = "Hello, World!".getBytes(StandardCharsets.UTF_8); // example: generate a key pair and sign KeyPairGenerator kpg = KeyPairGenerator.getInstance("Ed25519"); KeyPair kp = kpg.generateKeyPair(); // algorithm is pure Ed25519 Signature sig = Signature.getInstance("Ed25519"); sig.initSign(kp.getPrivate()); sig.update(msg); System.out.println(Hex.encodeHexString(sig.sign())); // example: use KeyFactory to contruct a public key KeyFactory kf = KeyFactory.getInstance("EdDSA"); NamedParameterSpec paramSpec = new NamedParameterSpec("Ed25519"); EdECPublicKeySpec pubSpec = new EdECPublicKeySpec(paramSpec, new EdECPoint(true, new BigInteger("1"))); PublicKey pubKey = kf.generatePublic(pubSpec); System.out.println(pubKey.getAlgorithm()); System.out.println(Hex.encodeHexString(pubKey.getEncoded())); System.out.println(pubKey.getFormat());
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
例子中 Ed25519 是使用 SHA-512(SHA-2)和 Curve25519 的 EdDSA 簽名方案。旨在提供與高質量 128 位對稱密碼相當的抗攻擊能力,公鑰長度為 256 位,簽名長度為 512 位。
隱藏類(JEP 371)
Java15 引入了一個新的特性:隱藏類(Hidden Classes),一個專為框架而設計的特性。大多數開發人員不會直接使用這個特性,一般是通過動態字節碼或 JVM 語言來使用隱藏類。
隱藏類有下面三個特點:
不可發現:在運行時生成內部類對象;
訪問控制:只能通過反射訪問,不能直接被其他字節碼訪問;
較短的生命周期:可獨立于其他類加載、卸載,且效率很高,能夠減少框架的內存占用。
隱藏類的功能特性還是比較有意思的,會涉及類加載、卸載、不可見、反射等很多內容,后續會開文單獨聊,文章會放在 從小工到專家的 Java 進階之旅 專欄中。
重新實現 DatagramSocket 接口(JEP 373)
老的 DatagramSocket API 在 Java15 中被重寫,是繼 Java14 重寫 Socket API 的后續不走。這個特性是 Loom 項目的先決條件。
目前,DatagramSocket和MulticastSocket將所有的套接字委托為java.net.DatagramSocketImpl的實現,根據不同的平臺,Unix 平臺使用PlainDatagramSocketImpl,Windows 平臺使用TwoStackPlainDatagramSocketImpl和DualPlainDatagramSocketImpl。抽象類DatagramSocketImpl是 Java1.1 提供的,功能很少且有一些過時方法,阻礙了 NOI 的實現。
類似于 Java14 中對 Socket API 的重寫(參見 Java14 新特性),會在DatagramSocket內部封裝一個DatagramSocket實例,將所有調用直接委托給該實例。包裝實例或者使用 NIO 的DatagramChannel::socket創建套接字,或者是使用原始DatagramSocket類的實現DatagramSocketImpl實現功能(用于實現向后兼容)。
我們可以看下新的依賴圖:
禁用偏向鎖(JEP 374)
在 Java15 中,默認禁用偏向鎖,棄用了所有相關命令行選項。
偏向鎖是 HotSpot 中一種用于減少非競爭鎖定開銷的優化技術,不過在如今的應用程序中,優化增益不太明顯了。
根據官方說法,使用偏向鎖增益最多的是大量使用早期同步組件(比如Hashtable、Vector等),隨著新的 API 實現和針對多線程場景引入的支持并發的數據結構,偏向鎖的鎖定及撤銷,會帶來性能的開銷,從而是優化收益降低。
而且隨著越來越多的功能特性引入,偏向鎖在同步子系統中引入的大量代碼,侵入 HotSpot 其他組件,帶來代碼的復雜性和維護成本,成為代碼優化的阻礙。所以官方要將其移除。
不過,有些應用在禁用偏向鎖后會出現性能下降,可以使用-XX:+UseBiasedLocking手動開啟。
ZGC:可伸縮低延遲垃圾收集器(JEP 377)
ZGC 是在 Java11 引入的(參見 Java11 新特性),一直處于試驗階段,想要體驗,需要在參數中使用-XX:+UnlockExperimentalVMOptions -XX:+UseZGC組合啟用,在 Java15 中,ZGC 成為正式特性,想要使用可以直接用命令-XX:+UseZGC就行。
ZGC 是一個重新設計的并發的垃圾回收器,可以極大的提升 GC 的性能,支持任意堆大小而保持穩定的低延遲。從 https://openjdk.java.net/jeps/333 給出的數據可以看出來,在 128G 堆大小的測試中,ZGC 優勢明顯,找了一張網上的圖片:
雖然 ZGC 愿景很好,但是還有很長的路要走,所以默認的垃圾收集器還是 G1。
Shenandoah:低暫停時間垃圾收集器(JEP 379)
Shenandoah 是在 Java12 引入的(參見)Java12 的新特性,本次和 ZGC 一起轉正。同樣的,想要使用 Shenandoah,不再需要參數-XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC組合,只使用-XX:+UseShenandoahGC即可。需要注意的是,Shenandoah 只在 OpenJDK 中提供,OracleJDK 中并不包含。
文本塊(JEP 378)
文本塊是千呼萬喚終于轉正,在 Java13 中首次引入(參見 Java13 的新特性),在 Java14 中又增加了預覽特性(參見 Java14 的新特性),終于在 Java15 確定下來,可以放心使用了。
我們再復習一下:
@Test void testTextBlock() { final String singleLine = "你好,我是看山,公眾號「看山的小屋」。這行沒有換行,而且我的后面多了一個空格 \n 這次換行了"; final String textBlockSingleLine = """ 你好,我是看山,公眾號「看山的小屋」。\ 這行沒有換行,而且我的后面多了一個空格、s 這次換行了"""; Assertions.assertEquals(singleLine, textBlockSingleLine); }
1
2
3
4
5
6
7
8
9
10
這個功能特性是代碼可讀性的優化。
預覽
密封類和接口(JEP 360)
目前,Java 沒有提供對繼承的細粒度控制,只有 public、protected、private、包內控制四種非常粗粒度的控制方式。
為此,密封類的目標是允許單個類聲明哪些類型可以用作其子類型。這也適用于接口,并確定哪些類型可以實現它們。該功能特性新增了sealed和non-sealed修飾符和permits關鍵字。
我們可以做如下定義:
public sealed class Person permits Student, Worker, Teacher {} public sealed class Student extends Person permits Pupil, JuniorSchoolStudent, HighSchoolStudent, CollegeStudent, GraduateStudent {} public final class Pupil extends Student {} public non-sealed class Worker extends Person {} public class OtherClass extends Worker {} public final class Teacher extends Person {}
1
2
3
4
5
6
7
8
9
10
11
12
我們可以先定義一個sealed修飾的類Person,使用permits指定被繼承的子類,這些子類必須是使用final或sealed或non-sealed修飾的類。其中Student是使用sealed修飾,所以也需要使用permits指定被繼承的子類。Worker類使用non-sealed修飾,成為普通類,其他類都可以繼承它。Teacher使用final修飾,不可再被繼承。
從類圖上看沒有太多區別:
但是從功能特性上,起到了很好的約束作用,我們可以放心大膽的定義可以公開使用,但又不想被非特定類繼承的類了。
instanceof 模式匹配-預覽第二版(JEP 375)
instanceof 模式匹配首先在 Java14 中提供預覽功能(參見 Java14 特性),可以提供instanceof更加簡潔高效的實現,在 Java15 中沒有新增特性,主要是為了再次收集反饋,根據結果看,大家還是很期待這個功能,在 Java16 中正式提供。
我們再簡單看下instanceof的改進:
@Test void test1() { final Object obj1 = "Hello, World!"; int result = 0; if (obj1 instanceof String str) { result = str.length(); } else if (obj1 instanceof Number num) { result = num.intValue(); } Assertions.assertEquals(13, result); }
1
2
3
4
5
6
7
8
9
10
11
12
Record 類型-預覽第二版(JEP 384)
Record 類型用來增強 Java 語言特性,充當不可變數據載體。在 Java14 中提供預覽功能(參見 Java14 新特性),在 Java15 中提供第二次預覽,這次預覽的目標是收集用戶反饋。
比如,我們定義一個Person類:
public record Person(String name, String address) { }
1
2
我們轉換為之前的定義會是一坨下面這種代碼:
public final class PersonBefore14 { private final String name; private final String address; public PersonBefore14(String name, String address) { this.name = name; this.address = address; } public String name() { return name; } public String address() { return address; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } PersonBefore14 that = (PersonBefore14) o; return Objects.equals(name, that.name) && Objects.equals(address, that.address); } @Override public int hashCode() { return Objects.hash(name, address); } @Override public String toString() { return "PersonBefore14{" + "name='" + name + '\'' + ", address='" + address + '\'' + '}'; } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
Record 類型特性有四個特性:
設計一個面向對象的結構,表達簡單的信息聚合;
幫助開發人員專注于建模不可變數據,而不是可擴展的行為;
自動實現數據驅動的方法,比如equals、getter、setter等方法;
保留長期存在的 Java 規范,比如遷移兼容性。
我們不能將 Record 類型簡單的理解為去除“樣板化”代碼的功能,它不是解決 JavaBean 命名約定的中很多模板化方法的冗余繁雜問題,它的目標不是類似 Lombok 等工具自動生成代碼的功能,是從開發人員專注模型的角度出發的。
孵化
外部存儲器訪問 API-孵化第二版(JEP 383)
外部存儲器訪問 API 在 Java14 開始孵化(參見 Java14 新特性),在 Java15 中繼續孵化狀態,這個版本中增加了幾個特性:
新的VarHandleAPI,用于定制內存訪問句柄;
支持Spliterator接口實現并行處理內存段;
增強了對映射內存段的支持;
能夠像本機調用一樣操作或間接操作內存地址。
外部內存通常是說那些獨立 JVM 之外的內存區域,可以不受 JVM 垃圾收集的影響,通常能夠處理較大的內存。
這些新的 API 雖然不會直接影響多數的應用類開發人員,但是他們可以在內存的第三方庫中提供支持,包括分布式緩存、非結構化文檔存儲、大型字節緩沖區、內存映射文件等。
其他
移除 Nashorn JavaScript 引擎(JEP 372)
Nashorn JavaScript 引擎最初在 Java8 中引入(參見 Java8 新特性),在 Java11 被標記為過期,在 Java15 中被刪除,包括 Nashorn JavaScript 引擎、API、jjs 工具等內容。
Nashorn JavaScript 引擎是一個 JavaScript 腳本引擎,用來取代 Rhino 腳本引擎,對 ECMAScript-262 5.1 有完整的支持,增強了 Java 和 JavaScript 的兼容性,而且有很強的性能。
隨著 GraalVM 和其他虛擬機技術最近的引入,Nashorn 引擎不再在 JDK 生態系統中占有一席之地。而且,ECMAScript 腳本語言結構、API 改變速度太快,Nashorn JavaScript 引擎維護成本太高,所以,直接刪了。
移除 Solaris 和 SPARC 端口 API(JEP 381)
Solaris 和 SPARC 都已被 Linux 操作系統和英特爾處理器取代。放棄對 Solaris 和 SPARC 端口的支持將使 OpenJDK 社區的貢獻者能夠加速開發新功能,從而推動平臺向前發展。
Solaris 和 SPARC 端口 API 在 Java14 中標記過時,在 Java15 中徹底移除。僅僅半年就痛下殺手,可見社區對于維護這些 API 深受折磨。
廢除 RMI Activation(JEP 385)
RMI Activation 在 Java15 中標記為廢除,會在未來版本刪除。之所以被刪除,是因為在現代的 web 應用中,已經不需要這種激活機制,繼續維護,增加了 Java 開發人員的維護負擔。在 Java8 的時候,已經將其設置為非必選項。
從開發系統的角度看,雖然 RMI Activation 是一個還不錯的設計,但是已經有其他替代方案,繼續維護開發下去,成本收益完全不匹配,及早舍棄,可以選擇更加優秀的方案。有些類似于零邊際成本的思想。
文末總結
本文介紹了 Java15 新增的特性,完整的特性清單可以從 https://openjdk.java.net/projects/jdk/15/ 查看。后續內容會發布在 從小工到專家的 Java 進階之旅 系列專欄中。
青山不改,綠水長流,我們下次見。
推薦閱讀
一文掌握 Java8 Stream 中 Collectors 的 24 個操作
一文掌握 Java8 的 Optional 的 6 種操作
使用 Lambda 表達式實現超強的排序功能
Java8 的時間庫(1):介紹 Java8 中的時間類及常用 API
Java8 的時間庫(2):Date 與 LocalDate 或 LocalDateTime 互相轉換
Java8 的時間庫(3):開始使用 Java8 中的時間類
Java8 的時間庫(4):檢查日期字符串是否合法
Java8 的新特性
Java9 的新特性
Java10 的新特性
Java11 中基于嵌套關系的訪問控制優化
Java11 的新特性
Java12 的新特性
Java13 的新特性
Java14 的新特性
從小工到專家的 Java 進階之旅
你好,我是看山。游于碼界,戲享人生。如果文章對您有幫助,請、、關注。我還整理了一些精品學習資料,關注公眾號「看山的小屋」,回復“資料”即可獲得。
個人主頁:https://www.howardliu.cn
個人博文:Java 每半年就會更新一次新特性,再不掌握就要落伍了:Java15 的新特性
CSDN 主頁:https://kanshan.blog.csdn.net/
CSDN 博文:Java 每半年就會更新一次新特性,再不掌握就要落伍了:Java15 的新特性
Java JVM
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。