一帆磨砺

生活所迫,一叶孤舟

0%

Java版本特性-JDK17

引用参考文档链接

  1. jdk17特性列表
  2. Java 随机数相关 API 的演进与思考(上)
  3. Java 随机数相关 API 的演进与思考(下)
  4. 支持上下文的序列化过滤器,又一次给序列化打补丁

写在开头

  1. 浮点数计算将跨系统平台结果统一,不过float这个坑已经几乎所有人都知道,这个改动即便知道大部分也会遵循之前的安全习惯
  2. switch的匹配模式增强虽然还没开放,但可以直观的看出对简化代码的帮助很大
  3. 密封类sealed将会让多态环境更好控制,避免代码的无序扩张
  4. 反序列化过滤器没有想到使用场景,但是从介绍到demo可以直观的感受到它的重要性,一个是安全性,一个是反序列化过程的可控性
  5. 调用本地方法和操作堆外内存是非常重要的点,只是平时接触和使用的机会太少,这个特性未来能够正式使用一定能绽放精彩

float跨平台值统一

使浮点运算始终保持严格。所有的浮点数计算都遵循strictfp
该特性令我比较疑惑。疑惑的点是这个遵循的是所有系统平台执行的结果一致还是没有了数据波动。

案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class StrictFloat {
public static void func() {
float a = 0.1231231f;
double b = 0.23456d;
double c = a + b;
System.out.println(c);
}
public strictfp static void func2() {
float a = 0.1231231f;
double b = 0.23456d;
double c = a + b;
System.out.println(c);
}
}
// JDK8 中执行结果
// 0.3576831018900871
// 0.3576831018900871
// JDK17 执行 func 结果
// 0.3576831018900871

疑惑

按照个人理解的strictfp,执行结果应该是 0.3576831
但是依然有不稳定的波动值存在,因此只能理解为跨平台系统的执行结果统一了。。。。
上方案例代码在JDK8反编译汇编后,两个函数指令集一样,只有函数2有strictfp修饰符一个区别

JDK17不需要使用strictfp

1
2
3
4
5
6
7
//    Modifier 'strictfp' is redundant on Java 17 and later
public strictfp static void func2() {
float a = 0.1231231f;
double b = 0.23456d;
double c = a + b;
System.out.println(c);
}

增强PRNG(伪随机数生成器)

提供新的接口类型和实现,包括可跳转 PRNG 和另一类可拆分 PRNG 算法 (LXM)。
具体详细内容和关于随机数的可以浏览博文Java 随机数相关 API 的演进与思考(上)/Java 随机数相关 API 的演进与思考(下),博主写的非常详尽

macOS新的渲染管道

使用 Apple Metal API 为 macOS 实现 Java 2D 内部渲染管道,以替代使用已弃用的 Apple OpenGL API 的现有管道。

系统平台架构兼容: macOS/AArch64

弃用 Applet API

It is essentially irrelevant since all web-browser vendors have either removed support for Java browser plug-ins or announced plans to do so.

市面主流浏览器已经不再支持Java的浏览器插件,这个API已经没有作用了。

JDK内部代码继续加强 强封装

除了部分关键代码,对其他所有JDK内部的Java类内部元素进行强封装,以提高JDK的安全性和可维护性。

switch模式匹配增强(预调整)

因还未开放使用,因此使用官方文档案例说明:

1
2
3
4
5
6
7
8
9
10
static String formatterPatternSwitch(Object o) {
return switch (o) {
case null -> System.out.println("Oops");
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 && (s.length() > 100) -> String.format("String %s", s);
default -> o.toString();
};
}
  • 可跨类型匹配,带匹配对象无类型限制(从文档看起来是这样)
  • 可匹配null
  • 可在匹配条件添加判断条件

删除 RMI Activation

JDK15标注弃用,JDK17删除。
删除远程方法调用 (RMI) 激活机制,同时保留 RMI 的其余部分。

密封类-开放使用

与JDK16相比,JDK17没有新增变更。

  • sealed修饰的类必须有子类
  • non-sealed修饰的子类被其他类继承不受限制
  • sealed修饰的类的子类在不同包时,所有类必须在同一个模块下(同一个module-info.java

删除实验性 AOT 和 JIT 编译器

官方发现没啥人用,就给删了。(维护工作量大)
保留了实验性的JVMCI。

弃用安全管理器(Security Manager)

根据官方文档的说法,安全管理器使用率较低,有一部分框架甚至避开JDK内置策略实现自定义安全管理器。加上官方认定的性能不佳/权限模型脆弱/编码困难,因此决定弃用。
但是没有提到替代方案,只是在未来工作列表中表示会在多个方面增强安全性。

本地方法调用和操作堆外内存 API(孵化器)

JDK14/15/16已经进行过孵化,这次是在之前基础之上的演变。
官方文档没说和之前相比的变动内容,挖个坑,等以后填。。。。。

Vector API(第二次孵化器)

  • 增强 API 以支持字符操作,例如 UTF-8 字符解码。short具体来说,我们添加了用于在向量和数组之间复制字符的方法char,以及用于与整数向量进行无符号比较的新向量比较运算符。
  • byte用于将向量转换为数组和从boolean 数组转换为 API 的增强功能。
  • 使用英特尔的短向量数学库 (SVML)对 x64 上的超越和三角通道运算的内在支持。
  • 对 Intel x64 和 ARM NEON 实施的一般性能增强。

特定于上下文的反序列化过滤器

结合官方文档和支持上下文的序列化过滤器,又一次给序列化打补丁大致明白了这个是干啥的。
简单的总结就是在所有使用ObjectInputStream的时候,都会经过反序列化过滤器,如果不符合所有过滤器,则无法反序列化。

案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class DeserializationFilter {

/**
* 可以先不执行该方法正常序列化/反序列化
*/
public static void func() {
// 该过滤器将只允许java.base路径的类反序列化,其他所有反序列化操作均拒绝
ObjectInputFilter filter = ObjectInputFilter.Config.createFilter("java.base/*;!*");
ObjectInputFilter serialFilter = ObjectInputFilter.Config.getSerialFilter();
ObjectInputFilter.Config.setSerialFilter(filter);
}

public static void serial(Object obj) {
try {
FileOutputStream fileOutputStream = new FileOutputStream("test");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(obj);
objectOutputStream.close();
fileOutputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}

public static void deserial () {
try {
FileInputStream fileInputStream = new FileInputStream("test");
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
Object o = objectInputStream.readObject();
if (o instanceof Shape s) {
System.out.println("deserial:");
s.func();
}
} catch (IOException | ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
}

不设置过滤器

1
2
3
4
5
6
7
8
public static void main(String[] args) {
// DeserializationFilter.func();
DeserializationFilter.serial(cirle);
DeserializationFilter.deserial();
}
// 反序列化正常
// deserial:
// com.janwarlen.feature.Circle

设置过滤器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public static void main(String[] args) {
DeserializationFilter.func();
DeserializationFilter.serial(cirle);
DeserializationFilter.deserial();
}
// 反序列化失败
Exception in thread "main" java.lang.RuntimeException: java.io.InvalidClassException: filter status: REJECTED
at JDK17/com.janwarlen.feature.DeserializationFilter.deserial(DeserializationFilter.java:38)
at JDK17/com.janwarlen.Demo.main(Demo.java:26)
Caused by: java.io.InvalidClassException: filter status: REJECTED
at java.base/java.io.ObjectInputStream.filterCheck(ObjectInputStream.java:1414)
at java.base/java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:2055)
at java.base/java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1909)
at java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2235)
at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1744)
at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:514)
at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:472)
at JDK17/com.janwarlen.feature.DeserializationFilter.deserial(DeserializationFilter.java:32)
... 1 more

欢迎关注我的其它发布渠道