Record是Java 16正式引入的不可变数据载体类,天然适合做DTO:它自动生成构造器、getter、equals、hashCode和toString,语义清晰、线程安全、无样板代码,且字段final保障不可变性。

Record 是什么,为什么它适合做 DTO
Record 是 Java 16 引入的语法糖,本质是编译器生成的不可变数据载体类。它不是运行时新类型,而是让编译器帮你写好构造器、字段、getter、equals、hashCode 和 toString —— 这些正是 DTO 最需要的契约行为。
你不需要手动重写 equals,也不用引入 Lombok;只要字段语义一致,Record 的 equals 就天然高效:它逐字段调用 Objects.equals(a, b),对基本类型直接比较,对引用类型避免空指针(Objects.equals 内部已处理),且不反射、无运行时开销。
定义 Record DTO 的正确写法
Record 声明必须严格匹配字段顺序与类型,否则实例化或序列化会出错:
✅ 正确示例:
public record User(String name, int age, List<string> roles) {
// 可选:静态工厂方法
public static User of(String name, int age) {
return new User(name, age, List.of("USER"));
}
}</string>
⚠️ 容易踩的坑:
- 字段名不能和
record关键字冲突(如record、yield) - 不能在 record 主体中声明同名字段(编译报错:duplicate field)
- 不能继承其他类(record 隐式 final,且已继承
java.lang.Record) - 泛型类型参数需显式写出(如
List),不能只写List(否则擦除后无法参与equals比较)
Record 的 equals 行为细节与边界情况
Record 的 equals 不是简单 ==,也不是靠反射,而是编译期生成的字段级深度比较逻辑:
常见错误现象:
-
User("Alice", 25, null).equals(User("Alice", 25, List.of()))→ false(null vs 空列表) -
User("Alice", 25, List.of("A")).equals(User("Alice", 25, Arrays.asList("A")))→ true(Objects.equals对不同List实现兼容) -
User("Alice", 25, List.of("A")).equals(new Object())→ false(类型检查提前返回)
性能影响:
- 字段越多,
equals调用链越长,但仍是 O(n),无对象创建开销 - 若字段含大数组或深层嵌套 record,
equals会递归展开 —— 注意栈深度和实际耗时 - 没有缓存机制,每次调用都重新计算,不适合高频、相同对象反复比较场景(此时可考虑自定义缓存 hash)
与 Jackson / JSON 序列化的配合要点 Record 默认支持 Jackson 2.12+,但需注意字段访问策略和反序列化限制:
使用场景:
- Spring Boot 2.6+ 默认启用
ParameterNamesModule,能直接反序列化 record 构造器参数 - 若用老版本 Jackson 或自定义 ObjectMapper,需注册
new ParameterNamesModule()
关键配置项:
- 禁止用
@JsonCreator(mode = Mode.DELEGATING):record 不支持委托构造 - 字段名必须与 JSON key 完全一致(默认开启
MapperFeature.USE_STD_BEAN_NAMING时除外) - 若字段是
Optional,Jackson 默认不支持反序列化(会抛InvalidDefinitionException),建议改用 nullable 引用 +@JsonInclude(JsonInclude.Include.NON_NULL)
Record 的不可变性是硬约束:所有字段 final,构造器唯一入口,连 setXxx 都没地方写。这点比 Lombok 的 @Value 更彻底——但也意味着你没法在 record 内部做字段校验(如 age > 0),只能靠构造器逻辑或外部验证。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











