Java 14 新特性

概述

2020年 3 月 17 日, JDK/Java 14 正式 GA(General Available)。这是自从 Java 采用六个月一次的发布周期之后的第五次发布。Java 14 是两个长期支持版本 11 和 17 中间的一个,Java 14 无疑具有着承上启下的作用,从 Java 14 中我们或许能看出 Java 未来的发展趋势。

Java 14 新特性_第1张图片

此版本包含的 JEP( Java/JDK Enhancement Proposals, JDK 增强提案)比 Java 12 和 13 加起来的还要多。总共 16 个新特性,包括两个孵化器模块、三个预览特性、两个弃用的功能以及两个删除的功能。

  • 孵化器模块:将尚未定稿的 API 和工具先交给开发者使用,以获得反馈,并用这些反馈进一步改进 Java 平台的质量。
  • 预览特性:是规格已经成型、实现已经确定,但还未最终定稿的功能。它们出现在 Java 中的目的是收集在真实世界中使用后的反馈信息,促进这些功能的最终定稿。这些特性可能会随时改变,根据反馈结果,这些特性甚至可能会被移除,但通常所有预览特性最后都会在 Java中 固定下来。

环境安装

JDK 14 的下载、安装

https://www.oracle.com/java/technologies/javase-jdk14-downloads.html

Java 14 新特性_第2张图片

选择对应的版本下载安装即可。

IDEA 2020.1 的下载、安装、配置

https://www.jetbrains.com/idea/nextversion/#section=windows

Java 14 新特性_第3张图片

因为旧版本的 IDEA 不支持 Java 14,所以我们要去安装 2020 版本的 IDEA,eclipse 也是这样。

进入之后,点击 file -> project structure

Java 14 新特性_第4张图片

配置 Project language level 和 Language level

Java 14 新特性_第5张图片

Java 14 新特性_第6张图片

超实用新特性

1.JEP 305:instanceof 的模式匹配(预览)

这个特性很有意思,因为它为更为通用的模式匹配打开了大门。模式匹配通过更为简便的语法基于一定的条件来抽取对象的组件,而 instanceof 刚好是这种情况,它先检查对象类型,然后再调用对象的方法或访问对象的字段。
有了该功能,可以减少 Java 程序中显式强制转换的数量,从而提高生产力,还能实现更精确、简洁的类型安全的代码。

使用新特性前:

if (obj instanceof String) {
     
	String str = (String) obj;
	System.out.println(str.contains("Java"));
} else {
     
	System.out.println("非String类型");
}

使用新特性后:

// 新特性:省去了强制类型转换的过程
if (obj instanceof String str) {
     
	System.out.println(str.contains("Java"));
} else {
     
	System.out.println("非String类型");
}

可以看到新特性规定在比较类型的时候就进行类型转换,简化了代码,使程序更加可读,代码更加美观。

2.JEP 358:非常实用的 NullPointerException

该特性改进了 NullPointerException 的可读性,能更准确地给出 null 变量的信息。该特性可以帮助开发者提高生产力,以及改进各种开发工具和调试工具的质量。一个目标是减少开发人员的困惑和担忧 。

早在 Java 8 的时候就提供了 Optional

  • Optional 在可能为 null 的对象上做了一层封装,强制你思考值不存在的情况,这样就能避免潜在的空指针异常

  • 关于 Optional 的用法,不是本文的重点。在日常开发中经常结合 Stream 一起使用 Optional,还是比较好用的

另外一个值得一提的就是最近(2020 年 03 月 17 日)发布的 JDK 14 中对于 NPE 有了一个增强。那就是 JEP 358:Helpful NullPointerExceptions

  • 该特性可以更好地提示哪个地方出现的空指针,需要通过 -XX:+ShowCodeDetailsInExceptionMessages 开启
  • 在未来的版本中,这个特性可能会默认启用
  • 这个增强特性不仅适用于方法调用,只要会导致 NullPointerException 的地方也都适用,包括字段的访问、数组的访问和赋值
Bank bank = new Bank(new Customer());
bank.getCustomer().getAccount().withdraw(200);

这段代码会抛出空指针异常,在 Java 14 新特性中,我们可以配置虚拟机参数 -XX:+ShowCodeDetailsInExceptionMessages,这样在抛出异常的时候就会为我们打印出空指针出现的位置。

Exception in thread "main" java.lang.NullPointerException: Cannot invoke "com.ml.feature.Account.withdraw(double)" because the return value of "com.ml.feature.Customer.getAccount()" is null
at com.ml.feature.Feature02.main(Feature02.java:20)

3.JEP 359:Record(预览特性)

官方吐槽最为致命

早在 2019 年 2 月份,Java 语言架构师 Brian Goetz,曾经写过一篇文章,详尽的说明了并吐槽了 Java 语言,他和很多程序员一样抱怨“Java太啰嗦”或有太多的“繁文缛节”,他提到:开发人员想要创建纯数据载体类(plain data carriers)通常都必须编写大量低价值、重复的、容易出错的代码。如:构造函数、getter/setter、equals()、hashCode() 以及 toString() 等。

以至于很多人选择使用 IDE 的功能来自动生成这些代码。还有一些开发会选择使用一些第三方类库,如 Lombok 等来生成这些方法,从而会导致了令人吃惊的表现(surprising behavior)和糟糕的可调试性(poordebuggability)。

神说要用 record,于是就有了

  • 我们有时候需要编写许多低价值的重复代码来实现一个简单的数据载体类:构造函数,
    访问器, equals(), hashCode () , toString ()等。为了避免这种重复代码, Java 14推
    出record

  • Java 14 也许最令人兴奋,同时也是最令人惊讶的创新就是:Record 类型的引入

  • 使用 record 来减少类声明语法,效果类似 lombok 的 @Data 注解, Kotlin 中的 data class。它们的共同点是类的部分或全部状态可以直接在类头中描述,并且这个类中只包含了纯数据而已

  • 该预览特性提供了一种更为紧凑的语法来声明类。值得一提的是,该特性可以大幅减少定义类似数据类型时所需的样板代码

public record Person(String name, Person partner) {
     
}
  • 当你用 record 声明一个类时,该类将自动拥有以下功能:

    • 获取成员变量的简单方法,以上面代码为例 name() 和 partner()。注意区别于我们平常 getter 的写法
    • 一个 equals 方法的实现,执行比较时会比较该类的所有成员属性
    • 重写 equals 当然要重写 hashCode
    • 一个可以打印该类所有成员属性的 toString 方法
    • 请注意只会有一个构造方法
  • 和枚举类型一样,记录也是类的一种受限形式。作为回报,记录对象在简洁性方面提供了显著的好处。

  • Enum interface

  • 还可以在 Record 声明的类中定义静态字段、静态方法、构造器或实例方法。

  • 不能在 Record 声明的类中定义实例字段,类不能声明为abstract,不能声明显式的父类等。

  • 为了在 Java 14 中引入这种新类型,需要在 Java.lang.Class 对象中添加如下两个新方法:

    • RecordComponent[] getRecordComponents()
    • boolean isRecord()

4.JEP 361:switch 表达式

int num = switch (x) {
     
	case "1":
		yield 1;
	case "2":
		yield 2;
	case "3":
		yield 3;
	default:
		yield 4;
};
  • 这是 JDK 12 和 JDK 13 中的预览特性,现在是正式特性了

  • 该特性规定,switch 可以当作语句使用,也可以当作表达式使用

  • 这可以简化日常的编码方式,也为本版本中预览的模式匹配(JEP 305)特性打下了基础

  • 具体情况:使用 -> 来替代以前的:+break,另外还提供了 yield 来在 block 中返回值

5.JEP 368:文本块(预览第二版)

String text2 = """
    	The Sound of silence
   	 	Hello darkness, my old friend
    	I've come to talk with you again
    	Because a vision softly creeping
    	Left its seeds while I was sleeping
        And the vision that was planted in my brain
        Still remains
        Within the sound of silence
        """;
  • JDK 13 引入的 text blocks 进行第二轮 preview,JDK 14 的版本主要增加了两个 escape sequences,分别是 \ \s escape sequence
  • 现实问题
    在 Java 中,通常需要使用 String 类型表达 HTML,XML,SQL 或 JSON 等格式的字符串,在进行字符串赋值时需要进行转义和连接操作,然后才能编译该代码,这种表达方式难以阅读并且难以维护
  • 目标
    • 简化跨越多行的字符串,避免对换行等特殊字符进行转义,简化编写 Java 程序
    • 增强 Java 程序中用字符串表示的其他语言的代码的可读性
    • 解析新的转义序列

6.JEP 366:弃用 ParallelScavenge 和 SerialOld GC 组合

  • 由于维护和兼容性测试的成本,在 JDK 8 时将 Serial + CMS、ParNew + Serial Old 这两个组合声明为废弃(JEP 173),并在 JDK 9 中完全取消了这些组合的支持(JEP214)

  • ParallelScavenge + SerialOld GC 的 GC 组合要被标记为 Deprecate 了

  • JDK 官方给出将这个 GC 组合标记为 Deprecate 的理由是:这个 GC 组合需要大量的代码维护工作,并且,这个 GC 组合很少被使用。因为它的使用场景应该是一个很大的 Young 区配合一个很小的 Old 区,这样的话, Old 区用 SerialOld GC 去收集时停顿时间我们才能勉强接受

  • 废弃了 parallel young generation GC 与 SerialOld GC 的组合(-XX:+UseParallelGC与-XX:-UseParallelOldGC配合开启),现在使用 -XX:+UseParallelGC -XX:-UseParallelOldGC 或者 -XX:-UseParallelOldGC 都会出现告警如下:

Java HotSpot(TM) 64-Bit Server VM warning: Option UseParallelOldGC was deprecated in version 14.0 and will likely be removed in a future release.  

7.JEP 363:删除 CMS 垃圾回收器

  • 该来的总会来,自从 G1(基于 Region 分代)横空出世后,CMS 在 JDK 9 中就被标记为 Deprecate 了(JEP 291:Deprecate the Concurrent Mark Sweep (CMS) GarbageCollector)

  • CMS的弊端 :

    1. 会产生内存碎片,导致并发清除后,用户线程可用的空间不足
    2. 既然强调了并发(Concurrent),CMS 收集器对 CPU 资源非常敏感
    3. CMS 收集器无法处理浮动垃圾
  • 上述的这些问题,尤其是碎片化问题,给你的 JVM 实例就像埋了一颗炸弹。说不定哪次就在你的业务高峰期来一次 FGC。当 CMS 停止工作时,会把 Serial Old GC 作为备选方案,而 Serial Old GC 是 JVM 中性能最差的垃圾回收方式,停顿个几秒钟,上十秒都有可能

  • 移除了 CMS 垃圾收集器,如果在 JDK 14 中使用 -XX:+UseConcMarkSweepGC的话,JVM 不会报错,只是给出一个 warning 信息

  • 现在 G1 回收器已成为默认回收器好几年了

  • 我们还看到了引入了两个新的收集器:ZGC(JDK 11出现)和 Shenandoah(Open Jdk12)

    • 主打特点:低停顿时间

8-9.JEP:ZGC on MacOS 和 Windows

令人震惊、革命性的 ZGC

  • ZGC 与 Shenandoah 目标高度相似,在尽可能对吞吐量影响不大的前提下,实现在任意堆内存大小下都可以把垃圾收集的停顿时间限制在十毫秒以内的低延迟

  • 《深入理解 Java 虚拟机》一书中这样定义 ZGC:ZGC 收集器是一款基于 Region 内存布局的,(暂时)不设分代的,使用了读屏障、染色指针和内存多重映射等技术来实现可并发的标记-压缩算法的,以低延迟为首要目标的一款垃圾收集器

测试数据:

虽然 ZGC 还在试验状态,没有完成所有特性,但此时性能已经相当亮眼,用“令人震惊、革命性”来形容,不为过。
未来将在服务端、大内存、低延迟应用的首选垃圾收集器。
JEP 364:ZGC 应用在 MacOS 上
JEP 365:ZGC 应用在 Windows 上

  • JDK 14 之前, ZGC 仅 Linux 才支持

  • 尽管许多使用 ZGC 的用户都使用类 Linux 的环境,但在 Windows 和 macOS 上,人们也需要 ZGC 进行开发部署和测试。许多桌面应用也可以从 ZGC 中受益。因此,ZGC 特性被移植到了 Windows 和 MacOS 上

  • 现在 Mac 或 Windows 上也能使用 ZGC 了,示例如下:-XX:+UnlockExperimentalVMOptions -XX:+UseZGC

写在最后

虽然 Java 14 为我们带来了许多如此方便的语法糖和底层的优化,但是大多数公司用的还是 Java 8,小部分公司用 Java 11 这两个长期版本,个人认为了解 Java 14 的新特性是为了能够一步步体会到 Java 语言的发展趋势。按照 Oracle 公司每半年发布一个大版本,每个季度发布一个中间特性版本,每三年发布一次长期支持版本的规律,下一个长期支持版本会是 2021 年发布的 Java 17,这注定是一个新的时代,是一个能够改变 Java 历史的版本,是决定 Java 是继续霸占编程语言榜首,还是被 Golang 超越的关键版本,让我们见证历史吧!

你可能感兴趣的:(Java)