點贊再看,動力無限。Hello world : ) 微信搜「 程式猿阿朗 」。
本文 Github.com/niumoo/JavaNotes 和 程式猿阿朗部落格 已經收錄,有很多知識點和系列文章。
Java 16 在 2021 年 3 月 16 日正式釋出,不是長久支援版本,這次更新沒有帶來很多語法上的改動,但是也帶來了不少新的實用功能。
OpenJDK Java 16 下載:https://jdk.java.net/archive/
OpenJDK Java 16 文件:https://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 進行管理的,你也可以在 http://hg.openjdk.java.net/ 檢視 OpenJDK 的原始碼歷史版本。
但是現在遷移到了 GIt ,主要原因如下:
- Mercurial 生成的版本控制後設資料過大。
- Mercurial 相關的開發工具比較少,而 Git 幾乎在所有的主流 IDE 中已經無縫整合。
- Mercurial 相關的服務比較少,無論是自建託管,還是服務託管。
為了優雅的遷移到 Git,OpenJDK 做了如下操作。
- 將所有的單儲存庫 OpenJDK 專案從 Mercurial 遷移到 Git。
- 保留所有的版本控制歷史,也包括 Tag。
- 根據 Git 的最佳實踐重新格式化提交的訊息。
- 建立了一個工具用來在 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 語言函式方法,然後輸出執行結果的例子。
- 編寫一個 C 函式列印一個 "hello www.wdbyte.com"。
#include <stdio.h>
void printHello(){
printf("hello www.wdbyte.com\n");
}
- 將上面的程式碼編譯,然後輸出到共享庫 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
- 編寫一個 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();
}
}
}
- Java 程式碼編譯。
$ javac --add-modules jdk.incubator.foreign JEP389.java
警告: 使用 incubating 模組: jdk.incubator.foreign
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
常見平臺格式如下:
- Linux:
deb
andrpm
- macOS:
pkg
anddmg
- Windows:
msi
andexe
要注意的是,
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 中再次孵化。
此提案的目標如下:
- 通用:單個 API 應該能夠對各種外部記憶體(如本機記憶體、持久記憶體、堆記憶體等)進行操作。
- 安全:無論操作何種記憶體,API 都不應該破壞 JVM 的安全性。
- 控制:可以自由的選擇如何釋放記憶體(顯式、隱式等)。
- 可用:如果需要訪問外部記憶體,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
hashcode
、equals
、toString
等方法,減少了程式碼編寫量。
示例:編寫一個 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 新特性介紹
參考
<完>
Hello world : ) 我是阿朗,一線技術工具人,認認真真寫文章。
點贊的個個都是人才,不僅長得帥氣好看,說話還好聽。
文章持續更新,可以關注公眾號「 程式猿阿朗 」或訪問「程式猿阿朗部落格](https://www.wdbyte.com) 」。
回覆【資料】有我準備的各系列知識點和必看書籍。
本文 Github.com/niumoo/JavaNotes 已經收錄,有很多知識點和系列文章,歡迎Star。