Project Valhalla's First Preview: JEP 401 Redefines == for Java Objects

TL;DR · AI 摘要
JEP 401引入值对象,重新定义Java对象的==操作符行为,影响JDK 28的开发实践。
核心要点
- 值类字段必须在构造时显式初始化,且不可变
- 值对象的==比较字段值而非引用,与String等身份类行为不同
- 启用预览功能后,LocalDate等JDK类将转换为值类
结构提纲
按章节快速跳转。
- §引言
JEP 401作为Project Valhalla首个预览特性,重构Java对象比较机制。
value修饰符强制字段显式初始化,禁止同步方法。
值对象比较基于字段值而非引用,身份类保持原有行为。
预览启用后,Primitive Wrapper等类将转换为值类。
- ›迁移挑战
需调整现有代码的构造规则和同步方法实现。
思维导图
用一张图看清主题之间的关系。
查看大纲文本(无障碍 / 无 JS 友好)
- JEP 401值对象
- 核心机制
- value修饰符强制初始化
- ==语义变更
- 影响范围
- JDK类转换
- 代码迁移
金句 / Highlights
值得收藏与分享的关键句。
value class Point的字段必须在构造函数中显式赋值
值对象的==比较逻辑:相同类且字段值相等时返回true
String类保持身份类特性,new String("abc")与字面量比较不相等
Project Valhalla 的首次预览:JEP 401 重新定义 Java 对象的 == 运算符 - InfoQ
InfoQ 首页 News Project Valhalla 的首次预览:JEP 401 重新定义 Java 对象的 == 运算符
Java
Framework 之下:为什么 Agent Context 是一个基础设施问题(8 月 27 日网络研讨会)
Project Valhalla 的首次预览:JEP 401 重新定义 Java 对象的 == 运算符
2026 年 8 月 10 日 3 分钟阅读
作者:
- A N M Bazlur Rahman
#### 关注我们
Youtube
232K 粉丝
26K 粉丝
新
RSS
19K 读者
X
57.1k 粉丝
21K 点赞
Bluesky
收听本文 -
0:00
音频准备就绪
您的浏览器不支持音频元素。
正常
1.25x
1.5x
喜欢
新下拉阅读列表
- 阅读列表
JEP 401(值对象(预览))已集成到 JDK 28 中。它引入了仅包含 final 字段的无身份类实例,改变了这些对象的 == 运算符行为,并为更扁平、无需分配的 JVM 表示打开了大门。该预览功能默认处于禁用状态,编译和运行时都需要使用 --enable-preview 参数启用。
对于开发者而言,直接影响包括新增的值修饰符、更严格的构造规则、对同步的限制,以及多个基于值的 JDK 类的迁移影响。该提案经过 OpenJDK 的 valhalla-dev 邮件列表多年的讨论形成。
使用值修饰符声明的类是值类;其他所有类仍然是身份类。其实例字段隐式为 final,且每个字段必须在新实例可被观察到之前完成赋值。
value class Point {
private int x; // 隐式 final
private int y;
public Point(int x, int y) {
this.x = x; // 每个字段都已赋值
this.y = y; // 在构造完成之前
}
public int x() { return x; }
public int y() { return y; }
}记录类也可以使用该修饰符,例如 value record Color(byte red, byte green, byte blue) { }。值类的实例方法不能使用 synchronized。JEP 539(JVM 中的严格字段初始化)提供了强制构造规则的字节码验证。
最显著的语义变化是 == 运算符。对于身份对象,其行为保持不变。对于值对象,当两个操作数是相同类的实例且字段值相同时,== 运算符返回 true;引用类型的字段会递归使用 == 进行比较。
Point p1 = new Point(3, 4);
Point p2 = new Point(3, 4);
assert p1 == p2; // true: 同一类,字段值相同
Object o1 = p1, o2 = p2;
assert o1 == o2; // true: 作为 Object 仍然无法区分
String s1 = "hamburger";
String s2 = new String(s1);
assert s1 != s2; // true: String 仍然是身份类这并不是要放弃 equals 方法的邀请。JEP 401 并未将 == 重新定义为 equals 的替代方案;由于值对象的内部状态与其表示的状态并不总是相同,因此仍然建议使用 equals 方法进行对象比较。
启用预览功能后,包括原始包装类和 LocalDate 在内的多个基于值的 JDK 类将变为值类。禁用预览功能时,编译器将继续使用其身份对象形式,行为与 JDK 27 保持一致。使用预览功能编译的代码也必须在启用预览功能时运行。
重新编译迁移的类是推荐的,因为 LoadableDescriptors 类文件属性会在加载时通知 JVM 该类是一个值类。一些 API 将停止工作:为值对象创建 Reference 会抛出 IdentityException,且 javac 在 JDK 28 中会将身份警告扩展到值类。
预期的收益是优化而非保证的基准结果。JVM 可能会将值对象标量化解为构成字段,或将其扁平化为直接存储在字段或数组元素中的紧凑表示。扁平化仍然受原子性限制;在典型平台上,可用编码可能小至 64 位(包括空标志)。当两种优化均不适用时,JVM 会回退到普通分配,尤其是在优化后的 JIT 代码可用之前预热阶段。
JEP 401 将问题表述为开发者意图与 Java 保证之间的不匹配。代表复数、像素颜色或日期的小类用于承载数据,但普通 Java 对象由身份定义:每次构造函数调用都会生成一个可区分于其他对象的实例。对于此类类型,JEP 认为身份通常无关紧要且可能有害。
身份还会带来运行时成本。通常需要 JVM 为每个对象分配内存,并在每次使用时解引用该位置,增加垃圾收集器压力并降低局部性。逃逸分析可以回收部分成本,但其优化不可预测,且在对象逸出后(例如存储到字段或数组时)无法提供帮助。
JEP 承认值对象会显著改变 Java 的对象模型。开发者可能会对 == 和 synchronized 的新行为感到惊讶,尽管提案预计此类干扰将较为罕见且可管理。if_acmpeq 字节码也会增加一个值对象检查,身份检查路径仍旨在保持快速。
文档指出两个安全考量:== 和 identityHashCode 可能间接暴露私有字段值,且比较两个大型值对象树可能需要无界时间。
作者部分主容器
关于作者
部分标题
每位作者的主容器
#### A N M Bazlur Rahman
显示更多
显示更少
#### 本内容属于 Java 主题
##### 相关主题:
- 开发
- JEP
- Java
- JVM
- JDK 28
- 垃圾回收
- 性能
- Project Valhalla
- 相关编辑
- 相关赞助商 为什么 API 无法信任客户端——以及如何弥合差距
- 相关赞助商 测试。保护。重复。Guardsquare 将移动应用测试与保护结合,实现最大安全性且零性能折损。请求报价。
InfoQ 新闻通讯
每周内容精选每 Tuesday 发送。加入 25 万名以上高级开发者的社区。查看示例
我们保护您的隐私。