Java 17:新特性、升级考量及未来展望
Java 17,作为长期支持 (LTS) 版本,已于 2021 年 9 月 14 日正式发布。本文将深入探讨 Java 17 的新功能,并分析企业是否应考虑进行升级。
目前,许多应用程序仍在使用较旧的 Java 版本,包括之前的 LTS 版本,如 Java 11 和 Java 8。
那么,企业为何要升级到最新的 Java 版本呢? 升级至 Java 17 需要一定的投入,但其主要目的是充分利用 JVM 内部的各项创新特性和功能。
许多企业选择使用 Docker 和 Docker 镜像来简化升级过程,以便在短时间内轻松切换到 Java 17。开发人员可以在 Docker 镜像中定义他们的持续集成/部署 (CI/CD) 流程,并运行所有相关操作。这种方法不会影响使用旧版 Java 的其他团队,因为他们仍可使用原有的 Docker 镜像。
Java 17 的主要特性
macOS 和 AArch64 架构支持
Java 17 的一项关键 JVM 功能是使用 JEP 391 在 AArch64 架构上增强对 macOS 的支持。这将为 Apple 公司最新推出的 M1 系列处理器提供支持。
对于这些平台的用户而言,这或许并非全新特性,因为一些供应商早已推出支持此类架构的 JDK 版本,甚至可以追溯到 Java 8。然而,官方认可对于确保未来的维护和支持至关重要。 与此相对,Java 9 和 Java 16 增加了对 Linux/AArch64 平台的支持,以及 Windows/AArch64 的支持。
密封类
密封类是 Java 17 的新特性。该特性已经结束了试用阶段,正式成为 Java 17 的平台和语言的一部分。它允许开发人员指定一个类型的子类型,从而防止其他人以非预期的方式对其进行扩展或实现。
当您尝试将未密封类型转换为不允许的子类型时,密封类还允许编译器在编译时产生错误。此外,Java 17 为使用 Apple Metal API 而非 OpenGL 在 macOS 上运行的 AWT/Swing 应用程序带来了新的渲染管道。 它改进了 API 并增强了生成随机数的功能。
Java 17 中的变更、删除和限制
Java 17 还引入了一些变更、删除和新的限制。
JDK 内部封装
其中一个变化是 JDK 内部封装过程的完成。该过程最初在 Java 9 中引入,当时用户尝试使用反射等方法规避内部 API 的限制,会在运行时发出警告。同时,还添加了命令行参数来规范此行为。
自 Java 9 以来,已经创建了各种 API,以便以统一的方式执行最常用的任务,用户将在内部使用这些 API。在 Java 16 中,默认设置从警告更改为禁止访问,并抛出异常。但是,该行为可通过命令行参数进行更改。
在 Java 17 中,命令行参数被删除,并且无法禁用此限制。这意味着所有对内部 API 的未授权访问现在都受到保护。
严格的浮点语义
一项“删除”可以被描述为重新引入始终严格的浮点语义。 Java 1.2 引入了对 Java 中浮点语义默认值的修改,允许 JVM 在浮点计算中牺牲少量精度以提高性能。在必须使用严格语义的类和方法中,添加了 strictfp 关键字。从那时起,CPU 中引入了各种指令集类型,无需不必要的成本即可使用严格的浮点语义。这就消除了实现默认或严格语义的需要。
Java 17 取消了之前的默认语义,所有浮点运算都严格执行。strictfp 关键字仍然存在,但它没有任何效果,并且会在编译时引起警告。
提前编译 (AOT)
Java 9 引入了提前编译(AOT)作为利用 Graal 编译器的一个实验性功能,该编译器使用 Java 编写 JIT 代码。Java 10 通过整合 JVMCI 接口,使 Graal 编译器能够在 OpenJDK 中用作 JIT 编译器。 自发布以来,它有了很大的改进。Graal 编译器取得了巨大的进步,其 JVM 也被命名为 GraalVM。
RMI 激活
RMI 激活已通过JEP 407移除。此前,RMI 激活已在 Java 8 中删除,并最终被弃用,并标记为在 Java 15 中移除的要求。RMI 激活提供了一种通过 RMI 启用按需分布式对象资源的方法。但是,它的使用率非常低,并且目前有更好的替代方案。RMI 的其余部分不受激活部分消除的影响。
Applet API 的移除
Applet API 最终被指定为删除 JEP 398,该 API 最初在 Java 9 中被弃用。Applet API 提供了一种将 Java AWT/Swing 控件集成到浏览器内的网页中的方法。然而,没有任何现代浏览器可以支持这一点,这意味着在过去十年左右的时间里,小程序实际上已无法访问。
安全管理器
最关键的弃用是它的安全管理器(JEP 411)。安全管理器自 Java 1.0 以来一直在使用。它的目的是限制 Java 在计算机本地可以执行的操作,例如限制对网络、文件和其他网络资源的访问。它还会尝试通过阻止反射和特定 API 来对不受信任的代码进行沙盒处理。
安全管理器的终结始于 Java 12。添加了一个命令行参数以阻止在运行时使用安全管理器。Java 17 中的更改意味着,当尝试从命令行或在运行时动态设置安全管理器时,将在 JVM 中产生运行时警告。
孵化器和预览功能
鉴于 Java 17 已被升级为长期支持版本,很多人想知道 Java 17 是否包含任何预览和孵化器功能。实际上,Java 17 有两个孵化器模块和一个预览功能!
矢量 API
矢量 API (JEP 414) 目前正处于孵化器的第二阶段。该 API 允许开发人员定义矢量计算,然后 JIT 编译器会将其转换为 JVM 运行所在的 CPU 架构支持的相应矢量指令(例如,使用 SSE 或 AVX 指令集)。
以前,开发人员必须使用标量函数或构建特定于平台的本机库。在 Java 中实现 Vector API 还提供了一种无缝的回退机制,这在早期版本中较为复杂。
Vector API 的标准化使得 JDK 中的类可以使用它。Java 的数组 mismatch() 方法可以修改为在 Java 上运行,从而无需在 JVM 中维护和编写多个特定于平台的实现。
外部函数和内存 API
另一个孵化器功能称为外部函数和内存 API (JEP 412)。它是 Java 16 的另外两个孵化器模块的演进和合并,即外部链接器 API (JEP 389) 和外部内存 API (JEP 393)。它们都通过使用用 Java 编写的静态类型编程来提供对本机内存和代码的访问。
Switch 模式匹配
Java 17 中包含的语言预览的最后一个特性是 Switch 模式匹配 (JEP 406)。此语言特性根据类型扩展 switch 表达式和语句,类似于通过模式匹配使用的语法(JEP 394),而后者已成为 Java 16 的标准。
过去,如果要根据对象的动态特性执行不同的操作,则需要使用检查实例构造一个 if-else 链,例如:
String type(Object o) {
if (o instanceof List) {
return "A List of things.";
}
else if (o instanceof Map) {
return "A Map! It has keys and values.";
}
else if (o instanceof String) {
return "This is a string.";
}
else {
return "This is something else.";
}
}
通过结合开关表达式和开关的新模式匹配功能,该过程可以简化为类似于:
String type(Object o) {
return switch (o) {
case List l -> "A List of things.";
case Map m -> "A Map! It has keys and values.";
case String s -> "This is a string.";
default -> "This is something else.";
};
}
您可能已经注意到,在检查过程中有一个变量的声明。与 Pattern 中的其他变量一样,instance 的匹配表示该对象经过了类型检查和强制转换,并且可以从其当前区域内的变量中获得。
预览功能是模式匹配的又一步。下一步是包括解构数组和记录的功能。
您应该升级到 Java 17 吗?
是的,您需要不断升级到最新版本,但无需在第一天就升级。您正在使用的软件和库可能尚未更新以包含与 Java 17 的兼容性,因此您可能需要等待一段时间才能完成。
如果您坚持使用 Java 的 LTS 版本(如 Java 8 或 Java 11),那么该语言和 JVM 本身有很多理由需要升级到 Java 17。由于它是一个长期维护版本,因此您的生产环境最终也很有可能会更新到 Java 17。
如果您正在开始一个全新的项目,或者正在为 Java 17 做准备,那么尽早切换到 Java 17 可能是最有效率的选择,因为它可以降低迁移成本。这也可让从事该项目的开发人员充分利用所有最新功能和操作方面。
您可以利用过去几年发生的许多改进,例如改进对在 Java 上运行的容器的支持,以及新的低延迟垃圾收集器实现。