InfoQ

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

8.5内容质量
Project Valhalla's First Preview: JEP 401 Redefines == for Java Objects

TL;DR · AI 摘要

JEP 401引入值对象,重新定义Java对象的==操作符行为,影响JDK 28的开发实践。

核心要点

  • 值类字段必须在构造时显式初始化,且不可变
  • 值对象的==比较字段值而非引用,与String等身份类行为不同
  • 启用预览功能后,LocalDate等JDK类将转换为值类

结构提纲

按章节快速跳转。

  1. JEP 401作为Project Valhalla首个预览特性,重构Java对象比较机制。

  2. ·值对象定义

    value修饰符强制字段显式初始化,禁止同步方法。

  3. 值对象比较基于字段值而非引用,身份类保持原有行为。

  4. 预览启用后,Primitive Wrapper等类将转换为值类。

  5. 需调整现有代码的构造规则和同步方法实现。

思维导图

用一张图看清主题之间的关系。

查看大纲文本(无障碍 / 无 JS 友好)
  • JEP 401值对象
    • 核心机制
      • value修饰符强制初始化
      • ==语义变更
    • 影响范围
      • JDK类转换
      • 代码迁移

金句 / Highlights

值得收藏与分享的关键句。

#Java#JEP 401#Project Valhalla#值对象
打开原文

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 粉丝

Linkedin

26K 粉丝

Instagram

RSS

19K 读者

X

57.1k 粉丝

Facebook

21K 点赞

Bluesky

收听本文 -

0:00

音频准备就绪

您的浏览器不支持音频元素。

正常

1.25x

1.5x

喜欢

新下拉阅读列表

  • 阅读列表

JEP 401(值对象(预览))已集成到 JDK 28 中。它引入了仅包含 final 字段的无身份类实例,改变了这些对象的 == 运算符行为,并为更扁平、无需分配的 JVM 表示打开了大门。该预览功能默认处于禁用状态,编译和运行时都需要使用 --enable-preview 参数启用。

对于开发者而言,直接影响包括新增的值修饰符、更严格的构造规则、对同步的限制,以及多个基于值的 JDK 类的迁移影响。该提案经过 OpenJDK 的 valhalla-dev 邮件列表多年的讨论形成。

使用值修饰符声明的类是值类;其他所有类仍然是身份类。其实例字段隐式为 final,且每个字段必须在新实例可被观察到之前完成赋值。

code
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;引用类型的字段会递归使用 == 进行比较。

code
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 万名以上高级开发者的社区。查看示例

我们保护您的隐私。