JDK17特性
一、JAVA17概述
JDK 16 刚发布半年(2021/03/16),JDK 17 又如期而至(2021/09/14),这个时间点特殊,蹭苹果发布会的热度?记得当年 JDK 15 的发布也是同天。
Oracle 宣布,从 JDK 17 开始,后面的 JDK 带附加条款免费提供!!!Java 17+ 可以免费使用了,包括商用,更详细的条款可以点击阅读详细条款**
JDK 17 是自 2018 年 JDK 11 后的第二个长期支持版本,支持到 2029 年 9 月,支持时间长达 8 年,这下可以不用死守 JDK 8 了,JDK 17+ 也可以是一种新的选择了。下一个第三个长期支持版本是 JDK 21,时间为 2023 年 9 月,这次长期支持版本发布计划改了,不再是原来的 3 年一次,而是改成了 2 年一次!非长期支持版本还是半年发一次不变,下一个非长期支持版本计划在 2022/03 发布
OpenJDK文档:https://openjdk.java.net/projects/jdk/17/
JDK 17 这个版本提供了 14 个增强功能,另外在性能、稳定性和安全性上面也得到了大量的提升,以及还有一些孵化和预览特性,有了这些新变化,Java 会进一步提高开发人员的生产力。
二、语法层面的变化
1.JEP 409:密封类
- 概述
密封类,这个特性在 JDK 15 中首次成为预览特性,在 JDK 16 中进行二次预览,在 JDK 17 这个版本中终于正式转正了。
- 历史
密封类是由JEP 360提出的,并在JDK 15 中作为 预览功能提供。它们由JEP 397再次提出并进行了改进,并作为预览功能在 JDK 16中提供。该 JEP 建议在 JDK 17 中完成密封类,与 JDK 16 没有任何变化。
- 目标
- 允许类或接口的作者控制负责实现它的代码。
- 提供比访问修饰符更具声明性的方式来限制超类的使用。
- 通过为模式的详尽分析提供基础,支持模式匹配的未来方向。
- 原因
类和接口的继承层次结构的面向对象数据模型已被证明在对现代应用程序处理的现实世界数据进行建模方面非常有效。这种表现力是 Java 语言的一个重要方面。
然而,在某些情况下,可以有效地控制这种表现力。例如,Java 支持枚举类来模拟给定类只有固定数量实例的情况。在以下代码中,枚举类列出了一组固定的行星。它们是该类的唯一值,因此您可以彻底切换它们——无需编写 default
子句:
enum Planet {
MERCURY, VENUS, EARTH }
Planet p = ...
switch (p) {
case MERCURY: ...
case VENUS: ...
case EARTH: ...
}
使用枚举类值的模型固定集通常是有帮助的,但有时我们想一套固定的模型种价值。我们可以通过使用类层次结构来做到这一点,而不是作为代码继承和重用的机制,而是作为列出各种值的一种方式。以我们的行星示例为基础,我们可以对天文领域中的各种值进行建模,如下所示:
interface Celestial {
... }
final class Planet implements Celestial {
... }
final class Star implements Celestial {
... }
final class Comet implements Celestial {
... }
然而,这种层次结构并没有反映重要的领域知识,即我们的模型中只有三种天体。在这些情况下,限制子类或子接口的集合可以简化建模。
考虑另一个例子:在一个图形库中,一个类的作者 Shape
可能希望只有特定的类可以扩展 Shape
,因为该库的大部分工作涉及以适当的方式处理每种形状。作者对处理 的已知子类的代码的清晰度感兴趣 Shape
,而对编写代码来防御 的未知子类不感兴趣 Shape
。允许任意类扩展 Shape
,从而继承其代码以供重用,在这种情况下不是目标。不幸的是,Java 假定代码重用始终是一个目标:如果 Shape
可以扩展,那么它可以扩展任意数量的类。放宽这个假设是有帮助的,这样作者就可以声明一个类层次结构,该层次结构不能被任意类扩展。在这样一个封闭的类层次结构中,代码重用仍然是可能的,但不能超出。
Java 开发人员熟悉限制子类集的想法,因为它经常出现在 API 设计中。该语言在这方面提供了有限的工具:要么创建一个类 final
,使其具有零个子类,要么使该类或其构造函数成为包私有的,因此它只能在同一个包中具有子类。包私有超类的示例 出现在 JDK 中:
package java.lang;
abstract class AbstractStringBuilder {
... }
public final class StringBuffer extends AbstractStringBuilder {
... }
public final class StringBuilder extends AbstractStringBuilder {
... }
当目标是代码重用时,包私有方法很有用,例如 AbstractStringBuilder
让 append
. 然而,当目标是对替代方案进行建模时,这种方法是无用的,因为用户代码无法访问关键抽象——超类——以 switch
覆盖它。允许用户访问超类而不允许他们扩展它是无法指定的,除非诉诸涉及非 public
构造函数的脆弱技巧——这些技巧不适用于接口。在声明 Shape
及其子类的图形库中,如果只有一个包可以访问 Shape
.
总之,超类应该可以被广泛访问 (因为它代表用户的重要抽象)但不能广泛 扩展(因为它的子类应该仅限于作者已知的那些)。这样一个超类的作者应该能够表达它是与一组给定的子类共同开发的,既为读者记录意图,又允许 Java 编译器强制执行。同时,超类不应过度约束其子类,例如,强迫它们成为 final
或阻止它们定义自己的状态。
2.JEP 406:switch模式匹配(预览)
- 概述
使用 switch
表达式和语句的模式匹配以及对模式语言的扩展来增强 Java 编程语言。扩展模式匹配以 switch
允许针对多个模式测试表达式,每个模式都有特定的操作,以便可以简洁安全地表达复杂的面向数据的查询。
instanceof 模式匹配是JAVA14 非常赞的一个新特性! 这次在 JDK 17 中为 switch 语句支持模式匹配
- 目标
switch
通过允许模式出现在case
标签中来扩展表达式和语句的表现力和适用性。- 允许在
switch
需要时放松对历史的零敌意。- 引入两种新的模式:保护模式,允许使用任意布尔表达式细化模式匹配逻辑,以及带 括号的模式,以解决一些解析歧义。
- 确保所有现有的
switch
表达式和语句继续编译而不做任何更改并以相同的语义执行。- 不要引入
switch
与传统switch
构造分离的具有模式匹配语义的新式表达式或语句。switch
当 case 标签是模式与 case 标签是传统常量时,不要使表达式或语句的行为不同。
老式的写法
static String formatter(Object o) {
String formatted = "unknown";
if (o instanceof Integer i) {
formatted = String.format("int %d", i);
} else if (o instanceof Long l) {
formatted = String.format("long %d", l);
} else if (o instanceof Double d) {
formatted = String.format("double %f", d);
} else if (o instanceof String s) {
formatted = String.format("String %s", s);
}
return formatted;
}
支持模式匹配的switch
static String formatterPatternSwitch(Object o) {
return switch (o) {
case Integer i -- **String.format("int %d", i);
case Long l -- **String.format("long %d", l);
case Double d -- **String.format("double %f", d);
case String s -- **String.format("String %s", s);