碎碎念

从哪几个角度学习新特性

  • 语法层面: lambda表达式,switch,自动装箱、自动拆箱,enum,泛型<>,接口中的默认方法,静态方法,私有方法
  • API层面: StreamAPI,新的日期时间,Optional,String,集合框架
  • 层面优化: JVM的优化,元空间,GC,GC的参数,js的执行引擎

instanceof的模式匹配(预览)

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

1
2
3
if (o instanceof String str) {
System.out.println(str.contains("Java"));
}

非常实用的NullPointerException

该特性改进了NullPointerException的可读性,能够更准确地给出null变量的信息,通过-XX:+ShowCodeDetailsInExceptionMessages开启,这个增强特性不仅适用于方法调用,只要会导致NullPointerException的地方都适用,包括字段的访问、数组的访问和赋值

Record

使用Record来减少类声明语法,效果类似lombok的@Data注解,Kotlin中的dataclass。它们的共同点是类的部分或全部状态可以直接在类中描述,并且这个类中只包含了纯数据而已。该预览提供了一种更为紧凑的语法来声明类,值得一提的是,该特性可以大幅减少定义类似数据时所需的样板代码

1
public record User(String userId, String password) {}

可以看到用Record创建一个对象非常的简单,当你用Record声明一个类时,该类将自动拥有以下功能:

  • 获取成员变量的简单方法,以上面代码为例userId和password。注意区别于我们平常getter的写法
  • 一个equals方法的实现,执行比较时会比较该类的所有成员方法
  • 重写equals当然要重写hashCode
  • 一个可以打印该类所有成员属性的toString方法
  • 请注意只会有一个构造方法
  • 还可以在Record声明的类中定义静态字段、静态方法、构造器或实例方法
  • 不能在Record声明的类中定义实例字段,类不能声明为abstract,不能声明显式的父类等

和枚举类型一样,记录也是类的一种受限形式,作为回报,记录对象在简洁性方面提供了显著的好处

Switch表达式

  • 这是JDK12和13中的预览特性,现在是正式特性了
  • 该特性规定,switch可以当作语句使用,也可以当作表达式使用
  • 这可以简化日常的编码方式,也为本版本中预览的模式匹配特性打下了基础
  • 使用-> 来替代以前的:+break;另外还提供了yield来在block中返回值
1
2
3
4
5
6
7
8
9
int num = switch (x) {
case "1" -> 1;
case "2" -> 2;
case "3" -> 3;
default -> {
System.out.println("default");
yield 4;
}
};

文本块

  • 简化跨越多行的字符串,避免对换行等特殊符号进行转义,简化编写Java程序
  • 增强Java程序中用字符串表示的其他语言的代码的可读性
  • 解析新的转义序列
  • \: 取消换行操作
  • \s: 表示一个空格
1
2
3
4
5
6
7
String text = """
It also lays
the foundation
for the pattern
matching feature
previewed in this version
""";

只需要打出三个双引号就可以把一段文本粘贴进来,并且没有换行字符,对于HTML代码,JSON代码等可读性更强

弃用ParallelScavenge和SerialOld

由于维护和兼容性测试的版本,在JDK8时将Serial + CMS、ParNew + Serial Old 这两个组合声明为废弃,并在JDK9中完全取消了这些组合的支持。ParallelScavenge和SerialOld的GC组合要被标记为Deprecate。原因就是这个GC组合需要大量的代码维护工作,并且这个GC组合很少被使用。因为它的使用场景应该是一个很大的Young区配合一个很小的Old区,这样的话,Old区使用SerialOldGc去收集时停顿时间我们才能接受。现在用-XX:+UseParallelGC或者-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

删除CMS垃圾回收器

由于维护和兼容性测试,在JDK8时将Serial + CMS、ParNew + Serial Old这两个组合声明为废弃,并在JDK9中完全取消了这些组合的支持。CMS的弊端:

  • 会产生内存碎片,导致并发清除后,用户线程可用空间不足
  • 既然强调了并发(Concurrent),CMS收集器对CPU资源非常敏感
  • CMS收集器无法处理浮动垃圾

上述这些问题,尤其是碎片化问题,给你的JVM实例就像是埋了一颗炸弹,说不定哪次就在你在业务高峰期来一次FGC,当CMS停止工作时,会把SerialOldGC作为备选方案,而SerialOldGC是JVM中性能最差的垃圾回收方式,停顿个几秒钟,上十秒都有可能,移除了CMS垃圾回收器,如果在JDK14中使用-XX:+UseConcMarkSweepGC的话JVM不会报错,只是会给出一个warning信息

Java HotSpot(TM) 64-Bit Server VM warning: Ignoring option UseConcMarkSweepGC; support was removed in 14.0

现在G1回收器已成为默认回收器好几年了。我们还看到了引入了两个新的收集器: ZGC(JDK11出现)和Shenandoah(Open JDK12),主打特点就是低停顿时间

ZGC

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