Java 16 新功能介紹

點贊再看,動力無限。Hello world : ) 微信搜「 程序猿阿朗 」。

本文 Github.com/niumoo/JavaNotes程序猿阿朗博客 已經收錄,有很多知識點和系列文章。

Java 16 在 2021 年 3 月 16 日正式發佈,不是長久支持版本,這次更新沒有帶來很多語法上的改動,但是也帶來了不少新的實用功能。

OpenJDK Java 16 下載://jdk.java.net/archive/

OpenJDK Java 16 文檔://openjdk.java.net/projects/jdk/16/

此文章屬於 Java 新特性教程 系列,會介紹 Java 每個版本的新功能,可以點擊瀏覽。

1. JEP 347: 啟用 C++ 14 語言特性

這項更新和 Java 開發者關係不太密切,JEP 347 允許 在 JDK 的 C++ 源碼中使用 C++ 14 的語言特性,並且給出了哪些特性可以在 HotSpot 代碼中使用的具體說明。

擴展閱讀:啟用 C++ 14 語言特性

2. JEP 357:從 Mercurial 遷移到 Git

在此之前,OpenJDK 源代碼是使用版本管理工具 Mercurial 進行管理的,你也可以在 //hg.openjdk.java.net/ 查看 OpenJDK 的源代碼歷史版本。

但是現在遷移到了 GIt ,主要原因如下:

  1. Mercurial 生成的版本控制元數據過大。
  2. Mercurial 相關的開發工具比較少,而 Git 幾乎在所有的主流 IDE 中已經無縫集成。
  3. Mercurial 相關的服務比較少,無論是自建託管,還是服務託管。

為了優雅的遷移到 Git,OpenJDK 做了如下操作。

  1. 將所有的單存儲庫 OpenJDK 項目從 Mercurial 遷移到 Git。
  2. 保留所有的版本控制歷史,也包括 Tag。
  3. 根據 Git 的最佳實踐重新格式化提交的消息。
  4. 創建了一個工具用來在 Mercurial 和 Git 哈希之間進行轉換。

擴展閱讀: 從 Mercurial 遷移到 Git

3. JEP 369:遷移到 GitHub

和 JEP 357 從 Mercurial 遷移到 Git 的改變一致,在把版本管理遷移到 Git 之後,選擇了在 GitHub 上託管 OpenJDK 社區的 Git 倉庫。不過只對 JDK 11 以及更高版本 JDK 進行了遷移。

4. JEP 376:ZGC 並發線程堆棧處理

這次改動讓 ZGC 線程堆棧處理從安全點(Safepoints)移動到並發階段。

如果你忘記了什麼是 Safepoints,可以複習一下。

我們都知道,在之前,需要 GC 的時候,為了進行垃圾回收,需要所有的線程都暫停下來,這個暫停的時間我們成為 Stop The World

而為了實現 STW 這個操作, JVM 需要為每個線程選擇一個點停止運行,這個點就叫做安全點(Safepoints)

擴展閱讀:JEP 376:ZGC 並發線程堆棧處理

5. JEP 380:Unix 域套接字通道

添加 UnixDomainSocketAddress.java 類用於支持 Unix 域套接字通道。

添加 Unix-domain socket 到 SocketChannel 和 ServerSocketChannel API 中。

添加枚舉信息 java.net.StandardProtocolFamily.UNIX。

6. JEP 386:移植 Alpine Linux

Apine Linux 是一個獨立的、非商業的 Linux 發行版,它十分的小,一個容器需要不超過 8MB 的空間,最小安裝到磁盤只需要大約 130MB 存儲空間,並且十分的簡單,同時兼顧了安全性。

此提案將 JDK 移植到了 Apline Linux,由於 Apline Linux 是基於 musl lib 的輕量級 Linux 發行版,因此其他 x64 和 AArch64 架構上使用 musl lib 的 Linux 發行版也適用。

擴展閱讀:JEP 386: Alpine Linux Port

7. JEP 387:更好的 Metaspace

自從引入了 Metaspace 以來,根據反饋,Metaspace 經常佔用過多的堆外內存,從而導致內存浪費,現在可以更及時地將未使用的 HotSpot class-metaspace 內存返還給操作系統,從而減少 Metaspace 的佔用空間,並優化了 Metaspace 代碼以降低後續的維護成本。

8. JEP 388:移植 Windows/AArch64

將 JDK 移植到 Windows/AArch64 架構上,Windows/AArch64 已經是終端用戶市場的熱門需求。

9. JEP 389:外部連接器 API(孵化)

這項提案讓 Java 代碼可以調用由其他語言(比如 C ,C++)編寫的編譯後的機器代碼,替換了之前的 JNI 形式。

不過這還是一個孵化中的功能,運行時需要添加 --add-modules jdk.incubator.foreign 參數來編譯和運行 Java 代碼。

下面是一個調用 C 語言函數方法,然後輸出運行結果的例子。

  1. 編寫一個 C 函數打印一個 “hello www.wdbyte.com”。
#include <stdio.h>

void printHello(){
	printf("hello www.wdbyte.com\n");
}
  1. 將上面的代碼編譯,然後輸出到共享庫 hello.so
$ gcc -c -fPIC hello.c
$ gcc -shared -o hello.so hello.o
$ ll
total 128
-rw-r--r--  1 darcy  staff    76B 10 28 19:46 hello.c
-rw-r--r--  1 darcy  staff   776B 10 28 19:46 hello.o
-rwxr-xr-x  1 darcy  staff    48K 10 28 19:47 hello.so
  1. 編寫一個 Java 代碼,調用 hello.so 的 printHello 方法。
import jdk.incubator.foreign.CLinker;
import jdk.incubator.foreign.FunctionDescriptor;
import jdk.incubator.foreign.LibraryLookup;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.nio.file.Path;
import java.util.Optional;

public class JEP389 {

  public static void main(String[] args) throws Throwable {

      Path path = Path.of("/Users/darcy/git/java-core/java-16/src/com/wdbyte/hello.so");

      LibraryLookup libraryLookup = LibraryLookup.ofPath(path);

      Optional<LibraryLookup.Symbol> optionalSymbol = libraryLookup.lookup("printHello");
      if (optionalSymbol.isPresent()) {

          LibraryLookup.Symbol symbol = optionalSymbol.get();

          FunctionDescriptor functionDescriptor = FunctionDescriptor.ofVoid();

          MethodType methodType = MethodType.methodType(Void.TYPE);

          MethodHandle methodHandle = CLinker.getInstance().downcallHandle(
                  symbol.address(),
                  methodType,
                  functionDescriptor);
          methodHandle.invokeExact();

      }

  }
}
  1. Java 代碼編譯。
$ javac --add-modules jdk.incubator.foreign JEP389.java
警告: 使用 incubating 模塊: jdk.incubator.foreign
1 個警告
  1. Java 代碼執行。
$ java --add-modules  jdk.incubator.foreign -Dforeign.restricted=permit JEP389.java
WARNING: Using incubator modules: jdk.incubator.foreign
警告: 使用 incubating 模塊: jdk.incubator.foreign
1 個警告
hello www.wdbyte.com

擴展閱讀: JEP 389:外部鏈接器 API(孵化器)

10. JEP 390:基於值的類的警告

添加了一個註解,用於標識當前是是基於值的類,比如 Java 8 引入的預防空指針的 Optional 類,現在已經添加了註解標識。

@jdk.internal.ValueBased
public final class Optional<T> {
    // ...
}

擴展閱讀:基於值的類的警告

11. JEP 392:打包工具

在 Java 14 中,JEP 343 引入了打包工具,命令是 jpackage,在 Java 14 新功能文章里也做了介紹:

使用 jpackage 命令可以把 JAR 包打包成不同操作系統支持的軟件格式。

jpackage --name myapp --input lib --main-jar main.jar --main-class myapp.Main

常見平台格式如下:

  1. Linux: deb and rpm
  2. macOS: pkg and dmg
  3. Windows: msi and exe

要注意的是,jpackage 不支持交叉編譯,也就是說在 windows 平台上是不能打包成 macOS 或者 Linux 系統的軟件格式的。

在 Java 15 中,繼續孵化,現在在 Java 16 中,終於成為了正式功能。

下面是一個例子,把一個簡單的 Java Swing 程序打包成當前操作系統支持的軟件格式,然後安裝到當前電腦。

編寫 Java 代碼

import javax.swing.*;
import java.awt.*;

public class JEP392 {

    public static void main(String[] args) {
        JFrame frame = new JFrame("Hello World Java Swing");
        frame.setMinimumSize(new Dimension(800, 600));
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JLabel lblText = new JLabel("Hello World!", SwingConstants.CENTER);
        frame.getContentPane().add(lblText);
        frame.pack();
        frame.setVisible(true);

    }
}

編譯後,創建一個 JAR 文件。

$ javac JEP392.java
$ java JEP392.java
$ jar cvf JEP392.jar JEP392.class

將生成的 JEP392.jar 打包到符合當前平台的軟件包中。

$ ~/develop/jdk-16.0.1.jdk/Contents/Home/bin/jpackage -i . -n JEP392 --main-jar hello.jar --main-class JEP392
$ ll
-rw-r--r--@ 1 darcy  staff    50M 10 28 20:34 JEP392-1.0.dmg
-rw-r--r--  1 darcy  staff   864B 10 28 20:22 JEP392.class
-rw-r--r--  1 darcy  staff   1.0K 10 28 20:30 JEP392.jar
-rw-r--r--  1 darcy  staff   588B 10 28 20:22 JEP392.java

ll 後顯示的 JEP392-1.0.dmg(我用的 MacOS ,所以格式是 dmg)就是打包後的結果。

雙擊這個文件後可以像 mac 軟件一樣安裝。其他平台類似。

安裝後可以在啟動台啟動。

不同的系統安裝位置不同:

  • Linux: /opt
  • MacOS : /Applications
  • Windows: C:\Program Files\

擴展閱讀:JEP 392:打包工具

12. JEP 393:外部內存訪問(第三次孵化)

此提案旨在引入新的 API 以允許 Java 程序安全有效的訪問 Java 堆之外的內存。相關提案早在 Java 14 的時候就已經提出了,在 Java 15 中重新孵化,現在在 Java 16 中再次孵化。

此提案的目標如下:

  1. 通用:單個 API 應該能夠對各種外部內存(如本機內存、持久內存、堆內存等)進行操作。
  2. 安全:無論操作何種內存,API 都不應該破壞 JVM 的安全性。
  3. 控制:可以自由的選擇如何釋放內存(顯式、隱式等)。
  4. 可用:如果需要訪問外部內存,API 應該是 sun.misc.Unsafa.

擴展閱讀:外部內存訪問

13. JEP 394:instanceof 模式匹配

改進 instanceof 在 Java 14 中已經提出,在 Java 15 中繼續預覽,而現在,在 Java 16 中成為正式功能。

在之前,使用 instanceof 需要如下操作:

if (obj instanceof String) {
    String s = (String) obj;    // grr...
    ...
}

多餘的類型強制轉換,而現在:

if (obj instanceof String s) {
    // Let pattern matching do the work!
    ...
}

擴展閱讀:Java 14 新特性介紹 – instanceof

14. JEP 395:Records

Record 成為 Java 16 的正式功能,下面是介紹 Java 14 時關於 Record 的介紹。

record 是一種全新的類型,它本質上是一個 final 類,同時所有的屬性都是 final 修飾,它會自動編譯出 public get hashcodeequalstoString 等方法,減少了代碼編寫量。

示例:編寫一個 Dog record 類,定義 name 和 age 屬性。

package com.wdbyte;

public record Dog(String name, Integer age) {
}

Record 的使用。

package com.wdbyte;

public class Java14Record {

    public static void main(String[] args) {
        Dog dog1 = new Dog("牧羊犬", 1);
        Dog dog2 = new Dog("田園犬", 2);
        Dog dog3 = new Dog("哈士奇", 3);
        System.out.println(dog1);
        System.out.println(dog2);
        System.out.println(dog3);
    }
}

輸出結果:

Dog[name=牧羊犬, age=1]
Dog[name=田園犬, age=2]
Dog[name=哈士奇, age=3]

這個功能在 Java 15 中進行二次預覽,在 Java 16 中正式發佈。

15. JEP 396:默認強封裝JDK內部

Java 9 JEP 261引入了 --illegal-access 控制內部 API 訪問和 JDK 打包的選項。

此 JEP 將 --illegal-access 選項的默認模式從允許更改為拒絕。通過此更改,JDK的內部包和 API(關鍵內部 API除外)將不再默認打開。

該 JEP 的動機是阻止第三方庫、框架和工具使用 JDK 的內部 API 和包,增加了安全性。

16. JEP 397:Sealed Classes(密封類)預覽

Sealed Classes 再次預覽,在 Java 15 新特性介紹文章里已經介紹過相關功能,並且給出了詳細的使用演示,這裡不再重複介紹。

下面是一段引用:

我們都知道,在 Java 中如果想讓一個類不能被繼承和修改,這時我們應該使用 final 關鍵字對類進行修飾。不過這種要麼可以繼承,要麼不能繼承的機制不夠靈活,有些時候我們可能想讓某個類可以被某些類型繼承,但是又不能隨意繼承,是做不到的。Java 15 嘗試解決這個問題,引入了 sealed 類,被 sealed 修飾的類可以指定子類。這樣這個類就只能被指定的類繼承。

而且 sealed 修飾的類的機制具有傳遞性,它的子類必須使用指定的關鍵字進行修飾,且只能是 final sealed non-sealed 三者之一。

擴展閱讀:Java 15 新特性介紹

參考

  1. //openjdk.java.net/projects/jdk/16/
  2. //docs.oracle.com/en/java/javase/16/

<完>

Hello world : ) 我是阿朗,一線技術工具人,認認真真寫文章。

點贊的個個都是人才,不僅長得帥氣好看,說話還好聽。

文章持續更新,可以關注公眾號「 程序猿阿朗 」或訪問「程序猿阿朗博客](//www.wdbyte.com) 」。

回復【資料】有我準備的各系列知識點和必看書籍。

本文 Github.com/niumoo/JavaNotes 已經收錄,有很多知識點和系列文章,歡迎Star。

等你好久