Java 15新特性全面解析:2020年必知的Java语言更新与改进

按照每六个月发布一次的惯例,在2020年3月17日发布了Java 14之后,我们现在即将迎来2020年9月15日发布的Java 15,这是接下来的非LTS版本。

Java 15 新特性

以下是Java 15中的一些特性简要介绍:

  • 密封类(预览版)– JEP 360
  • instanceof的模式匹配(第二次预览)– JEP 375
  • 记录类(第二次预览)– JEP 359
  • 文本块(标准版)– JEP 378
  • 隐藏类 – JEP 371
  • 移除Nashorn JavaScript引擎 – JEP 372
  • 重新实现传统DatagramSocket API – JEP 373
  • 禁用和废弃偏向锁 – JEP 374
  • Shenandoah:低暂停时间垃圾收集器 – JEP 379
  • 移除Solaris和SPARC端口 – JEP 381
  • 外部内存访问API(第二次孵化)– JEP 383
  • 废弃RMI激活机制以便移除 – JEP 385

在Mac OS上安装Java 15

  • 要开始使用Java 15,请从此处下载JDK。
  • 将tar文件复制并解压到/Library/Java/JavaVirtualMachines目录,如下所示:
$ cd /Library/Java/JavaVirtualMachines

$ sudo cp ~/Downloads/openjdk-15_osx-x64_bin.tar.gz /Library/Java/JavaVirtualMachines

$ sudo tar xzf openjdk-15_osx-x64_bin.tar.gz

$ sudo rm openjdk-15_osx-x64_bin.tar.gz

完成后,使用任何文本编辑器打开bash_profile文件。我正在使用vim ~/.bash_profile命令。将Java 15的路径设置为JAVA_HOME,保存更改并执行source ~/.bash_profile命令以使更改生效。

export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk-15.jdk/Contents/Home

最后,你就可以使用Java 15编译和运行程序了。我们将使用JShell,一个交互式REPL命令行工具,快速测试新的Java 15特性。

请注意,Java 15中发布的许多功能处于预览状态。这意味着虽然它们目前完全可用,但以后可能会进行修改。一些功能可能会成为标准,或在下一个发布周期中被移除。为了测试预览功能,您需要在运行JShell或Java程序时显式设置–enable-preview,如下所示:

jshell --enable-preview

javac --release 15 --enable-preview Author.java

在接下来的几个部分中,让我们讨论一下Java 15中的重大语言变化。

1. 密封类(预览版)

Java 15的新特性:密封类与记录(第2部分)

自从 Kotlin 推出以来,密封类一直存在,并且 Java 15 最终引入了这一特性,以提供更好地对继承进行控制的功能。

正如其名,密封类可以让您限制或允许仅特定类型的类继承关系。

这对于模式匹配非常有用,因为您可以在一组特定的类别之间进行切换。

以下语法在Java 15中定义了一个密封类:

public sealed class Vehicle permits Car, Bike, Truck {
...
}

所以,上述代码意味着只有在关键字”permits”之后定义的类才可以扩展Vehicle密封类。

如果你已经在同一个文件中定义了Car、Bike和Truck这些类,你可以省略关键字permits,编译器会隐式地处理它,如下所示:

sealed class Vehicle {...}

final class Car extends Vehicle {...}
final class Bike extends Vehicle {...}
final class Truck extends Vehicle {...}

正如你在上面所看到的,我们已经定义了每个类的final修饰符。现在,这是一个关于密封类的重要规则,你需要记住:每个允许的类都必须设置一个明确的修饰符。它可以是final、sealed或者non-sealed之一。

以下是每个修饰词对继承产生的影响:

  • 声明为final的允许子类不能被进一步扩展。
  • 声明为sealed的允许子类可以被进一步扩展,但只能由该子类允许的类进行扩展。
  • 声明为non-sealed的允许子类可以被任何类进一步扩展。超类不能在此类层次结构中进一步限制子类。

在Java 15之前,开发人员只能使用final关键字或范围修饰符来控制继承。因此,密封类为Java开发人员在定义类层次结构时带来了额外的灵活性。

Java的反射API还提供了两种处理密封类的新方法:

java.lang.constant.ClassDesc[] getPermittedSubclasses();

boolean isSealed()

2. 记录(Records)(第二个预览)

在Java 14中,记录作为一项预览功能被引入,旨在减少在编写基于POJO的数据承载类时的样板代码。这在Kotlin中早就存在,以数据类的形式。

现在,Java 15 推出了记录(Records)的第二个预览版本。虽然没有进行大的改动(只是一些小的添加),但是有一些重要的澄清和限制需要您了解。

  • 在Java 15之前,可以在记录中声明本地方法(尽管这不是一个好主意)。现在,JEP明确禁止在记录中声明本地方法。可以理解的是,定义本地方法会通过引入外部状态依赖来削弱记录的独特卖点。
  • 与记录类的记录组件相对应的隐式声明字段是final的,现在不应该通过反射修改它们,因为这将抛出IllegalAccessException。

记录应该是数据载体类,你应该完全避免在其中定义本地方法。

封装类型的记录

(此处内容待续)

这是文章《Java 15的新特性》的第3部分(共4部分)。

我们知道记录是最终的,不能被扩展。幸运的是,记录可以实现接口。

所以您可以定义一个封闭接口,并以以下方式在您的记录中实现它们:

sealed interface 汽车 permits 宝马, 奥迪 { ... }

record 宝马(int 价格) implements 汽车 { ... }

record 奥迪(int 价格, String 型号) implements 汽车 { ... }

本地记录

在方法中,可以定义记录来存储中间值。与局部类不同,局部记录是隐式静态的。这意味着它们无法访问封装方法的变量和实例成员,这实际上是一个优点,因为它防止了记录捕获值。

对于之前必须创建辅助记录的Java开发人员来说,本地记录是一个巨大的便利。

通过引入本地记录,我们可以看到如何在流API中使用以下方法执行值的计算。

List<Merchant> findTopMerchants(List<Merchant> merchants, int month) {
    // 本地记录
    record MerchantSales(Merchant merchant, double sales) {}

    return merchants.stream()
        .map(merchant -> new MerchantSales(merchant, computeSales(merchant, month)))
        .sorted((m1, m2) -> Double.compare(m2.sales(), m1.sales()))
        .map(MerchantSales::merchant)
        .collect(toList());
}

结论

在Java 15中,上述两个是主要的语言特性。此外,我们还有第二个预览版的模式匹配供用户反馈,文本块现在已成为标准特性,还有一项重要的新特性——隐藏类。

隐藏类是一个与框架开发人员相关的JVM功能。它允许通过Lookup::defineHiddenClass定义类实现,使其无法被发现。通过这种方式,这些类既无法使用Class.forName找到,也无法通过字节码直接引用。

以下是Java 15引入的主要变更总结。

bannerAds