
JDK 15已經在2020年9月15日發布!詳情見 JDK 15 官方計劃,下面是對 JDK 15 所有新特性的詳細決議!
官方計劃
- 2019/12/12 Rampdown Phase One (fork from main line)
- 2020/06/11 Rampdown Phase One (fork from main line)
- 2020/07/16 Rampdown Phase Two
- 2020/08/06 Initial Release Candidate
- 2020/08/20 Final Release Candidate
- 2020/09/15 General Availability
特性預覽
- 339: 愛德華茲曲線數字簽名演算法(EdDSA)
- 360: Sealed Classes (Preview)
- 371: Hidden Classes
- 372: 移除 Nashorn JavaScript 引擎
- 373: 重新實作 DatagramSocket API
- 374: 禁用偏向鎖
- 375: instanceof的模式匹配(Second Preview)
- 377: ZGC: 可擴展的低延遲垃圾收集器
- 378: 文本塊
- 379: Shenandoah: 低暫停時間的垃圾收集器
- 381: 洗掉 Solaris 和 SPARC Ports
- 383: 外部存盤器訪問API (Second Incubator)
- 384: Records (Second Preview)
- 385: 廢棄 RMI Activation
深入理解新特性
339: 愛德華茲曲線數字簽名演算法(EdDSA)
JEP 339: Edwards-Curve Digital Signature Algorithm (EdDSA)
與其他簽名方案相比,EdDSA 具有更高的安全性和性能,并且已有很多其他加密庫(如 OpenSSL 和 BoringSSL)支持此簽名方案,EdDSA 是 TLS 1.3的可選組件,且是 TLS 1.3 中僅有的三種簽名方案之一,用戶可以不必再使用第三方庫了,
360: Sealed Classes (Preview)
JEP 360: Sealed Classes (Preview)
為什么需要此特性
在 Java 語言中,代碼的重用是通過類的繼承實作的:多個子類可以繼承同一個超類(并重用超類的方法),但是重用代碼并不是類層次結構的唯一目的,有時類層次結構僅僅是對某個領域的建模,以這種方式使用類層次結構時,限制子類集合可以簡化建模,
比如在圖形庫中,Shape 類的開發者可能只希望有限個數的類擴展 Shape 類,開發者并不想為未知子類撰寫防御代碼,以前的 Java 并不會限制 Shape 類的擴展屬性,Shape 類可以擁有任意數量的子類,在封閉類(Sealed Classes)中,類層次結構是封閉的,但是代碼仍然可以在有限范圍內重用,
特性描述
通過 sealed 修飾符將一個類宣告為密封類,permits子句指定允許擴展密封類的類,例如下面的 Shape 類宣告指定了三個可擴展的子類,
package com.example.geometry;
public abstract sealed class Shape
permits Circle, Rectangle, Square {...}
在子類數量很少時,在密封類的源檔案中宣告子類會很方便,當以這種方式宣告子類時,密封類可以省略 allows 子句,同時 Java 編譯器將從源檔案中推斷允許的子類,例如下面的例子:
package com.example.geometry;
abstract sealed class Shape {...}
... class Circle extends Shape {...}
... class Rectangle extends Shape {...}
... class Square extends Shape {...}
密封類可以讓代碼更簡潔,代碼可以明確推斷出所有允許的子類,比如傳統的 if-else 和 instanceof 代碼編譯器分析起來很困難,無法確定子句是否覆寫了所有允許的子類,下面的方法會導致編譯期錯誤:
int getCenter(Shape shape) {
if (shape instanceof Circle) {
return ... ((Circle)shape).center() ...
} else if (shape instanceof Rectangle) {
return ... ((Rectangle)shape).length() ...
} else if (shape instanceof Square) {
return ... ((Square)shape).side() ...
}
}
在之后支持模式匹配的版本中,編譯器可以直接推斷出 Shape 所有允許的子類,不需要 default 陳述句,此外,如果缺少子類的任意一個,編譯器就會報錯,
int getCenter(Shape shape) {
return switch (shape) {
case Circle c -> ... c.center() ...
case Rectangle r -> ... r.length() ...
case Square s -> ... s.side() ...
};
}
JDK 中的密封類
package java.lang.constant;
public sealed interface ConstantDesc
permits String, Integer, Float, Long, Double,
ClassDesc, MethodTypeDesc, DynamicConstantDesc {...}
// ClassDesc is designed for subclassing by JDK classes only
public sealed interface ClassDesc extends ConstantDesc
permits PrimitiveClassDescImpl, ReferenceClassDescImpl {...}
final class PrimitiveClassDescImpl implements ClassDesc {...}
final class ReferenceClassDescImpl implements ClassDesc {...}
// MethodTypeDesc is designed for subclassing by JDK classes only
public sealed interface MethodTypeDesc extends ConstantDesc
permits MethodTypeDescImpl {...}
final class MethodTypeDescImpl implements MethodTypeDesc {...}
// DynamicConstantDesc is designed for subclassing by user code
public non-sealed abstract class DynamicConstantDesc implements ConstantDesc {...}
JVM 支持
JVM 在運行時識別密封類和介面,并防止未經授權的子類和子介面擴展密封類,
盡管 sealed 關鍵字是類修飾符,但是 ClassFile 中并沒有 ACC_SEALED 標志,相反,密封類的類檔案具有 PermittedSubclasses屬性,該屬性隱式指示密封修飾符,并顯式指定允許的子類:
PermittedSubclasses_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 number_of_classes;
u2 classes[number_of_classes];
}
Reflection API
java.lang.Class 將增加如下 public 方法:
- java.lang.constant.ClassDesc[] getPermittedSubclasses()
- boolean isSealed()
371: Hidden Classes
JEP 371: Hidden Classes
為什么需要此特性
隱藏類不能被其他類的位元組碼直接使用,適合在運行時生成類、并通過反射間接使用隱藏類的框架,
特性描述
許多機遇 JVM 構建的語言都通過動態生成類,提高靈活性和效率,比如在 Java 語言中,javac 不會在編譯時將 lambda 運算式轉換成特殊的類檔案,而是保存在位元組碼中,該位元組碼可以把 lambda 運算式動態生成為相應的物件,同樣地,非 Java 語言經常通過運行時生成動態代理,來實作語言的高級特性,
語言的實作者通常希望將動態生成的類,在邏輯上成為靜態生成類的實作的一部分:
- 不可發現,僅通過名字就發現該類是不必要且有害的,因為這破壞了
動態生成類僅是靜態生成類的實作細節這一目標, - 訪問控制,可能希望將靜態生成類的訪問控制擴展到動態生成類,
- 生命周期,動態生成類的生命周期可能很短,在靜態生成類中保留它們會占用不必要的記憶體,針對這種情況的現有解決方案(類加載器)不僅麻煩,而且效率低下,
不幸的是,類定義的標準 API(ClassLoader::defineClass 和 Lookup::defineClass)并不在意該類的位元組碼是動態生成(在運行時)還是靜態生成(在編譯時)的,如果標準 API 可以定義無法被發現,且具有有限生命周期的隱藏類,則動態生成類的 JDK 內部和外部框架都可以定義隱藏類,這樣做可以提高 JVM 語言實作的效率,比如:
- java.lang.reflect.Proxy 可以定義隱藏類作為實作代理介面的代理類;
- java.lang.invoke.StringConcatFactory 可以生成隱藏類來保存常量連接方法;
- java.lang.invoke.LambdaMetaFactory 可以生成隱藏的 nestmate 類,以容納訪問封閉變數的 lambda 主體,
372: 移除 Nashorn JavaScript 引擎
JEP 372: Remove the Nashorn JavaScript Engine
特性描述
Java 11 中已經將該引擎標記為廢棄,并明確表示要在將來的版本中洗掉它,Nashorn JavaScript 引擎最開始是 JDK 8 通過 JEP 174 繼承的,用來代替 Rhino 腳本引擎,當時 Nashorn JavaScript 引擎是 ECMAScript-262 5.1 標準的完整實作,但是隨著 ECMAScript 語言構造以及 API 的修改,我們發現 Nashorn 難以維護,
JDK 的兩個模塊會永久洗掉:
- jdk.scripting.nashorn
- jdk.scripting.nashorn.shell
373: 重新實作 DatagramSocket API
JEP 373: Reimplement the Legacy DatagramSocket API
Java.net.DatagramSocket 和 java.net.MulticastSocket API 重構了基礎實作,使得其更易于維護和除錯,新的實作能更好地適應虛擬執行緒的作業,
374: 禁用偏向鎖
JEP 374: Disable and Deprecate Biased Locking
特性描述
默認情況下禁用偏向鎖,并廢棄所有相關的命令列,
為什么需要此特性
偏向鎖是 HotSpot 虛擬機使用的一項優化技術,能夠減少無競爭鎖定時的開銷,偏向鎖的目的是假定 monitor 一直由某個特定執行緒持有,直到另一個執行緒嘗試獲取它,這樣就可以避免獲取 monitor 時執行 cas 的原子操作,monitor 首次鎖定時偏向該執行緒,這樣就可以避免同一物件的后續同步操作步驟需要原子指令,從歷史上看,偏向鎖使得 JVM 的性能得到了顯著改善,
但是過去看到的性能提升,在現在看來已經不那么明顯了,受益于偏向鎖的應用程式,往往是使用了早期 Java 集合 API的程式(JDK 1.1),這些 API(Hasttable 和 Vector) 每次訪問時都進行同步,JDK 1.2 引入了針對單執行緒場景的非同步集合(HashMap 和 ArrayList),JDK 1.5 針對多執行緒場景推出了性能更高的并發資料結構,這意味著如果代碼更新為使用較新的類,由于不必要同步而受益于偏向鎖的應用程式,可能會看到很大的性能提高,此外,圍繞執行緒池佇列和作業執行緒構建的應用程式,性能通常在禁用偏向鎖的情況下變得更好,
偏向鎖為同步系統引入了許多復雜的代碼,并且對 HotSpot 的其他組件產生了影響,這種復雜性已經成為理解代碼的障礙,也阻礙了對同步系統進行重構,因此,我們希望禁用、廢棄并最終洗掉偏向鎖,
375: instanceof的模式匹配(Second Preview)
JEP 375: Pattern Matching for instanceof (Second Preview)
為什么需要此特性
通過對 instanceof 運算子進行模式匹配,來增加 Java 語言,模式匹配可以使應用程式更簡潔、安全地提取特定物件,
特性描述
很多程式都會判斷一個運算式是否具有某種型別或結構,然后有條件地進一步處理,比如下面的 instanceof-and-cast 用法:
if (obj instanceof String) {
String s = (String) obj;
// use s
}
上述代碼做了 3 件事:
- 判斷 obj 是否是 string 型別
- 將 obj 轉換為 string 型別
- 宣告了一個新的區域變數 s
這種模式很簡單,但是這樣的寫法并不是最優的,第 2 步的型別轉換是重復的,同時重復可能會帶來錯誤,模式匹配允許簡明地表達物件的所需“形狀”(模式),并允許各種陳述句和運算式針對其輸入來測驗“形狀”(匹配),從 Haskell 到 C# 等很多語言都接受了模式匹配,
在下面的代碼中,短語 String s 是型別測驗模式:
if (obj instanceof String s) {
// can use s here
} else {
// can't use s here
}
下面的代碼也是正確的:
if (obj instanceof String s && s.length() > 5) {.. s.contains(..) ..}
377: ZGC: 可擴展的低延遲垃圾收集器
JEP 377: ZGC: A Scalable Low-Latency Garbage Collector (Production)
ZGC 已經在 JEP 333時集成到了 JDK 11 中,當時是作為實驗特性引入的,在 JDK 11 發布以來,我們收到了很多積極的反饋,并修復了許多 bug,添加了很多新功能,更重要的是,ZGC 已經支持所有主流平臺:
- Linux/x86_64 (JEP 333)
- Linux/aarch64 (8214527)
- Windows (JEP 365)
- macOS (JEP 364)
現在可以通過 -XX:+UnlockExperimentalVMOptions -XX:+UseZGC 命令選項啟用 ZGC,
378: 文本塊
JEP 378: Text Blocks
Java語言增加文本塊功能,文本塊是多行字串文字,能避免大多數情況下的轉義問題,
為什么需要此特性
在Java中,HTML, XML, SQL, JSON等字串物件都很難閱讀和維護,
HTML
使用one-dimensional的字串語法:
String html = "<html>\n" +
" <body>\n" +
" <p>Hello, world</p>\n" +
" </body>\n" +
"</html>\n";
使用two-dimensional文本塊語法:
String html = """
<html>
<body>
<p>Hello, world</p>
</body>
</html>
""";
SQL
使用one-dimensional的字串語法:
String query = "SELECT `EMP_ID`, `LAST_NAME` FROM `EMPLOYEE_TB`\n" +
"WHERE `CITY` = 'INDIANAPOLIS'\n" +
"ORDER BY `EMP_ID`, `LAST_NAME`;\n";
使用two-dimensional文本塊語法:
String query = """
SELECT `EMP_ID`, `LAST_NAME` FROM `EMPLOYEE_TB`
WHERE `CITY` = 'INDIANAPOLIS'
ORDER BY `EMP_ID`, `LAST_NAME`;
""";
多語言示例
使用one-dimensional的字串語法:
ScriptEngine engine = new ScriptEngineManager().getEngineByName("js");
Object obj = engine.eval("function hello() {\n" +
" print('\"Hello, world\"');\n" +
"}\n" +
"\n" +
"hello();\n");
使用two-dimensional文本塊語法:
ScriptEngine engine = new ScriptEngineManager().getEngineByName("js");
Object obj = engine.eval("""
function hello() {
print('"Hello, world"');
}
hello();
""");
特性描述
文本塊是Java語言的新語法,可以用來表示任何字串,具有更高的表達能力和更少的復雜度,
文本塊的開頭定界符是由三個雙引號字符(""")組成的序列,后面跟0個或多個空格,最后跟一個行終止符,內容從開頭定界符的行終止符之后的第一個字符開始,
結束定界符是三個雙引號字符的序列,內容在結束定界符的第一個雙引號之前的最后一個字符處結束,
與字串文字中的字符不同,文本塊的內容中可以直接包含雙引號字符,允許在文本塊中使用\“,但不是必需的或不建議使用,
與字串文字中的字符不同,內容可以直接包含行終止符,允許在文本塊中使用\n,但不是必需或不建議使用,例如,文本塊:
"""
line 1
line 2
line 3
"""
等效于字串文字:
"line 1\nline 2\nline 3\n"
或字串文字的串聯:
"line 1\n" +
"line 2\n" +
"line 3\n"
379: Shenandoah: 低暫停時間的垃圾收集器
JEP 379: Shenandoah: A Low-Pause-Time Garbage Collector (Production)
特性描述
Shenandoah GC 由JEP 189 集成到 JDK 12 中,現在,Shenandoah GC 已經可以用于生產環境了!(Shenandoah GC 的具體特性以后會有專門的文章講解,本篇文章略過),
381: 洗掉 Solaris 和 SPARC Ports
JEP 381: Remove the Solaris and SPARC Ports
洗掉了對 Solaris/SPARC、Solaris/x64和 Linux/SPARC 埠支持的源代碼,并重新構建 JDK,這些代碼在 JDK 14中已經被標記為廢棄的,并明確表示在未來版本中會洗掉,
383: 外部存盤器訪問API (Second Incubator)
JEP 383: Foreign-Memory Access API (Second Incubator)
引入新的能使 Java 程式安全高效訪問 Java 堆記憶體之外的外部記憶體的 API,
384: Records (Second Preview)
JEP 384: Records (Second Preview)
通過 Records(不知道如何翻譯,囧……)增強Java編程語言,Records提供了一種緊湊的語法來宣告類,這些類是淺層不可變資料的透明持有者,
為什么需要此特性
我們經常聽到這樣的抱怨:“Java太冗長”、“Java規則過多”,首當其沖的就是充當簡單集合的“資料載體”的類,為了寫一個資料類,開發人員必須撰寫許多低價值、重復且容易出錯的代碼:建構式、訪問器、equals()、hashCode()和toString()等等,
盡管IDE可以幫助開發人員撰寫資料載體類的絕大多數編碼,但是這些代碼仍然冗長,
從表面上看,將Records是為了簡化模板編碼而生的,但是它還有“遠大”的目標:modeling data as data,records應該更簡單、簡潔、資料不可變,
描述
records是Java的一種新的型別,同列舉一樣,records也是對類的一種限制,records放棄了類通常享有的特性:將API和表示解耦,但是作為回報,records使資料類變得非常簡潔,
一個record具有名稱和狀態描述,狀態描述宣告了record的組成部分,例如:
record Point(int x, int y) { }
因為records在語意上是資料的簡單透明持有者,所以記錄會自動獲取很多標準成員:
- 狀態宣告中的每個成員,都有一個 private final的欄位;
- 狀態宣告中的每個組件的公共讀取訪問方法,該方法和組件具有相同的名字;
- 一個公共的建構式,其簽名與狀態宣告相同;
- equals和hashCode的實作;
- toString的實作,
限制
records不能擴展任何類,并且不能宣告私有欄位以外的實體欄位,宣告的任何其他欄位都必須是靜態的,
records類都是隱含的final類,并且不能是抽象類,這些限制使得records的API僅由其狀態描述定義,并且以后不能被其他類實作或繼承,
在record中額外宣告變數
也可以顯式宣告從狀態描述自動派生的任何成員,可以在沒有正式引數串列的情況下宣告建構式(這種情況下,假定與狀態描述相同),并且在正常建構式主體正常完成時呼叫隱式初始化(this.x=x),這樣就可以在顯式建構式中僅執行其引數的驗證等邏輯,并省略欄位的初始化,例如:
record Range(int lo, int hi) {
public Range {
if (lo > hi) /* referring here to the implicit constructor parameters */
throw new IllegalArgumentException(String.format("(%d,%d)", lo, hi));
}
}
語法
RecordDeclaration:
{ClassModifier} record TypeIdentifier [TypeParameters]
(RecordComponents) [SuperInterfaces] [RecordBody]
RecordComponents:
{RecordComponent {, RecordComponent}}
RecordComponent:
{Annotation} UnannType Identifier
RecordBody:
{ {RecordBodyDeclaration} }
RecordBodyDeclaration:
ClassBodyDeclaration
RecordConstructorDeclaration
RecordConstructorDeclaration:
{Annotation} {ConstructorModifier} [TypeParameters] SimpleTypeName
[Throws] ConstructorBody
反射 API
下面的方法會被加到java.lang.Class中:
- RecordComponent[] getRecordComponents()
- boolean isRecord()
385: 廢棄 RMI Activation
JEP 385: Deprecate RMI Activation for Removal
將 RMI Activation 機制標記為廢棄,以便在將來的某個版本洗掉掉,RMI Activation 是 RMI 的過時部分,但是這并不表示會棄用 RMI 的其他部分,
總結
以上就是 JDK 15 的全部新特性,我們可以看到 G1 GC 已經退出歷史舞臺,新的 ZGC、Shenandoah GC 已經登上歷史舞臺,同時也丟棄了像自旋鎖這種歷史包袱,增加了許多諸如文本塊等簡潔的語法特性,我們可以預見 Java 的性能會越來越好,同時也會越來越簡潔,(當然簡潔程度跟 Kotlin 這種新興語言是比不了的,畢竟完全沒有歷史包袱),歡迎大家關注我的公眾號,
公眾號
coding 筆記、點滴記錄,以后的文章也會同步到公眾號(Coding Insight)中,希望大家關注_
代碼和思維導圖在 GitHub 專案中,歡迎大家 star!

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/228377.html
標籤:Java
上一篇:通過 GraalVM 將 Java 程式編譯成本地機器碼!
下一篇:Java基礎之:OOP——繼承
