JDK17 新特性

目录

1.Switch表达式新增匹配模式

类型模式匹配

守卫模式

2.文本块

3.增强的伪随机数生成器

 4.密封类 sealed class

5.删除实验性的 AOT 和 JIT 编译器

6.弃用安全管理器和Applet API以进行删除

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

8.对 NullPointerExceptions的优化

9.增加Stream.toList()方法

10.本地变量类型推断

11.Records


1.Switch表达式新增匹配模式

使用 switch 表达式和语句的模式匹配以及对模式语言的扩展来增强 Java 编程语言。这个新特性允许使用新的模式,包括类型模式和守卫模式。类型模式能够在switch表达式中使用instanceof,守卫模式能够使用布尔表达式。

类型模式匹配

JDK16 instanceof 模式匹配

if (obj instanceof String s) {
   // 直接使用 s拼接字符串

   s += "heihei";
} else if (obj instanceof Integer i){
   // 直接使用i进行整型逻辑运算
   i += 1;
}

JDK17 switch 可直接用 instanceof 模式匹配选择(需要提前考虑 null 判断)

Object o;
switch (o) {
    case null      -> System.out.println("首先判断对象是否为空,走空指针逻辑等后续逻辑");
    case String s  -> System.out.println("判断是否为字符串,s:" + s);
    case record p  -> System.out.println("判断是否为Record类型: " + p.toString());
    case int[] arr -> System.out.println("判断是否为数组,展示int数组的长度" + ia.length);
    case Integer i -> System.out.println("判断是否为Intger对象,i:" + i);
    case Student s   -> System.out.println("判断是否为具体学生对象,student:" + s.toString());
    case UserCommonService -> System.out.println("判断是否为普通用户实现类,然后走普通用户逻辑");
    case UserVipService    -> System.out.println("判断是否为vip用户实现类,然后走vip用户逻辑");
    default   -> System.out.println("Something else");
}

守卫模式

Object obj = "test";
switch (obj) {
    case String s && s.length() > 0 -> s;
    default -> "";

}

将冒号(:)替换为箭头(->),并且switch表达式默认不会失败,所以不需要break。 

2.文本块

在Java17之前的版本里,如果我们需要定义一个字符串,比如一个JSON数据,基本都是如下方式定义:

public void lowVersion() {
    String text = "{\n" +
        "  \"name\": \"小黑说Java\",\n" +
        "  \"age\": 18,\n" +
        "  \"address\": \"北京市西城区\"\n" +
        "}";
    System.out.println(text);
}

这种方式定义具有几个问题:

  • 双引号需要进行转义;
  • 为了字符串的可读性需要通过+号连接;
  • 如果需要将JSON复制到代码中需要做大量的格式调整

通过Java 17中的文本块语法,类似的字符串处理则会方便很多;通过三个双引号可以定义一个文本块,并且结束的三个双引号不能和开始的在同一行。 

private void highVersion() {
    String text = """
            {
              "name": "小黑说Java",
              "age": 18,
              "address": "北京市西城区"
            }
            """;
    System.out.println(text);
}

3.增强的伪随机数生成器

JDK 17 之前,我们可以借助 RandomThreadLocalRandomSplittableRandom来生成随机数。不过,这 3 个类都各有缺陷,且缺少常见的伪随机算法支持。

伪随机数生成器(pseudorandom number generator,PRNG),又称为确定性随机位生成器(deterministic random bit generator,DRBG),是用来生成接近于绝对随机数序列的数字序列的算法。一般来说,PRNG 会依赖于一个初始值,也称为种子,来生成对应的伪随机数序列。只要种子确定了,PRNG 所生成的随机数就是完全确定的,因此其生成的随机数序列并不是真正随机的。

就目前而言,PRNG 在众多应用都发挥着重要的作用,比如模拟(蒙特卡洛方法),电子竞技,密码应用。

//通过 RandomGeneratorFactory.of(“随机数生成算法”) 方法获得生成器
RandomGeneratorFactory l128X256MixRandom = RandomGeneratorFactory.of("L128X256MixRandom");
// 使用时间戳作为随机数种子
RandomGenerator randomGenerator = l128X256MixRandom.create(System.currentTimeMillis());
// 生成0-9随机数
System.out.println(randomGenerator.nextInt(10));

 4.密封类 sealed class

限制哪些其他类或接口可以扩展或实现它们。该提案的目标包括允许类或接口的作者控制由哪些代码负责实现它,提供一种比访问修饰符更具声明性的方式来限制超类的使用,并通过为模式的详尽分析提供基础来支持模式匹配的未来方向。

public abstract sealed class Furit permits Apple,Pear {
}
public non-sealed class Apple extends Furit {
}
public final class Pear extends Furit {

}

在定义Furit时通过关键字sealed声明为密封类,通过permits可以指定Apple,Pear类可以进行继承扩展。

指定的类permits必须位于超类附近:在同一个模块中(如果超类在命名模块中)或在同一个包中(如果超类在未命名模块中)。

指定的类permits必须具有规范名称,否则会报告编译时错误。这意味着匿名类和本地类不能成为密封类的子类型。

密封类对其允许的子类施加三个约束:

  1.     密封类及其允许的子类必须属于同一个模块,并且如果在未命名的模块中声明,则必须属于同一个包。
  2. 每个允许的子类都必须直接扩展密封类。
  3.  每个允许的子类都必须使用修饰符来描述它如何传播由其超类发起的密封:
    1.  可以声明允许的子类final以防止其在类层次结构中的一部分被进一步扩 展。(记录类是隐式声明的final。)
    2. 可以声明允许的子类sealed以允许其层次结构的一部分比其密封的超类所设想的扩展得更远,但以受限制的方式。
    3.  可以声明一个允许的子类non-sealed,以便它的层次结构部分恢复为对未知子类的扩展开放。密封类不能阻止其允许的子类这样做。(修饰符non-sealed是 为 Java 提出的第一个连字符关键字。)

5.删除实验性的 AOT 和 JIT 编译器

Java 17,删除实验性的提前 (AOT) 和即时 (JIT) 编译器,因为该编译器自推出以来很少使用,维护它所需的工作量很大。保留实验性的 Java 级 JVM 编译器接口 (JVMCI),以便开发人员可以继续使用外部构建的编译器版本进行 JIT 编译。

6.弃用安全管理器和Applet API以进行删除

安全管理器可追溯到 Java 1.0,多年来,它一直不是保护客户端 Java 代码的主要方法,也很少用于保护服务器端代码。为了推动 Java 向前发展,Java 17 弃用安全管理器,以便与旧版 Applet API ( JEP 398open in new window ) 一起移除。

Applet API 用于编写在 Web 浏览器端运行的 Java 小程序,很多年前就已经被淘汰了,已经没有理由使用了。

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

允许应用程序使用 JVM 范围的过滤器工厂配置特定于上下文和动态选择的反序列化过滤器,该工厂用于为每个反序列化操作选择一个过滤器。

  • 不可信数据的反序列化是一项具有内在风险的操作,因为在许多情况下传入数据流的内容是通过未知或未经身份验证的客户端获取的。

  • 防止序列化攻击的关键是禁止任意类的实例被反序列化,从而直接或间接地阻止其方法的执行。

  • 攻击者可以通过仔细构造流来运行任何恶意的类中的代码。如果对象构造涉及更改状态或触发其他操作的副作用,则应用程序对象、库对象和 Java 运行时的完整性可能会受到损害。

8.对 NullPointerExceptions的优化

之前输出将显示NullPointerException发生的行号,但不知道哪个方法调用时产生的null,必须通过调试的方式找到。

Exception in thread "main" java.lang.NullPointerException
        at com.heiz.java17.HelpfulNullPointerExceptionsDemo.main(HelpfulNullPointerExceptionsDemo.java:13)

在Java 17中,则会准确显示发生NPE的精确位置。

Exception in thread "main" java.lang.NullPointerException: Cannot invoke "com.heiz.java17.Address.getCity()" because the return value of "com.heiz.java17.Person.getAddress()" is null
		at com.heiz.java17.HelpfulNullPointerExceptionsDemo.main(HelpfulNullPointerExceptionsDemo.java:13)

9.增加Stream.toList()方法

如果需要将Stream转换成List,需要通过调用collect方法使用Collectors.toList(),代码非常冗长。

private static void oldStyle() {
    Stream stringStream = Stream.of("a", "b", "c");
    List stringList =  stringStream.collect(Collectors.toList());
    for(String s : stringList) {
        System.out.println(s);
    }
}

在Java 17中将会变得简单,可以直接调用toList()

private static void streamToList() {
    Stream stringStream = Stream.of("a", "b", "c");
    List stringList =  stringStream.toList();
    for(String s : stringList) {
        System.out.println(s);
    }
}

10.本地变量类型推断

之前我们想定义定义局部变量时。我们需要在赋值的左侧提供显式类型,并在赋值的右边提供实现类型:

MyObject value = new MyObject();

 在Java 10中,提供了本地变量类型推断的功能,可以通过var声明变量:

var value = new MyObject();

11.Records

Java 14 中便包含了一个新特性:EP 359: Records

Records的目标是扩展Java语言语法,Records为声明类提供了一种紧凑的语法,用于创建一种类中是“字段,只是字段,除了字段什么都没有”的类。

通过对类做这样的声明,编译器可以通过自动创建所有方法并让所有字段参与hashCode()等方法。

record Person (String firstName, String lastName) {}

record 解决了使用类作为数据包装器的一个常见问题。纯数据类从几行代码显著地简化为一行代码。

你可能感兴趣的:(java,开发语言)