Java 17 有什么新功能

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

Java 17 于2021 年 9 月发布,取代 Java 11 成为最新的 LTS(长期支持)版本。目前最关键的问题是“Java 17 有哪些新特性?

Java 17 中包含 JDK (14) Enhancement Proposals (JEP) 项目。其中十项是新功能,两项被移除,两项被弃用。

在本文中,我们将回顾 Java 17 中影响应用程序开发者的主要更新。我们还将包含一个已弃用和已移除功能的列表。

最新的标准 Java 长期支持版本包含始终严格的浮点数语义、外部函数和内存 API、一个用于伪随机数生成器的统一 API,以及许多其他功能。

Java 17 是一个新的长期支持 (LTS) 标准 Java 版本,目前已在生产环境中使用。此外,Oracle 表示,LTS 版本(至少提供八年产品支持)将不再每三年发布一次,而是改为每两年发布一次。Oracle 为非 LTS 版本提供六个月的支持。

即将发布的新标准 Java 版本中,有两项新功能:switch 语句的模式匹配预览支持上下文相关的反序列化过滤器,这两项提高了安全性。JDK 17 包含了自三年前发布的 JDK 11 LTS 版本以来添加的所有内容。

据 Oracle Java 平台集团副总裁 Georges Saab 称,更频繁的 LTS 版本将使那些只想使用 LTS 版本的企业能够更快地获得新功能。2023 年,Java 21 将是下一个长期支持版本。对于 JDK 17,Oracle 将允许在生产环境中使用 Oracle JDK 二进制文件不受限制地使用三年,比下一个 LTS 晚一年。但其中不包含企业生产支持的订阅。

来自应用程序监控公司 New Relic 的客户群数据显示,数千万个生产环境的 JVM 几乎普遍部署了 LTS 版本。根据 New Relic 的数据,几乎所有客户都在使用最近的两个 LTS 版本 JDK 11 或 JDK 8。根据 New Relic 的数据,90% 的用户使用 JDK 11,10% 使用 JDK 8。

然而,Oracle 声称六个月发布版本的下载量一直在稳步增长。尽管企业倾向于部署 LTS 版本,但开发者喜欢尝试六个月的版本。

恢复始终严格的浮点数语义

此 JEP 使浮点运算统一严格,主要面向科学应用。Strict 或 StrictFP 是标准的浮点运算,它们都确保在任何平台上使用浮点数进行的计算都会产生相同的结果。

Java 1.1 及更早版本中的默认行为是 strictfp。然而,由于硬件问题,架构师进行了更改,并且需要 strictfp 术语才能再次启用此类行为。因此,这个关键字不再是必需的。

新的 macOS 渲染管道

由于 Apple 在 macOS 10.14 中弃用了 Swing GUI 内部使用的 OpenGL API,此 JEP 为该操作系统实现了 Java 2D 内部渲染管道。除了底层引擎外,旧 API 在新版本中没有改变,新版本使用 Apple Metal API。

增强的伪随机数生成器

JEP 356 为伪随机数生成器(也与更具体的用例相关 (PRNG))提供了新的接口和实现。

因此,更易于互换使用多种方法,并且它还为使用数据流的编程提供了更大的支持。

从上面的例子可以看出,旧的随机类(如 Java.util.Random 和 SplittableRandom 以及 SecureRandom)已包含在新版本中,并扩展到了一个名为 *RandomGenerator* 接口的新接口。

新的 macOS 渲染管道

由于 Apple 在 macOS 10.14 中弃用了 Swing GUI 内部使用的 OpenGL API,此 JEP 为 macOS 实现了 Java 2D 内部渲染管道。除了底层引擎外,由于新实现使用 Apple Metal API,因此对现有 API 没有进行任何修改。

macOS/AArch64 移植

Apple 宣布将从 X64 转向 AArch64 架构的计算机产品线。根据此 JEP,JDK 现在可以在运行 macOS 的 AArch64 架构平台上运行。

弃用 Applet API 以便移除

尽管许多 Web 浏览器已停止支持 Java 插件,但这可能会让许多以 Applet API 开始开发生涯的 Java 开发者感到失望。尽管自 9 版本以来它已被标记为弃用,但此版本已将 API 标记为移除,因为它不再是必需的。

强封装 JDK 内部 API

由于 JEP 403 删除了 -illegal-access 标志,这是向强封装 JDK 内部 API 迈出的又一步。如果存在该标志,控制台将发送一条消息,告知平台该标志已被禁用。在这种情况下,平台将忽略该标志。

此功能将使 JDK 用户无法访问 sun.misc.Unsafe 等关键内部 API 以外的内部 API。

上下文相关的反序列化过滤器

通过使用一个 JVM 范围的过滤器工厂,该工厂用于为每个序列化操作选择一个过滤器,上下文相关的反序列化过滤器允许应用程序设置上下文相关的、动态选择的反序列化过滤器。Oracle 表示,提出此建议的原因是反序列化不受信任数据本身就有害,因为传入数据流的内容决定了创建的对象、它们的字段值以及它们之间的关系。流中的字节通常来自未知、不可信或未经身份验证的客户端。如果流构造得当,攻击者可以出于恶意目的执行任何类的代码。

应用程序对象、库对象和 Java 运行时的安全性都可能面临风险,如果对象创建产生了意外的后果,从而改变了状态或触发了其他操作。防止反序列化任何类的实例,从而防止直接或间接执行其方法,是击败序列化攻击的关键。通过在 Java 9 中引入反序列化过滤器,应用程序和库代码现在可以在反序列化之前评估传入的数据流。当创建反序列化流时,此代码提供验证逻辑作为 *java.io.ObjectInputFilter*。但是,依赖流创建者来特别请求验证存在缺点。

通过提供一个可以通过 API、系统属性或安全属性进行调整的 JVM 范围的反序列化过滤器,JDK 增强提案 290 试图解决这些限制。然而,这种方法并非没有缺点,特别是对于复杂的应用程序。更好的策略是设置每个流的过滤器,这样它们就不需要每个流开发者的参与。预期的改进应该使开发者更容易为每个反序列化上下文和用例创建和使用适当的过滤器。

对于简单的情况,过滤器工厂可以提供一个应用程序范围的固定过滤器。以下是一个接受示例类和 Java.base 模块中的类的类的过滤器,同时拒绝所有其他类的示例。

我们的 ObjectInputFilter 定义了两个方法。用于设置和检索的 JVM 范围过滤器工厂配置类。名为过滤器工厂的函数返回一个过滤器,它接受两个参数:当前过滤器和下一个过滤器。

移除实验性的 AOT 和 JIT 编译器

GraalVM 的 Ahead-Of-Time (AOT) 编译 (JEP 295) 和 Just-In-Time (JIT) 编译器 (JEP 317),分别在 JDK 9 和 JDK 10 中作为实验性功能引入,维护成本很高。

然而,它们并没有得到广泛采用。因此,此 JEP 将它们从平台中移除,但开发者仍然可以使用 GraalVM 来利用它们。

密封类和接口

尽管在 JDK 15 和 16 版本中以预览模式提供,密封类(Project Amber 的一部分)现在通过此 JEP 正式引入。

该功能限制了密封组件被其他类或接口扩展或实现的能力。将 JEP 406 与另一项模式匹配的进步相结合,将提供更复杂、更干净的类型、转换和操作代码模式的检查。

让我们通过一个操作示例来看看它的工作原理。

可以扩展或实现密封类和接口的类和接口的类型受到限制。该提案的目标包括允许类或接口的作者控制哪个代码负责实现它,提供一种比访问修饰符更具声明性的方法来限制超类的使用,并通过为模式的彻底分析奠定基础来支持模式匹配的未来方向。此功能有助于 API 设计者创建更健壮的编程。

JEP 409 的密封类和接口限制了哪些其他类或接口可以扩展或实现它们。

注意:Java 15 提供了对密封类和接口的预览。

使用 *permit* 关键字指定可以扩展或实现密封类的类和接口。

扩展密封类的能力仅限于密封、最终和非密封类。

密封类可以防止不必要的类层次结构。但这并不是唯一的驱动因素。您可以为密封类提供一个“逃生舱口”用于扩展。这就是引入开放类(open classes)的原因。

在上述代码片段中,Shape 是一个密封类。此类可以由以下类扩展:scalentriangle、pyramid、circle、rectangle 和 square。

该代码将 Square 类指定为非密封类,将 Circle、Scalentriangle 和 Pyramid 类指定为最终类。

密封的 Rectangle 类可以允许其他类似的类。

TransparentRectangle 和 FilledRectangle 是上面代码片段中的最后两个类。这两个类都扩展了密封的 Rectangle 类。

还提供了一个非密封的 Square 类。这里的“逃生舱口”意味着无限制的子类。

值得注意的是,使用密封类有一个限制,即所有允许的子类必须是同一模块的成员。如果它声明在一个未命名的模块中,它应该在同一个包中。

模式匹配 switch

switch 语句的模式匹配预览允许测试 switch 表达式和语句是否与各种模式匹配,每种模式都有不同的操作。这使得能够清晰、安全地描述面向数据的复杂查询。此功能旨在引入两种类型的模式:带保护的模式,允许使用任意布尔表达式来细化模式匹配逻辑;以及带括号的模式,用于清除一些解析歧义。它还允许模式出现在 case 标签中,并在需要时放宽 switch 历史上的 null 敏感性。

switch 表达式和语句的模式匹配增强是模式匹配的又一步。语言变得更具表现力,并且需要更少的样板代码来指定这些表达式。

Switch 表达式在 Java 12 中作为预览功能提供。Java 14 后来将 switch 表达式进行了编码。Switch 表达式可以用作任何其他表达式一样,并求值为单个值。不再需要 break 语句来避免贯穿,因为 switch 表达式支持“箭头 case”标签。

可以使用模式匹配来测试 switch 表达式是否与各种模式匹配,每种模式都有不同的操作。使用模式匹配可以简洁安全地表达面向数据的复杂查询。

上述代码片段直接返回该值,而不是将其保存在变量中。此外,代码示例中没有 break 语句来避免贯穿。Break 语句容易出错且容易被忽略,因此这一更改非常必要。

macOS 的渲染管道

Java 2D API 用于 Swing API 的渲染。在 Java 17 之前,OpenGL API 用于 macOS 的渲染。在 Java 17 中,您可以使用全新的 Apple Metal 加速渲染 API 进行 macOS 渲染。

目前,默认情况下它是禁用的。因此,渲染继续使用 OpenGL API,尽管 Apple 已弃用,但它们仍然可用。

将此系统属性设置为 true 以激活 Metal。

-Dsun.java2d.metal=true

您的代码作为编码器不会受到影响。这是因为 OpenGL 或 Metal 对应用程序来说是透明的。Java API 不受影响,因为内部实现只有一个区别。

Metal 管道需要 macOS 10.14.x 或更高版本。对于更早的操作系统版本,将不会配置管道。

移除实验性的 AOT 和 JIT 编译器

GraalVM 的 Ahead-Of-Time (AOT) 编译 (JEP 295) 和 Just-In-Time (JIT) 编译器 (JEP 317),分别在 JDK 9 和 JDK 10 中作为实验性功能引入,维护成本很高。

然而,它们没有得到任何显著的采用。因此,此 JEP 将它们从平台中移除。但是,开发者仍然可以通过使用 GraalVM 来利用它们。

弃用 Security Manager 以便移除

另一个被标记为移除的特性是因为它不再是必需的,那就是 Security Manager,它最初是为了保护客户端 Java 程序而创建的。

强封装

JDK 内部 API 被强力封装,除了 sun.misc.Unsafe 等关键内部 API 外,不允许通过一个命令行选项放松内部组件的强力封装,这在 JDK 9 到 JDK 16 中是允许的。该计划旨在使 JDK 更安全、更易于维护,同时鼓励开发者从内部组件转向标准 API。

增强的伪随机数生成器

伪随机数生成器 (PRNGs) 得到增强,提供新的接口类型和实现,例如可跳转的 PRNGs 和另一类可拆分的 PRNG 算法 (LXM)。所有当前和未来的 PRNGs 将通过一个名为 RandomGenerator 的新接口提供标准化的 API。将有四个独特的 RandomGenerator 接口可用。Java 伪随机数生成领域中许多发展领域的重点激发了这一想法。在此项工作中,无需为许多不同的 PRNG 算法提供实现。但是,已添加了三种在其他编程语言环境中常用的典型算法。

  • 使应用程序更容易互换使用不同的 PRNG 算法是该计划的目标之一。
  • 提供了 PRNG 对象流,增强了对基于流的编程的支持。
  • 移除当前 PRNG 类中的冗余代码。
  • 保留 java.util.current Random 类的行为。

访问大图标

Swing 库现在可以通过 Java 17 的新 API 访问大图标。

JDK 17 添加了一个新方法:javax.swing.filechooser.FileSystemView.getSystemIcon(File, int, int)。此方法提供了访问更好看的图标。

Windows 平台有完整的实现。然而,其他平台的结果可能有所不同,并且稍后将会有改进。

这是一个例子

获取应用程序可执行文件 (exe) 更好质量图标的能力是此功能的一个常见用例。此图标适用于设计一个在高 DPI 设置下可以更有效地缩放的标签。

LTS 定义

变化不仅限于代码,流程也在演变。

众所周知,Java 平台版本发布周期漫长且不准确。尽管最初计划每三年发布一次,但实际过程经常需要四年才能完成。负责平台演进的团队还决定调整发布周期,以适应当今创新和快速响应至关重要的市场动态。

因此,自 Java 10(2018 年 3 月 20 日发布)以来,已经实施了新的六个月功能发布模型。

六个月功能发布模型

新的六个月功能发布策略使平台开发人员可以自由地在功能准备好时发布它们。因此,不再有将功能仓促推向发布的紧迫性。否则,他们将不得不等待三到四年才能使该功能可供平台用户使用。

新范例还改善了用户与平台架构师之间的反馈循环。这是因为功能可以在孵化阶段可用,并在多次交互后才变得普遍可用。

LTS 模型

Java 经常用于企业应用程序,因此稳定性很重要。此外,维护所有这些版本的支持并提供补丁更新成本很高。

因此,开发了长期支持 (LTS) 版本。这为用户提供了更多支持。通过错误修复、性能改进和安全补丁,这些版本最终会变得更可靠、更安全。对于 Oracle 而言,此支持通常持续八年。

Java SE 11(2018 年 9 月发布)和 Java SE 17 是修改发布模型后的 LTS 版本(2021 年 9 月发布)。然而,模型在 17 版本中增加了一些新东西。换句话说,LTS 版本之间的间隔现在是两年而不是三年,使 Java 21(计划于 2023 年 9 月发布)成为下一个 LTS 的可能候选。

同样重要的是要注意,这个发布模型并不是全新的。它明显地借鉴并修改自其他已经证明了其有效性的项目模型,例如 Mozilla Firefox、Ubuntu 等。

外部函数和内存 API

Java 程序员可以访问 JVM 外部的代码并控制堆外内存,这得益于外部函数和内存 API。其目的是取代 JNI API,并提高其性能和安全性。

此 API 是 Panama 项目创建的另一个功能,JEPs 393-389-383-370 演进并取代了它。

我们想要调用该 API 的目标库必须首先被加载。

然后必须指定目标方法的签名,然后我们才能最终调用它。

Java 程序可以通过外部函数和内存 API 与 Java 运行时之外的代码和数据进行交互,该 API 在 Java 仍处于孵化阶段时引入。该 API 允许 Java 程序调用原生库并处理原生数据,而没有 JNI 的脆弱性和风险,通过有效地执行外部函数(即 JVM 外部的代码)并安全地访问外部内存(即 JVM 未维护的内存(Java Native Interface))。所提出的 API 是开发另外两个 API:外部内存访问 API 和外部链接器 API。

外部内存访问 API 设计为 Java 14 的孵化 API(2019 年),并在 Java 15 和 Java 16 中重新孵化。外部链接器 API 旨在成为 Java 16(2020 年底)的开发 API。API 计划旨在实现性能、通用性、可用性和安全性。

向量 API

平台中立的向量 API,作为 JDK 16 中的孵化 API 实现,并在 JDK 17 中重新孵化,提供了一种定义向量计算的方法,该计算将在运行时持续编译到支持的 CPU 架构上的最佳向量指令。与可比的标量计算相比,这提高了性能。JDK 17 包含对向量 API 的速度和实现改进,以及将字节向量转换为布尔数组和从中转换的改进。

向量 API 处理的是 SIMD(单指令多数据)过程,涉及同时执行多个指令集。它利用专门的 CPU 硬件,该硬件允许将此类指令作为管道执行并支持向量指令。

通过利用底层硬件的能力,新 API 将允许开发人员设计更有效的代码。

需要对许多独立操作数应用操作的应用程序通常包括图像处理、字符处理、密集算术应用和科学代数线性应用。

已移除的功能包括:

以下是两个对应用程序开发者很重要的已移除的关键方面:

  • 强封装 JDK 内部 API:JDK 具有不供外部使用的内部 API。然而,随着时间的推移,JDK 的底层组件已被各种库、框架、工具和软件程序的创建者使用。这可能会危及安全性与可维护性。这些 API 中的一个是:
  1. Java.* 包的一些私有类、方法和字段。
  2. 所有 sun.* 包类型的字段、类和方法。
  • 大多数 org.*、jdk.* 和 com.sun.* 包的类、方法和字段。
  • RMI 激活不再是 RMI 的组件:自 Java 8 起,它就是可选功能。这是因为 Web 技术目前支撑着分布式系统。有证据表明,目前只有少数应用程序在使用 RMI 激活。此外,维护 RMI 激活的运行成本很高且复杂。

新发布流程

本文基于 JEP 3,因为它涵盖了所有流程变更。在此,我们将尝试简洁地总结它。

考虑到上述新架构、平台的持续开发和新的六个月发布周期(通常是六月和十二月),Java 将会更快地发展。遵循以下过程,JDK 开发团队将启动下一个功能版本的发布周期。

主线的分支是流程的起点。然后,JDK/JDK$N 被用作稳定存储库以继续开发(例如,JDK17)。那里的开发将继续专注于稳定发布。

在继续流程之前,让我们先定义一些术语。

  • Bug:此术语指任务或工单。
  • Current:这些是指向新添加的功能的修复(该功能已存在于此版本中)或与当前版本(即将发布的版本)相关的真正 Bug(新 JEP)。
  • Targeted:与之前的迭代相关,并计划在此新迭代中进行改进或解决。
  • Priorities:从 P1 到 P5,P1 为最高优先级,P5 为最低优先级。
  • 新格式
  • 目前,没有新的 JEPs 或新 JEPs 添加到 JDK/JDK$N 存储库,它像一个发布分支一样运行。
  • 稳定后,此存储库中的开发将移至主线,新开发将在那里继续。
  • Ramp Down Phase 1 (RDP 1) 大约需要四到五周时间。开发人员放弃目标 P1-P3 和所有当前 P4-P5(取决于推迟、修复或增强)。因此,不需要 P5+ 测试/文档 Bug 和集中的 P3+ 代码问题。
  • Ramp Down Phase 2 (RDP 2) 持续三到四周。他们目前推迟了当前的 P3-P5 和目标 P1-P3(取决于推迟、修复或增强)。

下一步?

JDK 架构师仍在致力于许多旨在更新平台的项目。更好的开发工具和更快、更可靠的 API 是主要目标。

因此,JDK 18 应该会在六个月内发布,尽管它不太可能包含任何重大或颠覆性的修改。我们可以通过官方 OpenJDK 项目门户网站上的拟议 JEP 列表来关注此版本。

结论

在本文中,我们讨论了 Java 17 新版本的新闻,回顾了其最新的改进、新功能、支持定义和发布周期流程。