JDK 12 ,JDK 13 , JDK 14 新特性 详解

近年来,甲骨文做出了一些突破性的决定。 他们包括新的半年发布模式与预览功能和更短的发布和反馈周期的新功能。 许可模式也发生了变化,Oracle JDK 不再免费提供。 这加剧了竞争,因此您现在可以从包括 Oracle 在内的各种供应商获得免费的 OpenJDK 发行版。 自从 java11以来,它已经与 oraclejdk 实现了二进制兼容,并且采用开源许可证。

一年半以前,最后一个 LTS 版本 Java 11于2019年秋季发布。 从那时起,随后的两个主要版本只有有限数量的新特性。 JDK 孵化器项目(Amber, Valhalla, Loom …) ,然而,正在致力于许多新想法,所以不足为奇的是,刚刚发布的 JDK 14的功能范围再次显著扩大。 即使只有少数人会在生产环境中使用新版本,您仍然应该关注新特性,并尽早就预览功能提供反馈。 这是确保新特性投入生产直到下一个 LTS 版本最终完成的唯一方法,该版本将在2021年秋季以 Java 17的形式发布。

下列Java增强建议(JEP)已经实现。在本文中,我们将从开发人员的角度来仔细研究感兴趣的主题。

JEP 305: Pattern Matching for instanceof (Preview)
JEP 343: Packaging Tool (Incubator)
JEP 345: NUMA-Aware Memory Allocation for G1
JEP 349: JFR Event Streaming
JEP 352: Non-Volatile Mapped Byte Buffers
JEP 358: Helpful NullPointerExceptions
JEP 359: Records (Preview)
JEP 361: Switch Expressions (Standard)
JEP 362: Deprecate the Solaris and SPARC Ports
JEP 363: Remove the Concurrent Mark Sweep (CMS) Garbage Collector
JEP 364: ZGC on macOS
JEP 365: ZGC on Windows
JEP 366: Deprecate the ParallelScavenge + SerialOld GC Combination
JEP 367: Remove the Pack200 Tools and API
JEP 368: Text Blocks (Second Preview)
JEP 370: Foreign-Memory Access API (Incubator)

JEP 305: Pattern matching for instanceof

自20世纪60年代以来,模式匹配语言的概念已经在各种编程语言中得到了应用。 其中最现代的例子是 Haskell 和 Scala。 模式是一个谓词的组合,该谓词匹配目标结构和该模式中的一组变量。 如果这些变量匹配,则为它们分配相应的内容。 其目的是破坏对象,也就是将它们分解为它们的组件。

到目前为止,Java 只能区分 switch 语句中的数据类型 integer、 string 和 enum。 然而,随着 Java 12中开关表达式的引入,迈向模式匹配的第一步已经迈出。 使用 Java 14,我们现在可以额外地使用模式匹配操作符 instanceof。 避免了不必要的强制转换,减少的冗余提高了可读性。

例如,在此之前,必须按照以下步骤检查空字符串或空集合:

boolean isNullOrEmpty( Object o ) {
  return == null ||
    instanceof String && ((String) o).isBlank() ||
    instanceof Collection && ((Collection) o).isEmpty();
}

现在您可以在使用 instanceof 检查时直接将值赋给变量,并对其执行进一步调用:

boolean isNullOrEmpty( Object o ) {
  return o == null ||
    o instanceof String s && s.isBlank() ||
    o instanceof Collection c && c.isEmpty();
}

这种区别似乎微不足道,然而,Java 开发人员中的纯粹主义者节省了一个小而烦人的冗余。

开关表达式最早是在 Java 12和13中引入的,在这两种情况下都是作为一个预览特性。 它们现已在 jep361中最后确定。 这为开发人员提供了两种新的语法变体,它们具有更短、更清晰和更不容易出错的语义。 表达式的结果可以分配给变量,或者作为方法的值返回(清单1)。

JEP 358: Helpful NullPointerExceptions

对空引用的无意访问也是 Java 开发人员所担心的。 根据托尼•霍尔爵士(Sir Tony Hoare)自己的说法,他发明的零Y引用是一个错误,其后果高达数十亿美元。 这仅仅是因为在20世纪60年代阿尔戈语的发展过程中,它是如此容易实现。

在 Java 中,编译器和运行时环境都不支持处理零引用。 这些恼人的异常可以通过各种变通方法来避免。 最简单的方法是将检查设置为零。 不幸的是,这个过程非常繁琐,当我们需要它的时候我们往往会忘记它。 使用自 JDK 8以来包含的包装器类 Optional,您可以通过 API 显式地告诉调用者,一个值可以为零,并且它必须对此进行响应。 因此,您不能再意外地遇到空引用,而必须显式地处理可能为空的值。 这个过程对于公共接口的返回值非常有用,但是也会消耗额外的间接层,因为您总是需要解压实际值。

在其他语言中,辅助工具早已构建到语法和编译器中,比如Groovy中 NullObjectPattern 和 Safe Navigation 操作符(some?.method())。 在 Kotlin,可以明确区分可能不为空的类型和可能作为引用为 null 的类型。 我们将来也必须使用 Java 中的 nullpointerexception。 但是,作为预览特性引入的有用的NullPointerExceptions可以简化异常的故障排除。 为了在抛出 NullPointerException 时插入必要的信息,必须在启动时激活选项 -XX: + ShowCodeDetailsInExceptionMessages。 如果调用链中的一个值为零,那么您将收到一条有用的消息:

man.partner().name()

Result: java.lang.NullPointerException: Cannot invoke "Person.name()" because the return value of "Person.partner()" is null

Lambda表达式需要特殊处理。 例如,如果lambda函数的参数为零,则默认情况下将收到清单2所示的错误消息。要显示正确的参数名称,必须使用-g:vars选项编译源代码。 结果如下:

java.lang.NullPointerException: Cannot invoke "Person.name()" because "p" is null

Listing 2 清单2

Stream.of( man, woman )
  .map( p -> p.partner() )
.map( p -> p.name() )
.collect( Collectors.toUnmodifiableList() );

Result: java.lang.NullPointerException: Cannot invoke "Person.name()" because is null "

不幸的是,当一个空参数时,目前没有方法引用的指示:
但是,如本例所示,如果将每个流方法调用放在新行中,那么麻烦的代码行可以很快地缩小范围。 NullPointerExceptions在自动装箱/拆箱中也具有挑战性。 如果在这里也激活了编译器参数-g:vars,您还将收到新的有用的错误消息(清单3)。

Listing 3 清单3
int calculate() {
  Integer a = 2, b = 4, x = null;
  return a + b * x;
}
calculate();
Result: java.lang.NullPointerException: Cannot invoke "java.lang.Integer.intValue()" because "x" is null

JEP 359: Records

也许最令人兴奋的同时也是最令人惊讶的创新是记录类型的引入 。 它们是在Java 14发行中相对较晚实现的,是一种类声明的限制形式,类似于枚举。 记录是在Valhalla项目中开发的。 与Kotlin中的Data Classes和Scala中的Case Classes有某些相似之处。 紧凑的语法可能会使Lombok之类的库在将来过时。 Kevlin Henney还看到了以下优点:“我认为Java记录功能的有趣的副作用之一是,实际上,它将帮助揭示多少Java代码实际上是面向 getter / setter而非面向对象的。” 一个人有两个字段的简单定义可以在这里看到 :

public record Person( String name, Person partner ) {}

一个带有附加构造函数的扩展变量,因此只有字段name 是强制的,也可以实现:

public record Person( String name, Person partner ) {
  public Person( String name ) { this( name, null ); }
  public String getNameInUppercase() { return name.toUpperCase(); }
}

编译器生成一个不可变类,除了这两个属性和它自己的方法之外,它还包含访问器的实现(没有 getter!) 、构造函数 equals / hashcode 和 toString (清单4)。

Listing 4 清单4
public final class Person extends Record {
  private final String name;
  private final Person partner;

  public Person(String name) { this(name, null); }
  public Person(String name, Person partner) { this.name = name; this.partner = partner; }

  public String getNameInUppercase() { return name.toUpperCase(); }
  public String toString() { /* ... */ }
  public final int hashCode() { /* ... */ }
  public final boolean equals(Object o) { /* ... */ }
  public String name() { return name; }
  public Person partner() { return partner; }

使用的行为符合预期,您无法从调用方判断记录类型是实例化的(清单5)。

Listing 5 清单5
var man = new Person("Adam");
var woman = new Person("Eve", man);
woman.toString(); // ==> "Person[name=Eve, partner=Person[name=Adam, partner=null]]"

woman.partner().name(); // ==> "Adam"
woman.getNameInUppercase(); // ==> "EVE"

// Deep equals
new Person("Eve", new Person("Adam")).equals( woman ); // ==> true

总结

Java没有死,Java万岁! 半年两次的OpenJDK版本使语言和平台都受益。 这次,新功能比Java 12和13还要多。而且,仍有许多功能需要在将来的版本中实现。 因此,我们的Java开发人员不会感到无聊,并且未来的前景仍然一片光明。 到2020年9月,我们可以预见Java 15的到来。

【关注公众号 “码农架构”】回复:“资料” 最新面试资料整理文档

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章