JDK 15已發佈,你所要知道的都在這裏!

JDK 15已經在2020年9月15日發佈!詳情見 JDK 15 官方計劃。下面是對 JDK 15 全部新特性的詳細解析!html

官方計劃

  • 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)java

與其餘簽名方案相比,EdDSA 具備更高的安全性和性能,而且已有不少其餘加密庫(如 OpenSSL 和 BoringSSL)支持此簽名方案。EdDSA 是 TLS 1.3的可選組件,且是 TLS 1.3 中僅有的三種簽名方案之一。用戶能夠沒必要再使用第三方庫了。git

360: Sealed Classes (Preview)

JEP 360: Sealed Classes (Preview)github

爲何須要此特性

在 Java 語言中,代碼的重用是經過類的繼承實現的:多個子類能夠繼承同一個超類(並重用超類的方法)。可是重用代碼並非類層次結構的惟一目的,有時類層次結構僅僅是對某個領域的建模。以這種方式使用類層次結構時,限制子類集合能夠簡化建模。算法

好比在圖形庫中,Shape 類的開發者可能只但願有限個數的類擴展 Shape 類,開發者並不想爲未知子類編寫防護代碼。之前的 Java 並不會限制 Shape 類的擴展屬性,Shape 類能夠擁有任意數量的子類。在封閉類(Sealed Classes)中,類層次結構是封閉的,可是代碼仍然能夠在有限範圍內重用。shell

特性描述

經過 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 已經退出歷史舞臺,新的 ZGCShenandoah GC 已經登上歷史舞臺。同時也丟棄了像自旋鎖這種歷史包袱,增長了許多諸如文本塊等簡潔的語法特性。咱們能夠預見 Java 的性能會愈來愈好,同時也會愈來愈簡潔。(固然簡潔程度跟 Kotlin 這種新興語言是比不了的,畢竟徹底沒有歷史包袱)。歡迎你們關注個人公衆號。

公衆號

coding 筆記、點滴記錄,之後的文章也會同步到公衆號(Coding Insight)中,但願你們關注^_^

代碼和思惟導圖在 GitHub 項目中,歡迎你們 star!

相關文章
相關標籤/搜索