Java14版本特性【一文了解】

我沒有三顆心臟發表於2020-08-23

  • 「MoreThanJava」 宣揚的是 「學習,不止 CODE」,本系列 Java 基礎教程是自己在結合各方面的知識之後,對 Java 基礎的一個總回顧,旨在 「幫助新朋友快速高質量的學習」
  • 當然 不論新老朋友 我相信您都可以 從中獲益。如果覺得 「不錯」 的朋友,歡迎 「關注 + 留言 + 分享」,文末有完整的獲取連結,您的支援是我前進的最大的動力!

特性總覽

以下是 Java 14 中的引入的部分新特性。關於 Java 14 新特性更詳細的介紹可參考這裡

語言及特性更改:

  • Switch 表示式-標準(JEP 361)
  • instanceof 的模式匹配-預覽(JEP 305)
  • 有用的 NullPointerExceptions(JEP 358)
  • record-預覽(JEP 359)
  • 文字塊-預覽(JEP 368)

JVM 更改:

  • 針對 G1 NUMA 感知記憶體分配的優化(JEP 345)
  • 刪除併發標記掃描(CMS)垃圾收集器(JEP 363)
  • JFR 事件流(JEP 349)
  • macOS 上的 ZGC-實驗性(JEP 364)
  • Windows 上的 ZGC-實驗性(JEP 365)
  • 棄用 ParallelScavenge + SerialOld 的 GC 組合(JEP 366)

其他特性:

  • 打包工具(JEP 343)
  • 非易失性對映位元組緩衝區(JEP 352)
  • 棄用 Solaris 和 SPARC 埠(JEP 362)
  • 刪除 Pack200 工具和 API(JEP 367)
  • 外部儲存器訪問 API(JEP 370)

一. Switch 表示式-標準(JEP 361)

在上兩個版本中保留的預留功能,如今終於在 Java 14 中獲得了永久性的地位。

  • Java 12 為表達是引入了 Lambda 語法,從而允許使用多個大小寫標籤進行模式匹配,並防止出現導致冗長程式碼的錯誤。它還強制執行窮盡情況,如果沒有涵蓋所有輸入情況,則會丟擲編譯錯誤。

  • Java 13 在第二個預覽版本使用了 yield 替代了原有的 break 關鍵字來返回表示式的返回值。

Java 14 現在終於使這些功能成為了標準:

String result = switch (day) {
    case "M", "W", "F" -> "MWF";
    case "T", "TH", "S" -> "TTS";
    default -> {
        if (day.isEmpty()) {
            yield "Please insert a valid day.";
        } else {
            yield "Looks like a Sunday.";
        }
    }
};
System.out.println(result);

注意,yield 不是 Java 中的新關鍵字,它僅用於 Switch 表示式中。

二. instanceof 的模式匹配-預覽(JEP 305)

在 Java 14 之前,我們用於 instanceof-and-cast 檢查物件的型別並將其轉換為變數。

if (obj instanceof String) {        // instanceof
  String s = (String) obj;          // cast
  if("jdk14".equalsIgnoreCase(s)){
      //...
  }
}else {
	   System.out.println("not a string");
}

現在,在 Java 14 中,我們可以像這樣重構上面的程式碼:

if (obj instanceof String s) {      // instanceof, cast and bind variable in one line.
    if("jdk4".equalsIgnoreCase(s)){
        //...
    }
}else {
	   System.out.println("not a string");
}

如果 obj 是的例項 String,則將其 String 強制轉換為繫結變數並分配給該繫結變數 s

三. 有用的 NullPointerExceptions(JEP 358)

空指標異常是任何開發人員的噩夢。以前,直到 Java 13 為止,除錯臭名昭著的 NPE 都很棘手。開發人員不得不依靠其他除錯工具,或者手動計算為空的變數/ 方法,因為堆疊跟蹤只會顯示行號。

在 Java 14 之前:

String name = jd.getBlog().getAuthor()
 
//Stacktrace
Exception in thread "main" java.lang.NullPointerException
    at NullPointerExample.main(NullPointerExample.java:5)

Java 14 引入了新的 JVM 功能(帶-XX:+ShowCodeDetailsInExceptionMessages選項),它通過更具描述性的堆疊提供了更好的見解,如下所示:

Exception in thread "main" java.lang.NullPointerException: Cannot invoke "Blog.getAuthor()" because the return value of "Journaldev.getBlog()" is null
    at NullPointerExample.main(NullPointerExample.java:4)

注意:以上功能不是語言功能。這是對執行時環境的增強。

四. record-預覽(JEP 359)

record 是儲存純資料的資料型別。引入 record 背後的想法是快速建立沒有樣板程式碼的簡單簡潔類。(這有點類似 Kotlin 中的資料型別,這也是為什麼有言論說 Java 逐漸 "Kotlin 化" 的原因之一)

通常,Java 中的類需要您實現 equals()hashCode()getterssetters 方法。雖然某些 IDE 支援此類的自動生成,但是程式碼仍然很冗長。使用 record 您只需按照以下方式定義一個類。

record Author(){}
//or
record Author (String name, String topic) {}

Java 編譯器將自動生成一個帶有建構函式、私有 final 欄位、訪問器和 equalshashCode toString 方法的類。上一類的自動生成的 getter 方法是 name()topic()

編譯之後,我們可以檢視上面 record Author (String name, String topic){} 語句,編譯器為我們自動生成的類:

final class Author extends java.lang.Record {
    private final java.lang.String name;
    private final java.lang.String topic;

    public Author(java.lang.String name, java.lang.String topic) { /* compiled code */ }

    public java.lang.String toString() { /* compiled code */ }

    public final int hashCode() { /* compiled code */ }

    public final boolean equals(java.lang.Object o) { /* compiled code */ }

    public java.lang.String name() { /* compiled code */ }

    public java.lang.String topic() { /* compiled code */ }
}

此外,我們可以通過以下方式向記錄新增其他欄位,方法和建構函式:

record Author (int id, String name, String topic) {
    static int followers;
 
    public static String followerCount() {
        return "Followers are "+ followers;
    }
 
    public String description(){
        return "Author "+ name + " writes on "+ topic;
    }
 
    public Author{
    if (id < 0) {
        throw new IllegalArgumentException( "id must be greater than 0.");
     }
   }
}

record 內定義的其他建構函式稱為 Compact 建構函式。它不包含任何引數,只是規範本身建構函式的引數。

關於 record 需要注意的幾件事:

  • record 既不能擴充套件一個類,也不能被另一個類擴充套件。這是一個 final class
  • record 不能是抽象的。
  • record 不能擴充套件任何其他類,也不能在主體內定義例項欄位。例項欄位只能在狀態描述中定義。
  • 宣告的欄位是私有欄位和 final 欄位。
  • record 定義中允許使用靜態欄位和方法。

record 類的引用欄位內的值可以被改變

值得注意的是,對於定義為物件的欄位,只有引用是不可變的。底層值可以修改。

下面顯示了修改 ArrayList 的一條記錄。可以看到,每當 ArrayList 被更改時,該值都會被修改。

jshell> record Author(String name, List<String> topics){}
|  已建立 記錄 Author

jshell> var topicList = new ArrayList<String>(Arrays.asList("Java"));
topicList ==> [Java]

jshell> var author = new Author("Wmyskxz", topicList);
author ==> Author[name=Wmyskxz, topics=[Java]]

jshell> topicList.add("Python");
$6 ==> true

jshell> author.topics();
$7 ==> [Java, Python]

jshell>

record 能實現介面

下面的程式碼顯示了一個實現有記錄介面的示例:

interface Information {
  String getFullName();
}

record Author(String name, String topic) implements Information {
  public String getFullName() {
    return "Author "+ name + " writes on " + topic;
  }
}

record 支援多個建構函式

記錄允許宣告多個有或沒有引數的建構函式,如下所示:

record Author(String name, String topic) {
  public Author() {
 
    this("NA", "NA");
  }
 
  public Author(String name) {
 
    this(name, "NA");
  }
}

record 允許修改訪問器方法

雖然 record 為狀態描述中定義的欄位生成公共訪問方法,但它們也允許您在主體中重新定義訪問方法,如下所示:

record Author(String name, String topic) {
  public String name() {
        return "This article was written by " + this.name;
    }
}

在執行時檢查 record 及其元件

record 為我們提供了 isRecord()getRecordComponents() 來檢查類是否是一條記錄,並檢視它的欄位和型別。下面展示了它是如何做到的:

jshell> record Author(String name, String topic){}
|  已建立 記錄 Author

jshell> var author = new Author("Wmyskxz", "MoreThanJava");
author ==> Author[name=Wmyskxz, topic=MoreThanJava]

jshell> author.getClass().isRecord();
$3 ==> true

jshell> author.getClass().getRecordComponents();
$4 ==> RecordComponent[2] { java.lang.String name, java.lang.String topic }

jshell>

Tips:雖然我們在上面的程式碼示例中向記錄新增了額外的欄位和方法,但請確保不要做得過火。記錄被設計為普通的資料載體,如果您想實現許多其他方法,最好回到常規類。

五. 文字塊-預覽(JEP 368)

文字塊是 Java 13 中的預覽功能,其目的是允許輕鬆建立多行字串文字。在輕鬆建立 HTML 和 JSON 或 SQL 查詢字串時很有用。

在 Java 14 中,文字塊仍在預覽中,並增加了一些新功能。我們現在可以使用:

  • \ 反斜槓用於顯示美觀的多行字串塊。
  • \s 用於考慮尾隨空格,預設情況下編譯器會忽略它們。它保留了前面的所有空間。
String text = """
                Did you know \
                Java 14 \
                has the most features among\
                all non-LTS versions so far\
                """;

String text2 = """
                line1
                line2 \s
                line3
                """;
 
String text3 = "line1\nline2 \nline3\n"

//text2 and text3 are equal.

六. 針對 G1 NUMA 感知記憶體分配的優化(JEP 345)

新的 NUMA感知記憶體 分配模式提高了大型計算機上的 G1 效能。新增 +XX:+UseNUMA 選項以啟用它。

七. 刪除併發標記掃描(CMS)垃圾收集器(JEP 363)

Java 9 – JEP 291 已棄用此併發標記掃描(CMS)垃圾收集器,現在正式將其刪除。

/usr/lib/jvm/jdk-14/bin/java -XX:+UseConcMarkSweepGC Test

OpenJDK 64-Bit Server VM warning: Ignoring option UseConcMarkSweepGC; support was removed in 14.0

八. JFR 事件流(JEP 349)

JDK Flight Recorder(JFR)是用於收集有關正在執行的 Java 應用程式的診斷和效能分析資料的工具。通常,我們開始記錄,停止記錄,然後將記錄的事件轉儲到磁碟以進行分析,它可以很好地進行概要分析,分析或除錯。

該 JEP 改進了現有的 JFR 以支援事件流,這意味著現在我們可以實時傳輸 JFR 事件,而無需將記錄的事件轉儲到磁碟並手動解析它。

九. macOS 上的 ZGC-實驗性(JEP 364)

Java 11 – JEP 333 在 Linux 上引入了 Z 垃圾收集器(ZGC),現在可移植到 macOS。

十. Windows 上的 ZGC-實驗性(JEP 365)

Java 11 – JEP 333 在 Linux 上引入了 Z 垃圾收集器(ZGC),現在可移植到 Windows版本 >= 1803 的機器上。

十一. 棄用 ParallelScavenge + SerialOld 的 GC 組合(JEP 366)

由於較少的使用和大量的維護工作,Java 14 不贊成使用並行年輕代和序列老一代 GC 演算法的組合。

/usr/lib/jvm/jdk-14/bin/java -XX:-UseParallelOldGC Test

OpenJDK 64-Bit Server VM warning: Option UseParallelOldGC was deprecated in version 14.0 and will likely be removed in a future release.

十二. 其他特性

打包工具(JEP 343)

jpackage 是將 Java 應用程式打包到特定於平臺的程式包中的新工具。

  • Linux:deb 和 rpm
  • macOS:pkg 和 dmg
  • Windows:MSI 和 EXE

例如,將 JAR 檔案打包到支援 exe 的 Windows 平臺上。

非易失性對映位元組緩衝區(JEP 352)

改進的 FileChannel API 可建立 MappedByteBuffer非易失性儲存器(NVM)的 訪問,該儲存器即使在關閉電源後也可以檢索儲存的資料。例如,此功能可確保將可能仍在快取記憶體中的所有更改寫回到記憶體中。

ps:僅 Linux / x64 和 Linux / AArch64 OS 支援此功能!

棄用 Solaris 和 SPARC 埠(JEP 362)

不再支援 Solaris / SPARC,Solaris / x64 和 Linux / SPARC 埠,更少的平臺支援意味著更快地交付新功能。

刪除 Pack200 工具和 API(JEP 367)

Java 11 – JEP 336 不贊成使用 pack200unpack200 工具,以及軟體包中的 Pack200 API java.util.jar,現在正式將其刪除。

外部儲存器訪問 API(JEP 370)

孵化器模組,允許 Java API 訪問 Java 堆外部的外部記憶體。

外部儲存器訪問 API 引入了三個主要抽象:

  • MemorySegment:提供對具有給定範圍的連續記憶體區域的訪問。
  • MemoryAddress:提供到 MemorySegment 的偏移量(基本上是一個指標)。
  • MemoryLayout:提供一種描述記憶體段佈局的方法,該方法大大簡化了使用 var 控制程式碼訪問 MemorySegment 的過程。使用此方法,不必根據記憶體的使用方式來計算偏移量。例如,一個整數或長整數陣列的偏移量將有所不同,但將使用 MemoryLayout 透明地對其進行處理。

下面是一個例子:

import jdk.incubator.foreign.*;
import java.lang.invoke.VarHandle;
import java.nio.ByteOrder;

public class Test {

    public static void main(String[] args) {

	     VarHandle intHandle = MemoryHandles.varHandle(int.class, ByteOrder.nativeOrder());

        try (MemorySegment segment = MemorySegment.allocateNative(1024)) {

            MemoryAddress base = segment.baseAddress();

            System.out.println(base);                 // print memory address

	          intHandle.set(base, 999);                 // set value 999 into the foreign memory

            System.out.println(intHandle.get(base));  // get the value from foreign memory
        }
    }
}

編譯並使用孵化器模組執行 jdk.incubator.foreign

$ /usr/lib/jvm/jdk-14/bin/javac --add-modules jdk.incubator.foreign Test.java

warning: using incubating module(s): jdk.incubator.foreign
1 warning

$ /usr/lib/jvm/jdk-14/bin/java --add-modules jdk.incubator.foreign Test

WARNING: Using incubator modules: jdk.incubator.foreign
MemoryAddress{ region: MemorySegment{ id=0x4aac6dca limit: 1024 } offset=0x0 }
999

進一步閱讀:官方文件 - https://download.java.net/java/GA/jdk14/docs/api/jdk.incubator.foreign/jdk/incubator/foreign/package-summary.html

參考資料

  1. OpenJDK 官方說明 - http://openjdk.java.net/projects/jdk/14/
  2. What is new in Java 14 - https://mkyong.com/java/what-is-new-in-java-14/
  3. Java 14 Features | JournalDev - https://www.journaldev.com/37273/java-14-features

文章推薦

  1. 這都JDK15了,JDK7還不瞭解? - https://www.wmyskxz.com/2020/08/18/java7-ban-ben-te-xing-xiang-jie/
  2. 全網最通透的 Java 8 版本特性講解 - https://www.wmyskxz.com/2020/08/19/java8-ban-ben-te-xing-xiang-jie/
  3. Java9的這些史詩級更新你都不知道? - https://www.wmyskxz.com/2020/08/20/java9-ban-ben-te-xing-xiang-jie/
  4. 你想了解的 JDK 10 版本更新都在這裡 - https://www.wmyskxz.com/2020/08/21/java10-ban-ben-te-xing-xiang-jie/
  5. 這裡有你不得不瞭解的 Java 11 特性 - https://www.wmyskxz.com/2020/08/22/java11-ban-ben-te-xing-xiang-jie/
  6. Java 12 版本特性【一文了解】 - https://www.wmyskxz.com/2020/08/22/java12-ban-ben-te-xing-xiang-jie/
  7. Java 13 版本特性【一文了解】 - https://www.wmyskxz.com/2020/08/22/java13-ban-ben-te-xing-xiang-jie/
  8. 「MoreThanJava」系列文集 - https://www.wmyskxz.com/categories/MoreThanJava/
  • 本文已收錄至我的 Github 程式設計師成長系列 【More Than Java】,學習,不止 Code,歡迎 star:https://github.com/wmyskxz/MoreThanJava
  • 個人公眾號 :wmyskxz,個人獨立域名部落格:wmyskxz.com,堅持原創輸出,下方掃碼關注,2020,與您共同成長!

非常感謝各位人才能 看到這裡,如果覺得本篇文章寫得不錯,覺得 「我沒有三顆心臟」有點東西 的話,求點贊,求關注,求分享,求留言!

創作不易,各位的支援和認可,就是我創作的最大動力,我們下篇文章見!

相關文章