“语言特性不是越多越好,而是越恰到好处越好。”
2021 年 3 月 JDK 16 发布那会儿,我正折腾一个自己的小工具,写了个 instanceof 判断,后面跟着强转——结果因为嵌套太深,漏了一层检查,跑起来直接炸出 ClassCastException。本地调试了半小时才找到问题,气得我直拍桌子:这种低级错误,编译器就不能帮我拦一下吗?
结果 JDK 16 直接把 Records 和 instanceof 模式匹配给扶正了。不用再加 --enable-preview,IDE 也不再满屏黄线警告。我立马切过去重写那段代码,跑完测试,心里一块石头落地:终于不用自己盯着类型转换了,编译器替我兜底。
其实我一直对非 LTS 版本有点犯怵,总觉得“不稳定”。但那天突然想通了:既然已经在本地玩密封类(JDK 15 预览),又何必卡在最后一步?好东西,就该早点用上,管它是不是 LTS。
JDK 16 的更新基于 JSR 391,但我压根没去翻规范——真正打动我的,是它干了件特别实在的事:把过去几个版本试水的好东西,直接焊死在语言里。Records、模式匹配,这两个我天天在本地玩的预览特性,终于能放心写进任何代码了。至于 Vector API?哦对,还有 Unix-domain socket……听着挺酷,但我到现在都没找到用它的理由。
从 JDK 14 开始我就在本地折腾 Records,但一直不敢放进“正经练习代码”里,就怕哪天语法变了。JDK 16 终于把它扶正,我当场就把一堆手写的 DTO 全换成了 record。
以前写个纯数据类,光是 getter/setter/equals/toString 就占半屏。Lombok 虽然能省事,但有时候 IDE 不识别,调试时看不到字段值,特别抓狂。Records 不一样——编译器生成的东西,IDE 认,调试器认,连反编译都看得懂。
public record Point(int x, int y) {}一行搞定。不可变、带 accessor、toString 还带字段名。最爽的是,看到 record 关键字,就知道这东西不能继承、不能塞业务逻辑——再也不用纠结“这个类到底该不该有 setter”。
当然,Records 也有让我皱眉的地方。有次我想给它加个静态工厂方法做校验,结果发现如果自定义构造器,就得手动处理所有字段,有点啰嗦。后来才搞明白:record 的设计哲学说白了就是“只存数据,别想太多”。你要是开始往里面塞逻辑,那它就不是 record 了,跟普通 class 没啥区别。
📌 意义:Records 正式化,对我而言不是“多了一个语法”,而是少了一堆样板代码和心理负担。它可能替代不了 Lombok 在所有场景,但在纯数据载体上,它更干净、更可靠——至少不用求着注解处理器干活。
instanceof 模式匹配(正式特性,JEP 394)这个特性我从 JDK 14 就开始用,但每次写完都心虚——生怕哪天升级 JDK 时预览语法变了。JDK 16 终于让它转正,我第一时间把所有 instanceof + cast 全部重写了。
比如这段老代码:
if (obj instanceof String) {
String s = (String) obj;
if (s.length() > 5) {
// ...
}
}现在变成:
if (obj instanceof String s && s.length() > 5) {
// ...
}变量 s 自动绑定,作用域安全,编译器保证不会出 ClassCastException。最妙的是,它还能和密封类配合。比如处理一个封闭的表达式树,switch + 模式匹配,代码干净得不像 Java——简直像在写 Kotlin。
不过也有小坑:嵌套使用时括号优先级容易搞错。有次我写 if (x instanceof A a && y instanceof B b),结果因为短路求值顺序不对,逻辑跑偏了。后来养成习惯:复杂条件一定加括号,宁可啰嗦点,也不能赌。
📌 意义:模式匹配正式化,对我来说不只是“少写两行代码”,而是少了一个潜在的运行时陷阱。那种“类型检查和转换必须同步”的安全感,只有被
ClassCastException虐过的人才懂。
坦白讲,这个特性我至今没在实际代码里用过。
官方说它能让 Java 做“向量化计算”,说白了就是让 CPU 一次干好几件事,而不是一个一个来。听起来很牛,对吧?我也心动了,拿它在一个图像处理小脚本里试了试。
在 Intel 机器上跑,速度确实快了两倍多,感觉像是开了外挂。可一换到 M1 Mac 上……直接报错:UnsupportedOperationException。查了半天才知道,Vector API 还没完全适配 ARM 架构。
本来挺高兴,结果被现实泼了盆冷水。最后只能放弃。所以对我来说,Vector API 更像是“未来可期”,而不是“现在能用”。但它至少说明:Java 开始认真对待高性能计算了,不再是“只会写 CRUD”的语言。
这个特性其实挺实用,尤其在 macOS 或 Linux 上做本地通信。
我之前写了个本地代理小工具,用 TCP loopback,延迟高不说,还得占一个端口,偶尔还被防火墙拦。JDK 16 出来后,我试着改成 Unix-domain socket——说白了,就是让两个程序通过文件系统里的一个“虚拟管道”聊天,比走网络快多了。
var addr = UnixDomainSocketAddress.of("/tmp/my.sock");
try (var channel = SocketChannel.open(StandardProtocolFamily.UNIX)) {
channel.connect(addr);
// ...
}连接速度明显快了,而且不用管端口冲突。然而,当我把这个小工具放到 Windows 上跑单元测试时,直接挂了——原来 Windows 对 Unix-domain socket 支持有限。后来加了个 OS 判断,Windows 回退到 TCP,才算跑通。
说白了,这特性对大多数应用没用,但对需要高性能本地 IPC 的场景,是个实打实的便利。
JDK 16 对我来说,不是一个“值得长期追随的版本”,而是一个“让我敢用新特性的转折点”。
Records 和模式匹配正式化,直接消除了我对预览特性的顾虑。虽然升级过程中踩了 ZGC 日志、元空间监控的小坑,但整体收益远大于成本。代码更简洁,bug 更少,写起来更顺手——这才是技术演进该有的样子。
至于 Vector API、Unix-domain socket?它们可能暂时用不上,但至少让我看到:Java 没有躺在 Web 服务的舒适区里,它还在努力拓宽自己的边界。
JDK 16 很快就被 JDK 17 的光芒掩盖,但对我而言,它是一个关键的信任建立点——从那以后,我不再害怕用非 LTS 版本的新特性,只要它解决了我的真实痛点。
[1] JSR 391: Java SE 16 规范: https://www.jcp.org/en/jsr/detail?id=391
[2] OpenJDK JDK 16 官方页面: https://openjdk.org/projects/jdk/16/
[3] JEP 395: Records: https://openjdk.org/jeps/395
[4] JEP 394: Pattern Matching for instanceof: https://openjdk.org/jeps/394
[5] JEP 338: Vector API (Incubator): https://openjdk.org/jeps/338