jdk 16 主要的新特性参考:
openjdk JDK 16
baeldung New Features in Java 16
JDK 16 的新特性
Java 16 于2021 年 3 月 16发布,包含新特性:
- 338: Vector API (Incubator)
- 347: Enable C++14 Language Features
- 357: Migrate from Mercurial to Git
- 369: Migrate to GitHub
- 376: ZGC: Concurrent Thread-Stack Processing
- 380: Unix-Domain Socket Channels
- 386: Alpine Linux Port
- 387: Elastic Metaspace
- 388: Windows/AArch64 Port
- 389: Foreign Linker API (Incubator)
- 390: Warnings for Value-Based Classes
- 392: Packaging Tool
- 393: Foreign-Memory Access API (Third Incubator)
- 394: Pattern Matching for instanceof
- 395: Records
- 396: Strongly Encapsulate JDK Internals by Default
- 397: Sealed Classes (Second Preview)
开发者特性
增加了 Stream.toList 方法 (JDK-8180352)
旨在精简写法,Collectors.toList 和 Collectors.toSet非常高频的使用,现在提供了更简洁的写法:
1 | List<String> integersAsString = Arrays.asList("1", "2", "3"); |
Vector API Incubator (JEP-338)
矢量API 是在java 16开始孵化的,旨在提供一套更高效的矢量计算方法。
比如计算两个数组相乘:
1 | int[] a = {1, 2, 3, 4}; |
这种标量计算循环次数通常取决于数组大小,我们看下基于矢量的计算:
1 | int[] a = {1, 2, 3, 4}; |
上面代码首先利用静态方法IntVector.fromArray通过两个数据源数组创建了两个IntVectors。
第一个参数是矢量的大小,后面的参数分别是数组以及数组的偏移量。这边设置了矢量的大小为 128bit,因为java中int占4字节(484)。
在特定的架构中,编译器可以优化字节码,让循环从4次降低为1次,这种优化可以为机器学习与密码学带来好处。
目前此特性还在孵化中,后续可能会有变化。
Enable C++14 Language Features
JEP 347 允许 在 JDK 的 C++ 源码中使用 C++ 14 的语言特性,并且给出了哪些特性可以在 HotSpot 代码中使用的具体说明。
和java开发者关系不大。
Migrate from Mercurial to Git
在此之前,OpenJDK 源代码是使用版本管理工具 Mercurial 进行管理的,你也可以在 http://hg.openjdk.java.net/ 查看 OpenJDK 的源代码历史版本。
但是现在迁移到了 GIt ,主要原因如下:
- Mercurial 生成的版本控制元数据过大。
- Mercurial 相关的开发工具比较少,而 Git 几乎在所有的主流 IDE 中已经无缝集成。
- Mercurial 相关的服务比较少,无论是自建托管,还是服务托管。
为了优雅的迁移到 Git,OpenJDK 做了如下操作。
- 将所有的单存储库 OpenJDK 项目从 Mercurial 迁移到 Git。
- 保留所有的版本控制历史,也包括 Tag。
- 根据 Git 的最佳实践重新格式化提交的消息。
- 创建了一个工具用来在 Mercurial 和 Git 哈希之间进行转换。
github 地址 https://github.com/openjdk/
ZGC: Concurrent Thread-Stack Processing
这次改动让 ZGC 线程堆栈处理从 Safepoints 移动到并发阶段。
Unix-Domain Socket Channels
添加 UnixDomainSocketAddress.java 类用于支持 Unix 域套接字通道。
添加 Unix-domain socket 到 SocketChannel 和 ServerSocketChannel API 中。
添加枚举信息 java.net.StandardProtocolFamily.UNIX。
Alpine Linux Port
Apine Linux 是一个独立的、非商业的 Linux 发行版,它十分的小,一个容器需要不超过 8MB 的空间,最小安装到磁盘只需要大约 130MB 存储空间,并且十分的简单,同时兼顾了安全性。
此提案将 JDK 移植到了 Apline Linux,由于 Apline Linux 是基于 musl lib 的轻量级 Linux 发行版,因此其他 x64 和 AArch64 架构上使用 musl lib 的 Linux 发行版也适用。
Elastic Metaspace
自从引入了 Metaspace 以来,根据反馈,Metaspace 经常占用过多的堆外内存,从而导致内存浪费,现在可以更及时地将未使用的 HotSpot class-metaspace 内存返还给操作系统,从而减少 Metaspace 的占用空间,并优化了 Metaspace 代码以降低后续的维护成本。
Windows/AArch64 Port
将 JDK 移植到 Windows/AArch64 架构上,Windows/AArch64 已经是终端用户市场的热门需求。
Foreign Linker API (Incubator)
这项提案让 Java 代码可以调用由其他语言(比如 C ,C++)编写的编译后的机器代码,替换了之前的 JNI 形式。
不过这还是一个孵化中的功能,运行时需要添加 –add-modules jdk.incubator.foreign 参数来编译和运行 Java 代码。
下面是一个调用 C 语言函数方法,然后输出运行结果的例子。
1.编写一个 C 函数打印一个 “hello world”:
1 | #include <stdio.h> |
2.将上面的代码编译,然后输出到共享库 hello.so
1 | $ gcc -c -fPIC hello.c |
3.编写一个 Java 代码,调用 hello.so 的 printHello 方法。
1 | import jdk.incubator.foreign.CLinker; |
4.Java 代码编译:
1 | $ javac --add-modules jdk.incubator.foreign JEP389.java |
5.Java 代码执行:
1 | $ java --add-modules jdk.incubator.foreign -Dforeign.restricted=permit JEP389.java |
Warnings for Value-Based Classes
添加了一个注解,用于标识当前是是基于值的类,比如 Java 8 引入的预防空指针的 Optional 类,现在已经添加了注解标识。
1 | .internal.ValueBased |
Packaging Tool
在 Java 14 中,JEP 343 引入了打包工具,命令是 jpackage,在 Java 14 新功能文章里也做了介绍:
使用 jpackage 命令可以把 JAR 包打包成不同操作系统支持的软件格式:
1 | jpackage --name myapp --input lib --main-jar main.jar --main-class myapp.Main |
常见平台格式如下:
- Linux:
debandrpm - macOS:
pkganddmg - Windows:
msiandexe
要注意的是,jpackage 不支持交叉编译,也就是说在 windows 平台上是不能打包成 macOS 或者 Linux 系统的软件格式的。
在 Java 15 中,继续孵化,现在在 Java 16 中,终于成为了正式功能。
下面是一个例子,把一个简单的 Java Swing 程序打包成当前操作系统支持的软件格式,然后安装到当前电脑。
1 | import javax.swing.*; |
编译后,创建一个 JAR 文件
1 | $ javac JEP392.java |
将生成的 JEP392.jar 打包到符合当前平台的软件包中:
1 | $ ~/develop/jdk-16.0.1.jdk/Contents/Home/bin/jpackage -i . -n JEP392 --main-jar hello.jar --main-class JEP392 |
ll 后显示的 JEP392-1.0.dmg(用的 MacOS ,所以格式是 dmg)就是打包后的结果。
双击这个文件后可以像 mac 软件一样安装。其他平台类似。
不同的系统安装位置不同:
- Linux:
/opt - MacOS :
/Applications - Windows:
C:\Program Files\
Foreign-Memory Access API (Third Incubator)
此提案旨在引入新的 API 以允许 Java 程序安全有效的访问 Java 堆之外的内存。相关提案早在 Java 14 的时候就已经提出了,在 Java 15 中重新孵化,现在在 Java 16 中再次孵化。
此提案的目标如下:
- 通用:单个 API 应该能够对各种外部内存(如本机内存、持久内存、堆内存等)进行操作。
- 安全:无论操作何种内存,API 都不应该破坏 JVM 的安全性。
- 控制:可以自由的选择如何释放内存(显式、隐式等)。
- 可用:如果需要访问外部内存,API 应该是 sun.misc.Unsafe.
Pattern Matching for instanceof
改进 instanceof 在 Java 14 中已经提出,在 Java 15 中继续预览,而现在,在 Java 16 中成为正式功能。
在之前,使用 instanceof 需要如下操作:
1 | Object obj = "TEST"; |
现在
1 | Object obj = "TEST"; |
Records
Record 成为 Java 16 的正式功能。java 14其实已经介绍过了,这个功能在 Java 15 中进行二次预览,在 Java 16 中正式发布。
record 是一种全新的类型,它本质上是一个 final 类,同时所有的属性都是 final 修饰,它会自动编译出 public get hashcode equals toString 等方法,减少了代码编写量。
Strongly Encapsulate JDK Internals by Default
Java 9 JEP 261 引入了 --illegal-access 控制内部 API 访问和 JDK 打包的选项。
此 JEP 将 --illegal-access 选项的默认模式从允许更改为拒绝。通过此更改,JDK 的内部包和 API(关键内部 API 除外)将不再默认打开。
该 JEP 的动机是阻止第三方库、框架和工具使用 JDK 的内部 API 和包,增加了安全性。
Sealed Classes (Second Preview)
java 15介绍过了,二次预览。
简述:
我们都知道,在 Java 中如果想让一个类不能被继承和修改,这时我们应该使用
final关键字对类进行修饰。不过这种要么可以继承,要么不能继承的机制不够灵活,有些时候我们可能想让某个类可以被某些类型继承,但是又不能随意继承,是做不到的。Java 15 尝试解决这个问题,引入了sealed类,被sealed修饰的类可以指定子类。这样这个类就只能被指定的类继承。
而且sealed修饰的类的机制具有传递性,它的子类必须使用指定的关键字进行修饰,且只能是final、sealed、non-sealed三者之一。