本文翻译自What’s New in Java 15。
java15已于2020年九月发版,是一个短期jdk版本。在早期发行版本的一些特性基础上做了加强。本文涉及java15的新特性和一些java开发者刚兴趣的变化。
record是java的一个class type(译者:是的,在class之上又抽了一层type,已知具体类型有record,sealed[后面会介绍到]等)。主要用于简化只读数据对象【immutable data object】的创建。该特性在java14已经以“preview”的形式被提出,并于本版本被强化,但还不算做正式版本特性。
先来看一下没有record特性时我们定义一个只读对象:
public class Person {
private final String name;
private final int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
不难发现,为了定义一个只读对象,我们写了很多的代码:
看我们可使用record来以一种更加紧凑简化的方式来定义只读类:
public record Person(String name, int age) {}
请注意,和以往的类定义方式不同,这里用到了一个新的语法:用关键字record来指定具体的class type。那么这个语法的实际意义是什么呢?很简单,就是为我们自动生成了在2.1节中我们手动编写的代码。生成时机,自然是编译期了,因为我们在写代码要用到这些方法的。这些方法可见下面的反编译代码:
// Compiled from "ExampleRecord.java"
// 请忽略它是内部类这一点
public final class best.pratice.jdk15.ExampleRecord$Person extends java.lang.Record {
public best.pratice.jdk15.ExampleRecord$Person(java.lang.String, int);
public final java.lang.String toString();
public final int hashCode();
public final boolean equals(java.lang.Object);
public java.lang.String name();
public int age();
}
可见,record为我们省掉了太多的代码了。然而,它确实有一些局限:
sealed class的目的是允许类或者接口,声明它可以被哪些子类型实现。是一种对类层次的深度的限制。
sealed class涉及到两个关键字:sealed 和 permits (瞅瞅这关键字的时态和词形)
public abstract sealed class Person
permits Employee, Manager {
//...
}
这里我们声明了一个抽象类Person,并且指明了只有Employee和Manager类才能集成该抽象类。继承抽象类的语法和现行语法相同,但是有个限制:子类必须是final或者sealed或者non-sealed(non-sealed难倒不是第三个关键字吗),这样的机制就把类继承机制的无限继承限制在了有限层级范围。编译器完全能感知到这种继承限制,并能对我们的编码做一定的优化(其实是限制,有益的限制),它允许我们如下编码:
if (person instanceof Employee) {
return ((Employee) person).getEmployeeId();
}
else if (person instanceof Manager) {
return ((Manager) person).getSupervisorId();
}
// 这里不用再用else来兜底,编译器也不会报编译错误,因为编译器知道这里if...else if已经把可能的分支完全覆盖了。
hidden class是java15版本发布的一个新特性。虽然大部分java开发者不会直接从中受益,但对于工作内容是动态字节码和jvm语言的底层开发工程师来讲确为一则喜讯。
顾名思义,hidden clas的目标就是允许运行时创建一些不易被察觉到的类。这些隐藏类不能被编译时linked(因为编译器它们还没有生成),也不能被java的反射机制发现。它们的生命周期很短,因此他们的设计意图是为了高效的加载类和卸载类(这一句对于那些字节码层次的开发人员很有诱惑力吧)。诚然,现行java支持创建的匿名类是和隐藏类是有几分相似的,但是前者是依赖UnsafeAPI,而后者则无依赖。
模式匹配类型检查特性早在java14时就以“preview”预览状态出现了,而java15继续保持该状态,并且没有对此做任何增强。该特性主要用来消除样板代码,比如在instanceof操作符
// 往常的代码:instanceof判断之后,紧跟着就是一个类型强转。
if (person instanceof Employee) {
Employee employee = (Employee) person;
Date hireDate = employee.getHireDate();
//...
}
// ‘模式匹配特性’帮我们把instanceof判断和类型强转合二为一。
if (person instanceof Employee employee) {
Date hireDate = employee.getHireDate();
//...
}
// ‘模式匹配特性’甚至允许我们继续对强转后的变量做逻辑判断
if (person instanceof Employee employee && employee.getYearsOfService() > 5) {
//...
}
未来‘模式匹配特性’还会用于switch语句中。
“外部内存访问”特性在java14中是“incubating”孕育状态,在java15中依然如是,只不过后者给它增加了几个新功能点:
外部内存一般指非jvm堆内内存,当然它也不归java的垃圾收集机制管,而且它经常能操作巨大的内存分片。
以上新的api不会直接给大部分的程序员带来福音,但是却能给需要管理外部内存的第三方类库开发者送去实惠,比如分布式缓存、非规范文档存储、任意大的字节缓冲、内存映射文件等等。
在java15中,ZGC (JEP 377) 和Shenandoah (JEP 379) 都已经完成试验阶段,它们都可以通过配置的形式来选择使用。有必要说明一点,先前的G1收集器依然是默认的垃圾收集器。关于Shenandoah有一点需要注意:并不是所有供应商的JDK都可以使用它,尤其是Oracle JDK没有包含它。
java15中还有其他几个值得留意的改变:
在已发布的版本的基础上,java15提供了records、text blocks、新垃圾收集算法。它也新增了几个“preview”状态的特性:sealed classes、hidden classes。
java15不是一个长期版本,最多到2021年三月,届时讲推出java16,同样是一个短期版本。最近的一个长期版本是java17。