Java 15 有什么新功能

2024年9月10日 | 阅读 12 分钟

Java 15 或 JDK 15 是 Java SE 平台第 15 个版本的参考实现。 它作为一个重要的特性发布,为 Java 17 奠定了基础。

Java 15 提供了许多令人兴奋的新特性、孵化特性和预览特性,这些特性是运行 Java 程序的基础,对于 JDK(Java Development Kit)来说非常重要。

本文将讨论 Java 15 的背景、发布日期、特性、重要性、预测和采用情况。

JDK 平台中的这个短期发布版本是 Java 15,它于 **2020 年 9 月** 正式可用。它引入了一些新改进,同时也增强了之前版本中的一些特性。

Java 15 的发布

Java 15 于 **2020 年 9 月 15 日** 发布,遵循惯例的六个月发布周期。这正好是 Java 14(于 **2020 年 3 月 17 日** 发布)发布后的六个月。

Java 15 或 JDK 15 是 Java SE 平台第 15 个版本的主要实现来源。

Java 15 主要包含 15 项重大改进,其中包括 **记录(Records)及其预览特性,密封类(Sealed Classes)及其预览特性,隐藏类(Hidden Classes)和文本块(Text Blocks),以及第二个用于外部内存访问 API 的孵化器。**

对于需要低延迟垃圾收集器的生产系统,将 ZGC 和 Shenandoah GC 从实验性推广到生产环境也是一个重大的提升。

Java 15 的通用发布日期定在 2020 年 9 月 15 日。发布时间表中的关键日期如下所示:

  1. 2020 年 11 月 6 日:Java 15 Ramp down Phase One 发布,从主线分支提取。
  2. 2020 年 7 月 16 日:Java 15 Ramp down Phase Two 发布日期。
  3. 2020 年 8 月 6 日:Java 15 初始发布候选版发布日期。
  4. 2020 年 8 月 20 日:Java 15 最终候选版发布日期。
  5. 2020 年 9 月 15 日:Java 15 通用可用版发布日期。

Java EOL(生命周期结束)

Java 15 EOL OpenJDK 15 是一个非 LTS(长期支持)版本,在 LTS 于 2021 年 3 月到期后,它将继续获得为期六个月的支持。

如果社区同意更改其现有的默认支持周期,这些计划可能会受到影响。当然,Azul 等公司可能会继续为非 LTS 版本的 OpenJDK 提供 LTS 服务并收费。

Java 15 的特性

Java 15 的特性包括对 Java 的重大新增和删除,以及 Project Amber 中描述的模式匹配功能的持续可用性。此外,Nashorn JavaScript 引擎已被弃用。

JEP 358 将默认启用 Null Pointer Exception(而不是要求手动启用)。下面是 JEP 编号及其对应的 Java 15 特性列表:

  • JEP 378: 文本块(标准)
  • JEP 371: 隐藏类 -
  • JEP 360: 密封类(预览)-
  • JEP 375: 实例模式匹配(第二次预览)
  • JEP 384: 记录(第二次预览)
  • JEP 372: 禁用 Nashorn JavaScript 引擎
  • JEP 373: 重写旧的 DatagramSocket API
  • JEP 374: 禁用并弃用偏向锁定
  • JEP 379: 低延迟垃圾收集器:Shenandoah
  • JEP 383: 外部内存访问 API(第二个孵化器)-
  • JEP 381: 移除 Solaris 和 SPARC 端口
  • JEP 385: 弃用 RMI 激活以供移除

密封类的预览引入可以说是 JDK 15 中最大的新创新,它有助于开发人员精确地定义可能的子类型,同时为更多的模式匹配进步奠定基础。

让我们详细讨论每个特性

1. JEP 360- 密封类(预览)

如前所述,密封类提供了一种更精确的方法来声明超类中的可能子类。本质上,密封类有助于最小化不必要的扩展,同时促进对超类的访问。

只有具有适当权限的类和接口才能扩展或实现密封类或接口。

Kotlin 已经有了密封类特性一段时间了,Java 15 现在提供了这种功能,以便更精细地控制继承。

顾名思义,密封类允许您仅限于特定类型的类层次结构。

鉴于您只有有限数量的类可以在它们之间切换,这对于模式匹配来说非常有帮助。

Java 目前不提供精细粒度的继承控制。访问修饰符,如 public、protected 和 private,以及默认的包私有,提供了非常粗粒度的控制。

为此,密封类允许类指定哪些类型可以用作子类型。这同样适用于可以实现接口的类型的分类。

扩展密封类与我们在 Java 中使用的 `extends` 关键字相同。

因此,我们可以这样写:





如果任何类扩展了密封类,它本身就必须使用 `sealed`、`non-sealed` 或 `final` 关键字进行声明。通过声明所有这些,编译器将确保类层次结构保持不变。

使用密封类的主要优点是有限且详尽的层次结构。让我们将所有这些情况作为操作或示例进行检查:

没有密封类,编译器无法公平地推断我们的 if-else 表达式涵盖了所有可能的子类。如果没有最后的 `otherwise` 子句,编译器很可能会警告我们,我们的逻辑没有考虑所有场景。

在下面的示例中,您不需要使用关键字,因为它允许编译器隐式处理...因为您将 Plain、Tractor 和 Train 类声明在与 Driver 相同的​​文件中。




如上所示,我们已经指定了每个类的最终修改。这里有一个您必须记住的关键密封类规则:每个允许的类都必须明确设置修饰符。可以是 final、sealed 或 unsealed。

每个修饰符对继承的影响如下:

  • 当一个授权子类被标记为 **final** 时,它不能被进一步扩展。
  • 一个被声明为 **sealed** 的授权子类只能被该子类允许的类进行扩展。
  • 任何类都可以进一步扩展授权子类,并且可以将其指定为 **non-sealed**。下面的类层次结构中的子类不能受到超类的限制。

让我们以一个简单的计算器为例,它还可以让我们执行加法、减法、乘法和除法等数学运算。使用密封类,我们还可以采用 BinaryExpression 接口,该接口帮助云开发人员并明确允许这四种实现。

如果您确切地知道哪些子类型存在,那么就不需要 `catch-all else` 块或 `switch` 中的 `default` 部分。后者对于将来的模式匹配至关重要,因为对于密封类,javac 编译器可以确定 switch 是否详尽。类似地,当将数据序列化为带有模式的格式(如 xml)时,您就知道您的格式支持所有可能的子类型。

2. 记录(第二次预览)

为了减少创建基于 POJO 的数据载体类时的样板代码,记录作为预览功能在 Java 14 中被引入。Kotlin 中长期存在的数据类已经具备此功能。

Java 15 时,记录迎来第二次预览。尽管没有重大变化,但有几项小的补充和几项重要的澄清和限制,您应该了解。

  • 在 Java 15 之前,可以在记录中声明本地方法。然而,这并不明智。JEP 现在明确禁止在记录中声明本地方法。可以理解的是,通过引入本地函数来添加外部状态要求会削弱记录的独特卖点。
  • 目前不建议通过反射修改记录类对应的隐式声明字段,因为这样做会导致 `IllegalAccessException`。

由于它们被设计为数据载体类,因此您必须绝对避免在记录中定义本地方法。

根据 JEP 384,Java 记录特性现已进入第二次预览阶段。记录提供了一种比 JEP 359 最初提出的更简单、更易于理解的数据聚合类构建方法。记录特性的主要优点之一是它使评估样板代码的含义和意图变得不必要。

构造函数禁止使用 `this`,这强制执行了记录紧凑构造函数 `Object() { [native code] }` 的“自动初始化”惯用法。此外,JEP 384 为 Java 用户提供了更长的审查期。

无记录

需要注意的是,生成一个仅用于存储状态的不变对象需要大量的代码。我们有一个单独的 all-arguments 构造函数 `Object() { [native code] }`,所有字段都有一个 final 构造函数 `Object() { [native code] }`,以及每个字段的访问器函数。在极端情况下,我们甚至可以指定类为 final 以禁止子类化。

为了生成有用的日志输出,我们通常会更进一步,覆盖 `toString() { [native code] }` 方法。在比较这些对象的两个实例时,我们也可能需要覆盖 `equals` 和 `hashCode` 方法以防止出现不良结果。

有记录

通过使用新的记录类,我们可以以一种更简洁的方式定义相同的不可变数据对象。

这里发生了一些事件。类定义包含一种针对记录量身定制的新语法。我们在标题中提供构成记录的字段信息。

编译器可以从此标题推断内部字段。这意味着,由于它们默认已存在,因此我们无需创建任何显式成员变量或访问器。我们也不需要提供构造函数 `Object() { [native code] }`。

编译器还会为 `toString() { [native code] }`、`equals` 和 `hashCode` 方法提供合理的实现。

虽然记录消除了大量样板代码,但它们确实允许我们修改一些默认行为。例如,我们可以创建一个规范构造函数 `Object() { [native code] }` 来执行一些验证。

需要注意的是,记录有一些限制。它们不能声明为 abstract,不能使用本地方法,并且始终是 final。

本地记录

为了存储中间值,记录也可以在过程中定义。本地记录隐式是 static 的,与本地类不同。这可以防止记录捕获值,这意味着它们无法访问外部方法的变量和实例成员,这实际上非常棒。

对于以前不得不编写辅助记录的 Java 开发人员来说,现在可以使用本地记录了。

3. 隐藏类

Java 15 将包含一个名为隐藏类的新特性。它们不会直接帮助大多数开发人员,但对于任何从事动态字节码或 JVM 语言的人来说,它们可能会很有帮助。

隐藏类的目的是能够在运行时构建不可发现的类。 因此,其他类或反射都无法链接到它们或暴露它们。隐藏类被设计成在加载和卸载方面都很高效,因为这些类通常具有较短的生命周期。

应注意的是,最近的 Java 版本确实允许构造与隐藏类相似的匿名类。然而,它们依赖于 `Unsafe API`。隐藏类则没有这种依赖。

对于 Java 15,普通 Java 开发人员不会直接接触隐藏类,但任何使用代理或其他运行时动态生成的内容的人可能会使用它们。隐藏类的主要目的是生成“其他类的字节码无法直接使用的类”。

以 JVM 匿名类的形式,隐藏类的概念已经存在(不要与源代码中的匿名内部类混淆)。以前定义这些类需要使用 `Unsafe`,但这个 JEP 增加了一个支持 API 来实现这一点。

4. 文本块

在 Java 14 进行了第二次预览并添加了两个新的转义序列后,文本块现在计划作为一项完整功能包含在 Java 15 中。如果您没有关注原始字符串字面量的进展,JEP 326 最初设想文本块为 Java 12 中的原始字符串字面量。原始字符串字面量后来被删除,并作为一项更发达的功能(JEP 355:文本块(预览))重新引入。作为一项功能完善的功能,文本块现在将为开发人员提供一种可预测地格式化多行字符串字面量的方法,同时考虑避免大多数转义序列。如果您想了解有关文本块及其所有变体的更多信息,我们关于文本块的博客值得一读。

5. 外部内存 API

外部内存访问已经是 Java 14 的一项正在开发中的特性。Java 15 的目标是在引入一些新特性的同时,保持其孵化状态。

  • 为了定制内存访问 `VarHandle`,开发了一个新的 `VarHandle API`。
  • 使用 `Spliterator` 接口支持内存段的并发处理。
  • 改进了对映射内存段的支持。
  • 修改和取消引用来自本机调用等源地址的能力。

通常,位于托管 JVM 堆之外的内存称为“外部内存”。因此,它不受垃圾回收的影响,并且通常管理非常大的内存段。

大多数开发人员不会直接受到这些新 API 的影响,但处理外部内存的第三方库将从中受益匪浅。分布式缓存、反规范化文档存储、大型任意字节缓冲区、内存映射文件等都属于此类。

6. 移除 Nashorn JavaScript 引擎

为了支持 JVM 中的更多动态语言,Nashorn JavaScript 引擎作为 Rhino 脚本引擎的替代方案被添加到 JDK 8 中。然而,自 GraalVM 推出以来,该领域已成为多语言支持的焦点。尽管 Nashorn 最初计划在 Java 11 中移除,但它一直保留在 JDK 中,直到 JDK 14。此更改没有太大影响,但很重要,因为它标志着一次分离。

结论

在重要特性方面,Java 15 是一个相当大的版本。由于许多创新得到了知名 Java 开发者的支持,我们预计其中许多预览和孵化特性将在 Java 17 之前成熟为完整特性。

然而,我们认为 Java 15 在采用率方面不会取得巨大成功。对于大多数团队来说,仅仅为了六个月后迁移而使用这些特性是不值得花费时间和金钱的。真正的问题是,这些变化将在多大程度上影响 Java 17 作为下一个 LTS 版本的采用。

我们可以期待 Java 16 的发布,紧随其后的是 Java 17 的新长期支持版本。


下一主题Java 中的 ART