--- title: Java 17的新特性 tags: - JVM - Java - Java 17 - 新特性 - switch - 密封类 categories: - - JVM - Java keywords: 'Java,Java 16,新特性,switch,模式匹配,AArch64,密封类,伪随机数生成器' date: 2022-02-08 09:06:15 --- Java 17是截止到目前最新的长期支持版(LTS),其实相对于之前的一个Java发行版本,Java 17引入的新特性并不多,而与日常应用中所密切相关的特性也是比较少的,更多的是使之前版本中处于预览和孵化状态的特性转正。 本系列的文章有: - {% post_link nf-java8 %} - {% post_link nf-java9 %} - {% post_link nf-java10 %} - {% post_link nf-java11 %} - {% post_link nf-java12 %} - {% post_link nf-java13 %} - {% post_link nf-java14 %} - {% post_link nf-java15 %} - {% post_link nf-java16 %} - {% post_link nf-java17 %} ## 恢复严格浮点模式 在Java 1.2以后,受处理器浮点运算性能的影响,也为了追求更好的浮点计算性能,Java采用了宽松的浮点模式。在宽松模式下,Java程序的浮点计算结果是由处理器决定的,而不是采用IEEE标准。例如Intel处理器的浮点计算精度为80bit,但IEEE标准的浮点精度为64bit,那么此时程序在做浮点运算的时候,计算结果就会与IEEE标准之间产生误差。这样推广出去,相同的程序在不同的硬件平台上,可能计算得到的浮点数结果相互之间都不一样,这就对程序计算结果的准确性造成了比较大的影响。 但是如果使用Java之前提供的`strictfp`关键字显式声明需要使用严格浮点模式,那么程序就会使用IEEE标准对浮点数据进行处理,这样一来在不同的硬件平台上,浮点计算的结果也就一致了。在Java 17以前,要在程序中使用严格浮点模式,必须显式使用`strictfp`关键字进行标记;在Java 17以后,程序进行浮点计算将默认采用严格模式,不必再手动使用`strictfp`开启严格模式了。 ## 增强了伪随机数生成器 Java 17针对伪随机数生成器(PRNG)增加了一个新的`RandomGenerator`接口,这个接口为所有的PRNG算法提供了统一的API。新增的`RandomGenerator`接口的实例可以通过新增的`RandomGeneratorFactory`工厂类来构建。 例如以下是一个使用指定算法进行随机数生成的示例。 ```java RandomGeneratorFactory randomAlgorithm = RandomGeneratorFactory.of("L128X256MixRandom"); RandomGenerator generator = randomAlgorithm.create(System.currentTimeMillis()); System.out.println(generator.nextInt(10)); ``` 通过`RandomGeneratorFactory`提供的`all()`方法可以遍历出目前JDK中内置的所有PRNG算法。由于新的API中也内置了老的随机数生成器算法,所以使用新的API也是可以调用老的随机数生成器来生成随机数的。 ## 提供了对于macOS/AArch64架构的支持 由于Apple在2020年的WWDC中宣布将会将Mac计算机从x64架构逐步过渡到AArch64架构,所以为了JDK的平台兼容性,必须引入对AArch64架构的支持。 如果开发者现在使用的是新款采用M1系列芯片的Mac笔记本,那么现在可以放心的安装使用Java 17了。 ## 密封类 密封类在Java 15中被引入,现在在Java 17中已经正式转正。密封类相较于之前的Java版本中没有发生任何变化,所以可以直接按照之前Java 15新特性中的描述使用。 ## 指定上下文的反序列化过滤器 序列化是Java中的一项重要功能,通过把对象转换为可以在JVM间自由传输并自动重新构建的能力让数据的远程处理变得更加便捷和透明。但是Java的序列化功能中漏洞也是非常多的,例如对象的反序列化就是一个比较危险的动作,因为传入的数据流可以自由的引用对象,而我们在使用这些内容的时候又不太容易对其进行验证,这样就给了攻击者通过伪造和构建攻击代码传入攻击数据流导致整个应用面临巨大的风险。 所以在Java 17中,支持在反序列化时,通过设置一个过滤配置,来通知JVM在本次反序列化的时候允许或者禁止操作的类,当反序列化的时候如果碰到了被禁止的类,那么整个反序列化过程就会失败。 例如我们有以下两个可以被序列化的类。 ```java @Data @RequiredArgsConstructor class Person implements Serializable { @NonNull private String name; private Job job; } @Data @RequiredArgsConstructor class Job implements Serializable { @NonNull private String name; } ``` 那么以下的反序列化操作就会失败。 ```java byte[] objectBytes = personBaos.toByteArray(); ByteArrayInputStream bytesInputStream = new ByteArrayInputStream(objectBytes); ObjectInputStream objectInputStream = new ObjectInputStream(bytesInputStream); // 在这里设置反序列化的过滤器,当前语句的设置为允许 xyz.archgrid.demo.entity.person 类、 // java.base 中的所有类,并拒绝其他任何类 ObjectInputFilter filter = ObjectInputFilter.Config .createFilter("xyz.archgrid.demo.entity.Person;java.base/*;!*"); objectInputStream.setObjectInputFilter(filter); Object object = objectInputStream.readObject(); ``` 因为在示例中,`Person`类中使用了`Job`类的实例,但是`Job`类并没有在过滤器列表中列出,所以就会被拒绝。这段代码会抛出一个异常:`java.io.InvalidClassException: filter status: REJECTED`。 ## 预览版功能 ### Switch模式匹配 Switch表达式自从Java 12引入一来,一直在不断的改进和稳定中。在Java 17中,Switch表达式增加了可以用于匹配类型的模式匹配功能,也就是说Switch表达式可以使用类似于`instanceOf`的功能,来对被测量的内容进行判断。 例如可以使用以下这种复合的判断方式。 ```java String description = switch (someValue) { case null -> "没有提供有效的值"; case "test" -> "提供了一个字符串类型的测试值"; case String s -> String.format("提供了一个字符串类型的测试值: [%s]", s); case Integer i -> String.format("提供了一个整型的测试值: [%d]", i); case Long l -> String.format("提供了一个长整型的测试值: [%d]", l); case Double d -> String.format("提供了一个双精度浮点类型的测试值: [%f]", d); default -> "没有列举对于指定类型的判断。"; } ``` ## 移除与废弃的功能 以下在之前的Java发布版中已经标记为废弃的API,在Java 17中已经被删除。 - Applet API。 - RMI Activation。 - 实验性的AOT和JIT编译器。 以下功能在Java 17中已经被标记为废弃,请尽量不要再使用。 - Security Manager。 ## 孵化功能 - Vector,对于向量的计算功能,在Java 17中已经进入二次孵化的阶段,主要增强了对字符的操作以及字节向量与布尔数组之间的相互转换等功能。 - 外部函数和内存API,这套新的API将允许开发者与JVM之外的代码和数据进行交互,可以在不使用JNI的情况下调用系统本地库。