Java 16 中的新功能
Java 16于 2021 年 3 月 16 日正式发布,请在此处下载 Java 16。
Java 16 有 17 个 JEP 项。
- 1. JEP 338:Vector API(孵化器)
- 2. JEP 347:启用 C++14 语言功能
- 3. JEP 357:从 Mercurial 迁移到 Git
- 4. JEP 369:迁移到 GitHub
- 5. JEP 376:ZGC:并发线程堆栈处理
- 6. JEP 380:Unix 域套接字通道
- 7. JEP 386:Alpine Linux 端口
- 8. JEP 387:弹性元空间
- 9. JEP 388:Windows / AArch64 端口
- 10. JEP 389:外部链接器 API(孵化器)
- 11. JEP 390:基于值的类的警告
- 12. JEP 392:打包工具
- 13. JEP 393:外内存访问 API(第三孵化器)
- 14. JEP 394:instanceof 的模式匹配
- 15. JEP 395:记录
- 16. JEP 396:默认强封装JDK内部
- 17. JEP 397:密封类(第二次预览)
- 下载源代码
- 参考
Java 16 开发人员功能。
jpackage、记录(标准)、模式匹配(标准)、密封类型(第二次预览)、外部内存访问 API(第三个孵化器)、替换 JNI 的外部链接器 API(孵化器)、向量 API(孵化器)
1. JEP 338:Vector API(孵化器)
Java支持自动向量化来优化算法算法,这意味着Java(JIT编译器)会自动将一些标量操作(一次一项)转换为向量操作(一次多项);但是,开发者无法控制这种向量运算的转换,它完全依赖于 JIT 编译器来优化代码,而且并非所有的标量运算都是可转换的。
此 JEP 引入了新的 Vector API,以允许开发人员显式执行向量操作。
- 甲矢量处理器一次处理多个数据,被称为单指令多数据(SIMD) 。
- 甲标量处理器只处理一个数据的时间,被称为单指令,单数据(SISD) 。
2. JEP 347:启用 C++14 语言功能
此 JEP 允许在 JDK 内的 C++ 源代码中使用 [C++ 14 特性]((https://en.wikipedia.org/wiki/C%2B%2B14)。
3. JEP 357:从 Mercurial 迁移到 Git
此 JEP 将 OpenJDK 源代码从 Mercurial 迁移到 Git 或 GitHub,涉及以下JEP 369
迁移到 Git 的原因:
- 版本控制系统元数据 (Mercurial) 的文件太大。
- 可用工具
- 可用主机
4. JEP 369:迁移到 GitHub
该 JEP 加入了上述JEP 357,将 OpenJDK 源代码从 Mercurial 迁移到GitHub。
进一步阅读
5. JEP 376:ZGC:并发线程堆栈处理
此 JEP 通过将 ZGC 线程堆栈处理从安全点移动到并发阶段来改进 Z 垃圾收集器 (ZGC)。
历史
进一步阅读
6. JEP 380:Unix 域套接字通道
在Unix域套接字被用于相同的主机,该主机装置交换在同一主机上执行的进程之间的数据上的进程间通信(IPC)。Unix 域套接字类似于 TCP/IP 套接字,不同之处在于它们由文件系统路径名而不是 Internet 协议 (IP) 地址和端口号寻址。大多数 Unix 平台,Windows 10 和 Windows Server 2019,也支持 Unix 域套接字。
此 JEP 将Unix 域 (AF_UNIX) 套接字支持添加到现有SocketChannel和ServerSocketChannel。
新的 Unix 域 Socket 类或 API:
- 新的套接字地址类,
java.net.UnixDomainSocketAddress
- 新的
enum
,java.net.StandardProtocolFamily.UNIX
进一步阅读
7. JEP 386:Alpine Linux 端口
这个 JEP 将 JDK 移植到Alpine Linux和其他使用musl实现的Linux 发行版。这个 JDK 端口使 Java 能够在 Alpine Linux 中开箱即用,这有利于那些依赖 Java 的框架或工具,如 Tomcat 和 Spring。
PS Alpine Linux 包含小镜像,广泛应用于云部署、微服务和容器环境。
8. JEP 387:弹性元空间
Java 8 JEP 122删除了 PermGen(永久代),并在热点中引入了Metaspace,这是一个本地的堆外内存管理器。
该 JEP 通过更迅速地将未使用的 HotSpot 类元数据或元空间内存返回给操作系统来改进元空间内存管理,减少元空间占用空间并简化元空间代码。
进一步阅读
9. JEP 388:Windows / AArch64 端口
此 JEP 将 JDK 移植到 Windows/AArch64,在 ARM 硬件、服务器或基于 ARM 的笔记本电脑上运行 JDK + Windows。
PS Windows/AArch64 是终端用户市场的热门需求。
10. JEP 389:外部链接器 API(孵化器)
此 JEP 使 Java 代码能够调用或可由其他语言(如 C 或 C++)编写的本机代码调用,替换Java 本机接口 (JNI)
PS 这是一个孵化功能;需要添加--add-modules jdk.incubator.foreign
来编译和运行 Java 代码。
10.1 下面的例子展示了如何使用外部链接器 API 调用标准 C 库strlen
来返回字符串的长度。
size_t strlen(const char *s);
import jdk.incubator.foreign.*;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import static jdk.incubator.foreign.CLinker.C_LONG;
import static jdk.incubator.foreign.CLinker.C_POINTER;
// Java call standard C library
// size_t strlen(const char *s);
public class CStrLen {
public static void main(String[] args) throws Throwable {
if (args.length == 0) {
throw new IllegalArgumentException("Please provide an argument.");
}
String input = args[0];
MethodHandle strlen = CLinker.getInstance().downcallHandle(
LibraryLookup.ofDefault().lookup("strlen").get(),
MethodType.methodType(long.class, MemoryAddress.class),
FunctionDescriptor.of(C_LONG, C_POINTER)
);
try (MemorySegment str = CLinker.toCString(input)) {
long len = (long) strlen.invokeExact(str.address()); // 5
System.out.println(len);
}
}
}
编译时启用孵化器模块。
$ javac --add-modules jdk.incubator.foreign CStrLen.java
warning: using incubating module(s): jdk.incubator.foreign
1 warning
在启用孵化器模块的情况下运行。
$ java --add-modules jdk.incubator.foreign -Dforeign.restricted=permit CStrLen mkyong
WARNING: Using incubator modules: jdk.incubator.foreign
6
10.2 下面是调用在 C 代码中定义的函数的另一个示例。
一个简单的 C 函数来打印一个 hello world。
#include <stdio.h>
void printHello() {
printf("hello world!\n");
}
编译上述 C 代码并输出到共享库hello.so
。
$ gcc -c -fPIC hello.c
$ gcc -shared -o hello.so hello.o
在 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("/home/mkyong/projects/core-java/java-16/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();
}
}
}
编译。
$ javac --add-modules jdk.incubator.foreign JEP389.java
warning: using incubating module(s): jdk.incubator.foreign
1 warning
跑。
$ java --add-modules jdk.incubator.foreign -Dforeign.restricted=permit JEP389
WARNING: Using incubator modules: jdk.incubator.foreign
hello world!
历史
- Java 14 JEP 370引入了外部内存访问 API(孵化器)。
- Java 15 JEP 383引入了外部内存访问 API(第二孵化器)。
- Java 16 JEP 389引入了外部链接器 API(孵化器)。
- Java 16 JEP 393引入了外部内存访问 API(第三孵化器)。
- Java 17 JEP 412引入了外部函数和内存 API(孵化器)。
11. JEP 390:基于值的类的警告
如果我们同步基于值的类的实例,这个 JEP 会提供一个新的警告;还弃用原始包装类(基于值的)构造函数以进行删除。
11.1 如何识别基于值的类?
注释@jdk.internal.ValueBased
告诉我们一个类是否是一个基于值的类。查看以下两个类,我们可以知道原始包装类Double
和Optional
基于值的类。
package java.util;
@jdk.internal.ValueBased
public final class Optional<T> {
//...
}
package java.lang;
@jdk.internal.ValueBased
public final class Double extends Number
implements Comparable<Double>, Constable, ConstantDesc {
//...
}
注意
请参阅JEP 390:基于值的类的警告以获取基于值的类的完整列表。
11.2 下面的例子尝试synchronized
一个基于值的类。
public class JEP390 {
public static void main(String[] args) {
Double d = 20.0;
synchronized (d) {} // javac warning & HotSpot warning
}
}
编译上面的代码并点击新的警告。
$ javac JEP390.java
JEP390.java:7: warning: [synchronization] attempt to synchronize on an instance of a value-based class
synchronized (d) {} // javac warning & HotSpot warning
^
1 warning
11.3 Java 9 弃用了原始包装类(基于值)构造函数,现在标记为删除。
package java.lang;
//...
@jdk.internal.ValueBased
public final class Double extends Number
implements Comparable<Double>, Constable, ConstantDesc {
@Deprecated(since="9", forRemoval = true)
public Double(double value) {
this.value = value;
}
@Deprecated(since="9", forRemoval = true)
public Double(String s) throws NumberFormatException {
value = parseDouble(s);
}
//...
}
12. JEP 392:打包工具
JEP 将该jpackage
工具从jdk.incubator.jpackage
移至jdk.jpackage
,并成为 Java 16 中的标准或产品功能。这jpackage
是一个打包工具,用于将 Java 应用程序打包到特定于平台的包中,例如:
- Linux:deb 和 rpm
- macOS:pkg 和 dmg
- Windows:msi 和 exe
历史
- Java 14 JEP 343引入了一个
jpackage
孵化工具,它在 Java 15 中仍然是一个孵化工具。
12.1 下面的例子展示了jpackage
如何deb
在Linux系统(Ubuntu)中将一个简单的Java Swing程序打包成格式。
一个简单的 Java Swing 程序,用于显示 hello world。
import javax.swing.*;
import java.awt.*;
public class JEP392 {
public static void main(String[] args) {
JFrame frame = new JFrame("Hello World Java Swing");
// display frame site
frame.setMinimumSize(new Dimension(800, 600));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// center the JLabel
JLabel lblText = new JLabel("Hello World!", SwingConstants.CENTER);
// add JLabel to JFrame
frame.getContentPane().add(lblText);
// display it
frame.pack();
frame.setVisible(true);
}
}
创建一个jar
文件并将jpackage
其放入特定于平台的包中;因为这是在 Ubuntu 系统中测试的,所以它会创建一个.deb
文件。
# compile
$ javac JEP392.java
# create a jar file
$ jar cvf hello.jar JEP392.class
# package the jar file into platform-specific package
$ /opt/jdk-16/bin/jpackage -i . -n JEP392 --main-jar hello.jar --main-class JEP392
# The jpackage created this jep392_1.0-1_amd64.deb
$ ls -lsah
4.0K -rw-rw-r-- 1 mkyong mkyong 994 Mac 15 13:52 hello.jar
30M -rw-r--r-- 1 mkyong mkyong 30M Mac 15 14:01 jep392_1.0-1_amd64.deb
sudo dpkg -i jep392_1.0-1_amd64.deb
Selecting previously unselected package jep392.
(Reading database ... 225576 files and directories currently installed.)
Preparing to unpack jep392_1.0-1_amd64.deb ...
Unpacking jep392 (1.0-1) ...
Setting up jep392 (1.0-1) ...
$ ls -lsah /opt/jep392/bin/JEP392
1.6M -rwxr-xr-x 1 root root 1.6M Mac 15 14:01 /opt/jep392/bin/JEP392
默认安装目录在
- Linux是
/opt
- macOS 是
/Applications
- 窗户是
C:\Program Files\
PS 这可以通过jpackage --install-dir
选项覆盖。
运行已安装的 Java Swing 程序。
$ /opt/jep392/bin/JEP392
输出
将jpackage -h
永远是你最好的朋友:
$ /opt/jdk-16/bin/jpackage -h
Usage: jpackage <options>
Sample usages:
--------------
Generate an application package suitable for the host system:
For a modular application:
jpackage -n name -p modulePath -m moduleName/className
For a non-modular application:
jpackage -i inputDir -n name \
--main-class className --main-jar myJar.jar
From a pre-built application image:
jpackage -n name --app-image appImageDir
Generate an application image:
For a modular application:
jpackage --type app-image -n name -p modulePath \
-m moduleName/className
For a non-modular application:
jpackage --type app-image -i inputDir -n name \
--main-class className --main-jar myJar.jar
To provide your own options to jlink, run jlink separately:
jlink --output appRuntimeImage -p modulePath -m moduleName \
--no-header-files [<additional jlink options>...]
jpackage --type app-image -n name \
-m moduleName/className --runtime-image appRuntimeImage
Generate a Java runtime package:
jpackage -n name --runtime-image <runtime-image>
//...
进一步阅读
13. JEP 393:外内存访问 API(第三孵化器)
外部内存访问 API 允许 Java API 访问 Java 堆之外的外部内存,例如memcached、Lucene等。
此 JEP 更新了外部内存访问 API 并保留为孵化器模块。
历史
- Java 14 JEP 370引入了外部内存访问 API(孵化器)。
- Java 15 JEP 383引入了外部内存访问 API(第二孵化器)。
- Java 16 JEP 389引入了外部链接器 API(孵化器)。
- Java 16 JEP 393引入了外部内存访问 API(第三孵化器)。
- Java 17 JEP 412引入了外部函数和内存 API(孵化器)。
14. JEP 394:instanceof 的模式匹配
模式匹配instanceof
是 Java 16 中的标准或产品特性。
在模式匹配之前,我们检查对象的类型并手动转换为变量。
if (obj instanceof String) {
String s = (String) obj; // cast
}
现在我们可以检查对象的类型并自动转换它
if (obj instanceof String s) {
//... s is a string
}
例如,下面是一个常见的检查和转换示例。
if (obj instanceof String) {
String s = (String) obj;
if (s.length() > 5) {
if (s.equalsIgnoreCase("java16")) {
//...
}
}
}
而且,我们可以使用新的instanceof
.
if (obj instanceof String s && s.length() > 5) {
if (s.equalsIgnoreCase("java16")) {
//...
}
}
历史
15. JEP 395:记录
的record
定稿,并成为一个标准功能。
package com.mkyong.java16.jep395;
public class JEP395 {
record Point(int x, int y) { }
public static void main(String[] args) {
Point p1 = new Point(10, 20);
System.out.println(p1.x()); // 10
System.out.println(p1.y()); // 20
Point p2 = new Point(11, 22);
System.out.println(p2.x()); // 11
System.out.println(p2.y()); // 22
Point p3 = new Point(10, 20);
System.out.println(p3.x()); // 10
System.out.println(p3.y()); // 20
System.out.println(p1.hashCode()); // 330
System.out.println(p2.hashCode()); // 363
System.out.println(p3.hashCode()); // 330
System.out.println(p1.equals(p2)); // false
System.out.println(p1.equals(p3)); // true
System.out.println(p2.equals(p3)); // false
}
}
历史
进一步阅读
16. JEP 396:默认强封装JDK内部
Java 9 JEP 261引入了--illegal-access
控制内部 API 访问和 JDK 打包的选项。
此 JEP 将--illegal-access
选项的默认模式从允许更改为拒绝。通过此更改,JDK的内部包和 API(关键内部 API除外)将不再默认打开。
该 JEP 的动机是阻止第三方库、框架和工具使用 JDK 的内部 API 和包。
17. JEP 397:密封类(第二次预览)
Java 15 JEP 360引入了密封类和接口来限制哪些类可以扩展或实现它们。这个 JEP 是第二次预览,有一些改进。
进一步阅读
PS 这个密封类是Java 17 中的标准特性。
下载源代码
$ git clone https://github.com/mkyong/core-java
$ cd java-16