碎碎念

从哪几个角度学习新特性

  • 语法层面: 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 目标高度相似,在尽可能对吞吐量影响不大的前提下,对任意堆内存大小都可以把垃圾回收的停顿时间限制在十毫秒以内的低延迟