Java Record规范构造函数能否在反序列化或反射时被绕过?
Java Record构造函数绕开与不变量维护问题
先看示例代码,我们通过record的规范构造函数维护不变量:
public record User(String name) { public User { if (name == null || name.isBlank()) { throw new IllegalArgumentException("Invalid name"); } } }
常规实例化、反射调用Constructor::newInstance都会触发这个构造函数的校验逻辑。
关于反序列化的机制,标准流程的说明并没有明确是否能完全绕过构造函数。于是尝试用Unsafe做底层探究:
Unsafe unsafe = ...; // 通过反射获取 User user = (User) unsafe.allocateInstance(User.class); System.out.println(user.name());
已知allocateInstance不会调用构造函数,接下来针对三个疑问逐一解答:
问题1:这段Unsafe代码对record是否有效?JVM会阻止吗?
- 代码完全有效,JVM不会阻止这种方式创建record实例。
Unsafe是JVM提供的底层工具,它直接跳过所有对象初始化逻辑(包括构造函数执行、字段默认值赋值),直接在堆上分配内存空间。不管是普通类还是record,只要是合法的Java类,allocateInstance都能直接生成实例。
问题2:如果可行,是否会绕过规范构造函数,违反record的不变量?
- 必然会绕过规范构造函数,且直接违反record的不变量。
- 用
allocateInstance创建的User实例,name字段会是默认值null,完全不符合规范构造函数中"name不能为null或空白"的约束。 - Java语言规范中,record的设计初衷是让所有实例都满足规范构造函数维护的不变量,但
Unsafe属于底层"逃逸舱",不受普通语言规则约束,确实能破坏这个约定。
问题3:常见序列化框架(Jackson、Java Serialization)是否依赖类似机制,还是必须调用规范构造函数?
Java Serialization
- Java标准序列化处理record时必须调用规范构造函数。
- record默认实现
Serializable接口,序列化时会写入所有组件字段;反序列化时会调用规范构造函数,传入从流中读取的字段值,因此构造函数里的校验逻辑会正常生效,不会出现不符合不变量的实例。
Jackson
- Jackson处理record的默认行为是调用规范构造函数。
- 反序列化时,Jackson会自动匹配组件字段与构造函数参数,通过规范构造函数创建实例,你的
name校验逻辑会被触发。 - 虽然Jackson支持特殊配置(比如用
@JsonCreator指定其他构造函数),但默认情况下不会绕开规范构造函数。
内容的提问来源于stack exchange,提问作者Yaksh
相关产品推荐
相关产品推荐

