文章目录
本系列文章:
Java(一)数据类型、变量类型、修饰符、运算符
Java(二)分支循环、数组、字符串、方法
Java(三)面向对象、封装继承多态、重写和重载、枚举
Java(四)内部类、包装类、异常、日期
Java(五)反射、克隆、泛型、语法糖、元注解
Java(六)IO、NIO、四种引用
Java(七)JDK1.8新特性
Java(八)JDK1.17新特性
JDK1.17增加了14个新特性,官网的描述:
306: Restore Always-Strict Floating-Point Semantics
356: Enhanced Pseudo-Random Number Generators
382: New macOS Rendering Pipeline
391: macOS/AArch64 Port
398: Deprecate the Applet API for Removal
403: Strongly Encapsulate JDK Internals
406: Pattern Matching for switch (Preview)
407: Remove RMI Activation
409: Sealed Classes
410: Remove the Experimental AOT and JIT Compiler
411: Deprecate the Security Manager for Removal
412: Foreign Function & Memory API (Incubator)
414: Vector API (Second Incubator)
415: Context-Specific Deserialization Filters
Spring Boot 3.0 最低只支持JDK 17,已经不再向下兼容,这就意味着未来很多的开发者必定逐步从JDK8 转到JDK17
。
306:恢复严格的浮点语义
此处涉及了1个Java关键字:strictfp,指"strict floating-point"(严格浮点),它用于确保浮点计算结果的可移植性,从Java2开始引入。
Java浮点运算在不同平台、不同编译器中会有微小的差异,即在不同平台(16/32/64位处理器)上运行类文件时,可能会出现不同的输出(浮点值)。
使用strictfp关键词声明类、接口、方法、变量后,就会强制执行严格的浮点运算,以确保在不同平台上的浮点计算结果的一致性。在这种情况下,Java虚拟机会忽略硬件浮点单位的计算方式,而采用一种特定的、固定的浮点计算方式。
使用strictfp关键词会对性能产生一定的影响,因为它需要使用一种特定的浮点计算方式来进行计算。不过现如今硬件发生了巨大的变化,原来的问题已经不复存在,因此从JDK1.17开始,始终严格的浮点语义功能得到了恢复。
strictfp关键字可以修饰方法和类。从JDK1.2引入到JDK1.17之间,使用关键字strictfp时,程序才有"strict floating-point"(严格浮点)
功能,在JDK1.8中使用示例:
但在JDK1.17中,程序默认有"strict floating-point"(严格浮点)
功能,可以理解为默认使用了关键字strictfp,开发者不再显式使用该关键字。在JDK1.17中使用示例:
356:提供了生成伪随机数的接口
伪随机数接口为RandomGenerator,该接口对应的实现可以从工厂类RandomGeneratorFactory中获取。
生成5个10以内的随机整数示例:
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));
}
上面示例代码中的“L128X256MixRandom”是随机算法中的一种,支持的所有随机算法可以用如下方式查看:
RandomGeneratorFactory.all().forEach(factory -> {
System.out.println(factory.name());
});
// L32X64MixRandom
// L128X128MixRandom
// L64X128MixRandom
// SecureRandom
// L128X1024MixRandom
// L64X128StarStarRandom
// Xoshiro256PlusPlus
// L64X256MixRandom
// Random
// Xoroshiro128PlusPlus
// L128X256MixRandom
// SplittableRandom
// L64X1024MixRandom
382:使用新的macOS渲染库
391:支持macOS/AArch64架构
398:删除了不推荐使用的Applet API
403:强封装JDK内部API
406:switch模式匹配进入预览阶段【重要】
简单来说,就是在switch中,引入了与instance of
类似的类型匹配功能。示例:
public static void main(String[] args) {
System.out.println(formatterPatternSwitch(123)); //int 123
System.out.println(formatterPatternSwitch(123.123d)); //double 123.123000
}
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();
};
}
并且,在JDK1.17中,可以在switch中进行null值的判断了。示例:
// Java 17 之前
static void testFooBar1(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 testFooBar2(String s) {
switch (s) {
case null -> System.out.println("Oops");
case "Foo", "Bar" -> System.out.println("Great");
default -> System.out.println("Ok");
}
}
407:移除RMI激活机制
409:密封类【重要】
简单来说,密封类和接口用来限制哪些其他类或接口可以扩展或实现它们。
在Java中,如果想让一个类不能被继承和修改,应使用final关键字对类进行修饰。不过这种要么可以继承,要么不能继承的机制不够灵活,有些时候我们可能想让某个类可以被某些类型继承,但是又不能随意继承。Java15尝试解决这个问题,引入了sealed关键字,被sealed关键字修饰的类可以指定子类。这样这个类就只能被指定的类继承。
而且sealed关键字修饰的类的机制具有传递性,它的子类必须使用指定的关键字进行修饰,且只能是final 、sealed 、non-sealed三者之一。
使用密封类的一些注意事项:
permits指定的子类必须与父类在同一个显式命名的module下;
permits指定的子类必须直接继承父类;
permits指定的子类必须声明自己的密封类型(继承自哪个密封父类);
final表示这个子类不能再被继承;
non-sealed表示这个子类没有密封限制,随便继承。
- 1、密封类(Sealed Classes)
密封类:一个类可以用sealed修饰,表示这个类只能由permits指定的子类或接口来继承或实现。
抽象父类:
package com.test.demo.animal;
/**
* 一个类或接口可以用sealed修饰,表示这个类或接口只能由permits子句指定的子类或接口来继承或实现
*/
public abstract sealed class Animal permits Dog,Cat,Pig{
public abstract String eat();
}
3个子类:
package com.test.demo.animal;
/**
* non-sealed表示这个子类没有密封限制,随便继承
*/
public non-sealed class Dog extends Animal{
@Override
public String eat() {
return "骨头";
}
}
package com.test.demo.animal;
public final class Cat extends Animal{
@Override
public String eat() {
return "鱼";
}
}
package com.test.demo.animal;
/**
* 类Pig由sealed修饰,只能由permits指定的子类或接口来继承或实现
*/
public sealed class Pig extends Animal permits White,Black{
@Override
public String eat() {
return "饲料";
}
}
- 2、密封接口(Sealed interfaces)
密封接口:一个接口可以用sealed修饰,表示这个接口只能由permits指定的子类或接口来继承或实现。
密封接口的使用和密封类类似。抽象接口:
package com.test.demo.animal;
/**
* 一个类或接口可以用sealed修饰,表示这个类或接口只能由permits子句指定的子类或接口来继承或实现
*/
public sealed interface Animal permits Dog,Cat,Pig{
String eat();
}
实现该接口的类或子接口:
package com.test.demo.animal;
/**
* non-sealed表示这个子类没有密封限制,随便继承
*/
public non-sealed class Dog implements Animal{
@Override
public String eat() {
return "骨头";
}
}
package com.test.demo.animal;
public final class Cat implements Animal{
@Override
public String eat() {
return "鱼";
}
}
package com.test.demo.animal;
/**
* 接口Pig由sealed修饰,只能由permits指定的子类或接口来继承或实现
*/
public sealed interface Pig extends Animal permits Black {
String eat();
}
package com.test.demo.animal;
public final class Black implements Pig{
@Override
public String eat() {
return null;
}
}
410:删除实验AOT和JIT编译器
411:弃用待移除的安全管理器
412:外部函数和内存API(孵化器)孵化阶段
Java程序可以通过该API与Java运行时之外的代码和数据进行互操作。通过有效调用外部函数(即JVM之外的代码),以及安全地访问外部内存(即不由JVM管理的内存),API使Java程序能够调用本地库和处理本地数据,而没有JNI。
414:Vector API(第二孵化器)第二孵化阶段
引入一个API来表达向量计算,这些计算在运行时可靠地编译为支持的CPU架构上的最佳向量指令,从而实现优于等效标量计算的性能。
415:上下文特定的反序列化过滤器【重要】
简单来说,允许过滤器配置在反序列化期间通知反序列化期间允许或禁止的类。如果在反序列化过程中遇到禁止的类,反序列化将失败。
看个例子,假设Dog类中的Poc类是一个恶意构造的类,正常的反序列化是可以成功的。
public class TestDemo {
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()); //Dog{name='哈士奇'}
}
}
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 + '\'' + '}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Poc getPoc() {
return poc;
}
public void setPoc(Poc poc) {
this.poc = poc;
}
}
public class Poc implements Serializable {
}
假如我们不想让Poc对象反序列化成功,就可以使用过滤器来进行过滤。示例:
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.test.demo 类,允许 java.base 中的所有类,拒绝其他任何类
ObjectInputFilter filter = ObjectInputFilter.Config.createFilter(
"com.test.demo.Dog;java.base/*;!*");
objectInputStream.setObjectInputFilter(filter);
Object object = objectInputStream.readObject();
System.out.println(object.toString());
}
此时反序列化就会失败: