Java 17 新功能介绍(LTS)
点赞再看,动力无限。Hello world : ) 微信搜「程序猿阿朗 」。
本文 Github.com/niumoo/JavaNotes 和 未读代码博客 已经收录,有很多知识点和系列文章。

Java 17 在 2021 年 9 月 14 日正式发布,Java 17 是一个长期支持(LTS)版本,这次更新共带来 14 个新功能。
OpenJDK Java 17 下载:https://jdk.java.net/archive/
OpenJDK Java 17 文档:https://openjdk.java.net/projects/jdk/17/
| JEP | 描述 |
|---|---|
| JEP 306 | 恢复始终严格的浮点语义 |
| JEP 356 | 增强的伪随机数生成器 |
| JEP 382 | 使用新的 macOS 渲染库 |
| JEP 391 | 支持 macOS/AArch64 架构 |
| JEP 398 | 删除已启用的 Applet API |
| JEP 403 | 更强的封装 JDK 内部封装 |
| JEP 406 | Switch 模式匹配(预览) |
| JEP 407 | 移除 RMI Activation |
| JEP 409 | 密封类(Sealed Classes) |
| JEP 410 | JEP 401:移除实验性的 AOT 和 JIT 编译器 |
| JEP 411 | 弃用 Security Manager |
| JEP 412 | 外部函数和内存 API(孵化器) |
| JEP 414 | Vector API(第二孵化器) |
| JEP 415 | 指定上下文的反序列化过滤器 |
此文章属于Java 新特性教程 系列,会介绍 Java 每个版本的新功能,可以点击浏览。
1. JEP 306: 恢复始终严格的浮点语义
既然是恢复严格的浮点语义,那么说明在某个时间点之前,是始终严格的浮点语义的。其实在 Java SE 1.2 之前,所有的浮点计算都是严格的,但是以当初的情况来看,过于严格的浮点计算在当初流行的 x86 架构和 x87 浮点协议处理器上运行,需要大量的额外的指令开销,所以在 Java SE 1.2 开始,需要手动使用关键字 strictfp(strict float point) 才能启用严格的浮点计算。
但是在 2021 年的今天,硬件早已发生巨变,当初的问题已经不存在了,所以从 Java 17 开始,恢复了始终严格的浮点语义这一特性。
扩展:strictfp 是 Java 中的一个关键字,大多数人可能没有注意过它,它可以用在类、接口或者方法上,被 strictfp 修饰的部分中的 float 和 double 表达式会进行严格浮点计算。
下面是一个示例,其中的 testStrictfp() 被 strictfp 修饰。
package com.wdbyte;
public class Main {
public static void main(String[] args) {
testStrictfp();
}
public strictfp static void testStrictfp() {
float aFloat = 0.6666666666666666666f;
double aDouble = 0.88888888888888888d;
double sum = aFloat + aDouble;
System.out.println("sum: " + sum);
}
}
2. JEP 356:增强的伪随机数生成器
为伪随机数生成器 RPNG(pseudorandom number generator)增加了新的接口类型和实现,让在代码中使用各种 PRNG 算法变得容易许多。
这次增加了 RandomGenerator 接口,为所有的 PRNG 算法提供统一的 API,并且可以获取不同类型的 PRNG 对象流。同时也提供了一个新类 RandomGeneratorFactory 用于构造各种 RandomGenerator 实例,在 RandomGeneratorFactory 中使用 ServiceLoader.provider 来加载各种 PRNG 实现。
下面是一个使用示例:随便选择一个 PRNG 算法生成 5 个 10 以内的随机数。
package com.wdbyte.java17;
import java.util.Date;
import java.util.random.RandomGenerator;
import java.util.random.RandomGeneratorFactory;
import java.util.stream.Stream;
/**
* @author niulang
*/
public class JEP356 {
public static void main(String[] args) {
RandomGeneratorFactory<RandomGenerator> l128X256MixRandom = RandomGeneratorFactory.of("L128X256MixRandom");
// 使用时间戳作为随机数种子
RandomGenerator randomGenerator = l128X256MixRandom.create(System.currentTimeMillis());
for (int i = 0; i < 5; i++) {
System.out.println(randomGenerator.nextInt(10));
}
}
}
得到输出:
7
3
4
4
6
你也可以遍历出所有的 PRNG 算法。
RandomGeneratorFactory.all().forEach(factory -> {
System.out.println(factory.group() + ":" + factory.name());
});
得到输出:
LXM:L32X64MixRandom
LXM:L128X128MixRandom
LXM:L64X128MixRandom
Legacy:SecureRandom
LXM:L128X1024MixRandom
LXM:L64X128StarStarRandom
Xoshiro:Xoshiro256PlusPlus
LXM:L64X256MixRandom
Legacy:Random
Xoroshiro:Xoroshiro128PlusPlus
LXM:L128X256MixRandom
Legacy:SplittableRandom
LXM:L64X1024MixRandom
可以看到 Legacy:Random 也在其中,新的 API 兼容了老的 Random 方式,所以你也可以使用新的 API 调用 Random 类生成随机数。
// 使用 Random
RandomGeneratorFactory<RandomGenerator> l128X256MixRandom = RandomGeneratorFactory.of("Random");
// 使用时间戳作为随机数种子
RandomGenerator randomGenerator = l128X256MixRandom.create(System.currentTimeMillis());
for (int i = 0; i < 5; i++) {
System.out.println(randomGenerator.nextInt(10));
}
扩展阅读:增强的伪随机数生成器
3. JEP 382:使用新的 macOS 渲染库
macOS 为了提高图形的渲染性能,在 2018 年 9 月抛弃了之前的 OpenGL 渲染库 ,而使用了 Apple Metal 进行代替。Java 17 这次更新开始支持 Apple Metal,不过对于 API 没有任何改变,这一些都是内部修改。
4. JEP 391:支持 macOS/AArch64 架构
起因是 Apple 在 2020 年 6 月的 WWDC 演讲中宣布,将开启一项长期的将 Macintosh 计算机系列从 x64 过度到 AArch64 的长期计划,因此需要尽快的让 JDK 支持 macOS/AArch64 。
Linux 上的 AArch64 支持以及在 Java 16 时已经支持,可以查看之前的文章了解。
5. JEP 398:删除已弃用的 Applet API
Applet 是使用 Java 编写的可以嵌入到 HTML 中的小应用程序,嵌入方式是通过普通的 HTML 标记语法,由于早已过时,几乎没有场景在使用了。
示例:嵌入 Hello.class
<applet code="Hello.class" height=200 width=200></applet>
Applet API 在 Java 9 时已经标记了废弃,现在 Java 17 中将彻底删除。
6. JEP 403:更强的 JDK 内部封装
如 Java 16 的 JEP 396 中描述的一样,为了提高 JDK 的安全性,使 --illegal-access 选项的默认模式从允许更改为拒绝。通过此更改,JDK 的内部包和 API(关键内部 API 除外)将不再默认打开。
但是在 Java 17 中,除了 sun.misc.Unsafe ,使用 --illegal-access 命令也不能打开 JDK 内部的强封装模式了,除了 sun.misc.Unsafe API .
在 Java 17 中使用 --illegal-access 选项将会得到一个命令已经移除的警告。
➜ bin ./java -version
openjdk version "17" 2021-09-14
OpenJDK Runtime Environment (build 17+35-2724)
OpenJDK 64-Bit Server VM (build 17+35-2724, mixed mode, sharing)
➜ bin ./java --illegal-access=warn
OpenJDK 64-Bit Server VM warning: Ignoring option --illegal-access=warn; support was removed in 17.0
7. JEP 406:switch 的类型匹配(预览)
如 instanceof 一样,为 switch 也增加了类型匹配自动转换功能。
在之前,使用 instanceof 需要如下操作:
if (obj instanceof String) {
String s = (String) obj; // grr...
...
}
多余的类型强制转换,而现在:
if (obj instanceof String s) {
// Let pattern matching do the work!
...
}
switch 也可以使用类似的方式了。
static String formatterPatternSwitch(Object o) {
return switch (o) {
case Integer i -> String.format("int %d", i);
case Long l -> String.format("long %d", l);
case Double d -> String.format("double %f", d);
case String s -> String.format("String %s", s);
default -> o.toString();
};
}
对于 null 值的判断也有了新的方式。
// Java 17 之前
static void testFooBar(String s) {
if (s == null) {
System.out.println("oops!");
return;
}
switch (s) {
case "Foo", "Bar" -> System.out.println("Great");
default -> System.out.println("Ok");
}
}
// Java 17
static void testFooBar(String s) {
switch (s) {
case null -> System.out.println("Oops");
case "Foo", "Bar" -> System.out.println("Great");
default -> System.out.println("Ok");
}
}
扩展阅读: JEP 406:switch 的类型匹配(预览)
8. JEP 407:移除 RMI Activation
移除了在 JEP 385 中被标记废除的 RMI(Remote Method Invocation)Activation,但是 RMI 其他部分不会受影响。
RMI Activation 在 Java 15 中的 JEP 385 已经被标记为过时废弃,至今没有收到不良反馈,因此决定在 Java 17 中正式移除。
9. JEP 409:密封类(Sealed Classes)
Sealed Classes 在 Java 15 中的 JEP 360 中提出,在 Java 16 中的 JEP 397 再次预览,现在 Java 17 中成为正式的功能,相比 Java 16 并没有功能变化,这里不再重复介绍,想了解的可以参考之前文章。
10. JEP 401:移除实验性的 AOT 和 JIT 编译器
在 Java 9 的 JEP 295 中,引入了实验性的提前编译 jaotc 工具,但是这个特性自从引入依赖用处都不太大,而且需要大量的维护工作,所以在 Java 17 中决定删除这个特性。
主要移除了三个 JDK 模块:
- jdk.aot - jaotc 工具。
- Jdk.internal.vm.compiler - Graal 编译器。
- jdk.internal.vm.compiler.management
同时也移除了部分与 AOT 编译相关的 HotSpot 代码:
src/hotspot/share/aot— dumps and loads AOT code- Additional code guarded by
#if INCLUDE_AOT
11. JEP 411:弃用 Security Manager
Security Manager 在 JDK 1.0 时就已经引入,但是它一直都不是保护服务端以及客户端 Java 代码的主要手段,为了 Java 的继续发展,决定弃用 Security Manager,在不久的未来进行删除。
@Deprecated(since="17", forRemoval=true)
public class SecurityManager {
// ...
}
12. JEP 412:外部函数和内存 API (孵化)
新的 API 允许 Java 开发者与 JVM 之外的代码和数据进行交互,通过调用外部函数,可以在不使用 JNI 的情况下调用本地库。
这是一个孵化功能;需要添加--add-modules jdk.incubator.foreign来编译和运行 Java 代码。
历史
- 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(孵化器)。
13. JEP 414:Vector API(二次孵化)
在 Java 16 中引入一个新的 API 来进行向量计算,它可以在运行时可靠的编译为支持的 CPU 架构,从而实现更优的计算能力。
现在 Java 17 中改进了 Vector API 性能,增强了例如对字符的操作、字节向量与布尔数组之间的相互转换等功能。
14. JEP 415:指定上下文的反序列化过滤器
Java 中的序列化一直都是非常重要的功能,如果没有序列化功能,Java 可能都不会占据开发语言的主导地位,序列化让远程处理变得容易和透明,同时也促进了 Java EE 的成功。
但是 Java 序列化的问题也很多,它几乎会犯下所有的可以想象的错误,为开发者带来持续的维护工作。但是要说明的是序列化的概念是没有错的,把对象转换为可以在 JVM 之间自由传输,并且可以在另一端重新构建的能力是完全合理的想法,问题在于 Java 中的序列化设计存在风险,以至于爆出过很多和序列化相关的漏洞。
反序列化危险的一个原因是,有时候我们不好验证将要进行反序列化的内容是否存在风险,而传入的数据流可以自由引用对象,很有可能这个数据流就是攻击者精心构造的恶意代码。
所以,JEP 415 允许在反序列化时,通过一个过滤配置,来告知本次反序列化允许或者禁止操作的类,反序列化时碰到被禁止的类,则会反序列化失败。
14.1. 反序列化示例
假设 Dog 类中的 Poc 是恶意构造的类,但是正常反序列化是可以成功的。
package com.wdbyte.java17;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
/**
* @author niulang
*/
public class JEP415 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Dog dog = new Dog("哈士奇");
dog.setPoc(new Poc());
// 序列化 - 对象转字节数组
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);) {
objectOutputStream.writeObject(dog);
}
byte[] bytes = byteArrayOutputStream.toByteArray();
// 反序列化 - 字节数组转对象
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
Object object = objectInputStream.readObject();
System.out.println(object.toString());
}
}
class Dog implements Serializable {
private String name;
private Poc poc;
public Dog(String name) {
this.name = name;
}
@Override
public String toString() {
return "Dog{" + "name='" + name + '\'' + '}';
}
// get...set...
}
class Poc implements Serializable{
}
输出结果:
Dog{name='哈士奇'}
14.2. 反序列化过滤器
在 Java 17 中可以自定义反序列化过滤器,拦截不允许的类。
package com.wdbyte.java17;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputFilter;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
/**
* @author niulang
*/
public class JEP415 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Dog dog = new Dog("哈士奇");
dog.setPoc(new Poc());
// 序列化 - 对象转字节数组
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);) {
objectOutputStream.writeObject(dog);
}
byte[] bytes = byteArrayOutputStream.toByteArray();
// 反序列化 - 字节数组转对象
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
// 允许 com.wdbyte.java17.Dog 类,允许 java.base 中的所有类,拒绝其他任何类
ObjectInputFilter filter = ObjectInputFilter.Config.createFilter(
"com.wdbyte.java17.Dog;java.base/*;!*");
objectInputStream.setObjectInputFilter(filter);
Object object = objectInputStream.readObject();
System.out.println(object.toString());
}
}
class Dog implements Serializable {
private String name;
private Poc poc;
public Dog(String name) {
this.name = name;
}
@Override
public String toString() {
return "Dog{" + "name='" + name + '\'' + '}';
}
// get...set...
}
class Poc implements Serializable{
}
这时反序列化会得到异常。
Exception in thread "main" java.io.InvalidClassException: filter status: REJECTED
at java.base/java.io.ObjectInputStream.filterCheck(ObjectInputStream.java:1412)
at java.base/java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:2053)
at java.base/java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1907)
....
参考
<完>
文章持续更新,可以微信搜一搜「 程序猿阿朗 」或访问「程序猿阿朗博客 」第一时间阅读。本文 Github.com/niumoo/JavaNotes 已经收录,有很多知识点和系列文章,欢迎Star。

Java 17 新功能介绍(LTS)的更多相关文章
- Java 19 新功能介绍
点赞再看,动力无限. 微信搜「程序猿阿朗 」. 本文 Github.com/niumoo/JavaNotes 和 未读代码博客 已经收录,有很多知识点和系列文章. Java 19 在2022 年 9 ...
- Java 14 新功能介绍
不做标题党,认认真真写个文章. 文章已经收录在 Github.com/niumoo/JavaNotes 和未读代码博客,点关注,不迷路. Java 14 早在 2019 年 9 月就已经发布,虽然不是 ...
- 超详细 Java 15 新功能介绍
点赞再看,动力无限.微信搜「程序猿阿朗 」,认认真真写文章. 本文 Github.com/niumoo/JavaNotes 和 未读代码博客 已经收录,有很多知识点和系列文章. Java 15 在 2 ...
- Java 16 新功能介绍
点赞再看,动力无限.Hello world : ) 微信搜「程序猿阿朗 」. 本文 Github.com/niumoo/JavaNotes 和 程序猿阿朗博客 已经收录,有很多知识点和系列文章. Ja ...
- Java 18 新功能介绍
文章持续更新,可以关注公众号程序猿阿朗或访问未读代码博客. 本文 Github.com/niumoo/JavaNotes 已经收录,欢迎Star. Java 18 在2022 年 3 月 22 日正式 ...
- CentOS以及Oracle数据库发展历史及各版本新功能介绍, 便于构造环境时有个对应关系
CentOS版本历史 版本 CentOS版本号有两个部分,一个主要版本和一个次要版本,主要和次要版本号分别对应于RHEL的主要版本与更新包,CentOS采取从RHEL的源代码包来构建.例如CentOS ...
- Java 11 新特性介绍
Java 11 已于 2018 年 9 月 25 日正式发布,之前在Java 10 新特性介绍中介绍过,为了加快的版本迭代.跟进社区反馈,Java 的版本发布周期调整为每六个月一次——即每半年发布一个 ...
- Java 12 新特性介绍,快来补一补
Java 12 早在 2019 年 3 月 19 日发布,它不是一个长久支持(LTS)版本.在这之前我们已经介绍过其他版本的新特性,如果需要可以点击下面的链接进行阅读. Java 11 新特性介绍 J ...
- 【Linux】Ubuntu18.04镜像下载,新功能介绍
一.Ubuntu18.04镜像下载 官方下载地址:http://releases.ubuntu.com/18.04/ 官方64位iso下载地址:http://releases.ubuntu.com/1 ...
随机推荐
- webrtc源码阅读理解一
webrtc是一个比较成熟的实时音视频处理开源项目,一上来老大就扔给我一本webrtc native实践,虽然狠下心"翻"完了一遍,但是还是云里雾里的,在经过几个月的摸索之后,我大 ...
- navicat导出DDL语句
工作中有的时候需要将某个库中的表.视图.函数.存储过程等创建语句导出,又不需要表中的数据. 方法一:需要拷贝的创建语句条数不多,可以选择直接拷贝DDL语句 方法二:使用Navicat的备份功能
- sqlalchemy 查询结果转json个人解决方案
参考了网上很多资料,自己搞了一个适合的 在model 内增加一个函数: class User(db.Model): __tablename__ = 'user' userid = db.Column( ...
- 『GoLang』结构体与方法
结构体 结构体类型 Go 通过结构体的形式支持用户自定义类型,或者叫定制类型. Go 语言结构体是实现自定义类型的一种重要数据类型. 结构体是复合类型(composite types),它由一系列属性 ...
- asp.net core 集成swagger ui
什么是Swagger? 说swagger 之前,我们先说一下OpenApi 规范. OpenApi 是一种和语言无关的用于描述RESTAPIs 接口功能的一种规范,对RESTAPIs 接口的描述包括: ...
- Java运行时异常与非运行时异常
Java运行时异常与非运行时异常 Exception(异常)是程序本身可以处理的异常.主要包含RuntimeException等运行时异常和IOException,SQLException等非运行时异 ...
- Bayou复制分布式存储系统
本文主要参考文献[1]完成. 第1章导读 Bayou是一个复制的.弱一致性的存储系统,用于移动计算环境.为了最大化可用性,Bayou为用户提供了可以任意读写访问的副本.Bayou的设计侧重于为应用程序 ...
- MySQL5.7主从复制-异步复制搭建
两台服务器,系统是Redhat6.5,MySQL版本是5.7.18.1.在主库上,创建复制使用的用户,并授予replication slave权限.这里创建用户repl,可以从IP为10.10.10 ...
- 如何无缝迁移 SpringCloud/Dubbo 应用到 Serverless 架构
作者 | 行松 阿里巴巴云原生团队 本文整理自<Serverless 技术公开课>,"Serverless"公众号后台回复"入门",即可获取系列文章 ...
- dev分支和release是什么
master(主分支) 存在一条主分支(master).所有用户可见的正式版本,都从master发布(也是用于部署生产环境的分支,确保master分支稳定性).主分支作为稳定的唯一代码库,不做任何开发 ...