2020年 3 月 17 日, JDK/Java 14 正式 GA(General Available)。这是自从 Java 采用六个月一次的发布周期之后的第五次发布。Java 14 是两个长期支持版本 11 和 17 中间的一个,Java 14 无疑具有着承上启下的作用,从 Java 14 中我们或许能看出 Java 未来的发展趋势。
此版本包含的 JEP( Java/JDK Enhancement Proposals, JDK 增强提案)比 Java 12 和 13 加起来的还要多。总共 16 个新特性,包括两个孵化器模块、三个预览特性、两个弃用的功能以及两个删除的功能。
https://www.oracle.com/java/technologies/javase-jdk14-downloads.html
选择对应的版本下载安装即可。
https://www.jetbrains.com/idea/nextversion/#section=windows
因为旧版本的 IDEA 不支持 Java 14,所以我们要去安装 2020 版本的 IDEA,eclipse 也是这样。
进入之后,点击 file -> project structure
配置 Project language level 和 Language level
这个特性很有意思,因为它为更为通用的模式匹配打开了大门。模式匹配通过更为简便的语法基于一定的条件来抽取对象的组件,而 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类型");
}
可以看到新特性规定在比较类型的时候就进行类型转换,简化了代码,使程序更加可读,代码更加美观。
该特性改进了 NullPointerException 的可读性,能更准确地给出 null 变量的信息。该特性可以帮助开发者提高生产力,以及改进各种开发工具和调试工具的质量。一个目标是减少开发人员的困惑和担忧 。
早在 Java 8 的时候就提供了 Optional
Optional 在可能为 null 的对象上做了一层封装,强制你思考值不存在的情况,这样就能避免潜在的空指针异常
关于 Optional 的用法,不是本文的重点。在日常开发中经常结合 Stream 一起使用 Optional,还是比较好用的
另外一个值得一提的就是最近(2020 年 03 月 17 日)发布的 JDK 14 中对于 NPE 有了一个增强。那就是 JEP 358:Helpful NullPointerExceptions
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)
官方吐槽最为致命
早在 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 声明一个类时,该类将自动拥有以下功能:
和枚举类型一样,记录也是类的一种受限形式。作为回报,记录对象在简洁性方面提供了显著的好处。
Enum interface
还可以在 Record 声明的类中定义静态字段、静态方法、构造器或实例方法。
不能在 Record 声明的类中定义实例字段,类不能声明为abstract,不能声明显式的父类等。
为了在 Java 14 中引入这种新类型,需要在 Java.lang.Class 对象中添加如下两个新方法:
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 中返回值
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
""";
\
与\s escape sequence
由于维护和兼容性测试的成本,在 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.
该来的总会来,自从 G1(基于 Region 分代)横空出世后,CMS 在 JDK 9 中就被标记为 Deprecate 了(JEP 291:Deprecate the Concurrent Mark Sweep (CMS) GarbageCollector)
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)
令人震惊、革命性的 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 超越的关键版本,让我们见证历史吧!