1. 概述
Java 16 于 2021 年 3 月 16 日发布,作为继 Java 15 之后的最新短期增量版本。该版本引入了多项实用特性,包括记录类(Records)和密封类(Sealed Classes)。本文将重点解析这些核心改进。
2. 通过代理实例调用默认方法 (JDK-8159746)
Java 16 增强了接口默认方法的支持,现在可通过 java.lang.reflect.InvocationHandler
在动态代理中使用反射调用接口默认方法。来看个简单示例:
interface HelloWorld {
default String hello() {
return "world";
}
}
通过反射在代理实例上调用默认方法:
Object proxy = Proxy.newProxyInstance(getSystemClassLoader(), new Class<?>[] { HelloWorld.class },
(prox, method, args) -> {
if (method.isDefault()) {
return InvocationHandler.invokeDefault(prox, method, args);
}
// ...
}
);
Method method = proxy.getClass().getMethod("hello");
assertThat(method.invoke(proxy)).isEqualTo("world");
3. 时间段支持 (JDK-8247781)
DateTimeFormatter
新增时间段符号 B
,提供比传统 am/pm 更友好的时间表述:
LocalTime date = LocalTime.parse("15:25:08.690791");
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("h B");
assertThat(date.format(formatter)).isEqualTo("3 in the afternoon");
输出从 3pm
变为 3 in the afternoon
。还可使用 B
、BBBB
、BBBBB
分别表示短、完整、窄格式。
4. 新增 Stream.toList() 方法 (JDK-8180352)
旨在减少常用 Stream
收集器的样板代码,如 Collectors.toList
:
List<String> integersAsString = Arrays.asList("1", "2", "3");
List<Integer> ints = integersAsString.stream().map(Integer::parseInt).collect(Collectors.toList());
List<Integer> intsEquivalent = integersAsString.stream().map(Integer::parseInt).toList();
intsEquivalent
实现相同效果且代码更简洁。
5. Vector API 孵化器 (JEP-338)
Vector API 在 Java 16 进入孵化阶段,提供向量计算能力,在支持的 CPU 架构上比传统标量计算性能更优。传统数组乘法:
int[] a = {1, 2, 3, 4};
int[] b = {5, 6, 7, 8};
var c = new int[a.length];
for (int i = 0; i < a.length; i++) {
c[i] = a[i] * b[i];
}
向量计算版本:
int[] a = {1, 2, 3, 4};
int[] b = {5, 6, 7, 8};
var vectorA = IntVector.fromArray(IntVector.SPECIES_128, a, 0);
var vectorB = IntVector.fromArray(IntVector.SPECIES_128, b, 0);
var vectorC = vectorA.mul(vectorB);
vectorC.intoArray(c, 0);
关键点:
- 使用
IntVector.fromArray
创建向量 SPECIES_128
表示 128 位向量(4 个 int)- 特定架构下可优化为单周期计算
注意:孵化阶段 API 可能随版本变化。
6. 记录类 (JEP-395)
记录类在 Java 14 引入,Java 16 带来增量改进。记录类是受限形式的类,用于简洁定义不可变数据对象。
6.1 传统实现方式
public final class Book {
private final String title;
private final String author;
private final String isbn;
public Book(String title, String author, String isbn) {
this.title = title;
this.author = author;
this.isbn = isbn;
}
public String getTitle() {
return title;
}
public String getAuthor() {
return author;
}
public String getIsbn() {
return isbn;
}
@Override
public boolean equals(Object o) {
// ...
}
@Override
public int hashCode() {
return Objects.hash(title, author, isbn);
}
}
传统方式存在样板代码多、易出错等问题(如遗漏 equals
/hashCode
)。
6.2 记录类实现
public record Book(String title, String author, String isbn) {
}
两行代码搞定,简洁高效。
6.3 Java 16 新增特性
现在允许在内部类中定义记录类(Java 15 的 JEP-384 遗漏的限制):
class OuterClass {
class InnerClass {
Book book = new Book("Title", "author", "isbn");
}
}
7. instanceof 模式匹配 (JEP-394)
Java 16 为 instanceof
添加模式匹配支持。传统写法:
Object obj = "TEST";
if (obj instanceof String) {
String t = (String) obj;
// do some logic...
}
改进后:
Object obj = "TEST";
if (obj instanceof String t) {
// do some logic
}
直接在 instanceof
中声明变量,省去强制转换步骤。
8. 密封类 (JEP-397)
密封类在 Java 15 首次引入,用于限定哪些子类可继承父类或实现接口。
8.1 基本用法
public sealed interface JungleAnimal permits Monkey, Snake {
}
public final class Monkey implements JungleAnimal {
}
public non-sealed class Snake implements JungleAnimal {
}
关键点:
sealed
+permits
指定允许的子类- 子类必须使用以下修饰符:
sealed
:需指定自己的子类final
:禁止继承non-sealed
:开放继承
优势:支持穷尽模式匹配,无需 else
处理未知情况:
JungleAnimal j = // some JungleAnimal instance
if (j instanceof Monkey m) {
// do logic
} else if (j instanceof Snake s) {
// do logic
}
8.2 Java 16 新增特性
- 将
sealed
、non-sealed
、permits
识别为上下文关键字(类似abstract
) - 禁止创建密封类的局部子类(类似禁止匿名子类)
- 加强密封类及其派生类的类型转换检查
9. 其他重要变更
- 外部链接器 API:提供灵活的本机代码访问方式(初始支持 C 语言),未来可能扩展到 C++/Fortran,目标是替代 JNI。
- JDK 内部强封装:默认开启强封装(
--illegal-access=permit
参数失效),直接影响依赖 JDK 内部 API 的库和应用(测试场景尤甚)。
10. 总结
本文解析了 Java 16 的核心新特性,包括记录类、密封类、模式匹配等实用改进。完整变更列表请参考 JDK 发行说明。所有示例代码可在 GitHub 获取。