Java(八)JDK1.17新特性

  本系列文章:
    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());
    }

  此时反序列化就会失败:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值