Java 14 record专为不可变数据载体设计,自动声明private final字段、公共访问器、全参构造器及基于字段值的equals/hashCode/toString;但若含可变类型(如List),需通过防御性复制和不可变视图保障真正不可变。

Java 14 引入的 record 关键字,是专为定义不可变数据载体类而设计的语法糖。它天然满足不可变对象的核心要求:字段私有、只读、无修改方法、自动实现 equals/hashCode/toString,且构造过程即初始化完成。
record 自动实现不可变性的关键机制
声明一个 record,例如:
public record Person(String name, int age) {}
编译器会自动生成:
- 私有 final 字段:
private final String name;和private final int age; - 公共全参构造器:
Person(String name, int age),且仅此一个(无法重载无参构造器) - 公共访问器方法:
name()和age()(不是getName(),无setXxx) - 自动重写的
equals、hashCode、toString,基于所有组件字段
如何确保 record 真正不可变(尤其含可变类型时)
record 保证字段引用本身不可变(final),但若字段类型本身可变(如 List、Map、自定义可变类),仍可能被外部修改。需手动防御:
在调试 Java 后端 API 故障时使用此技能,尤其适用于以下任务:分析日志、跨 Controller、Service 和 Mapper 层追踪请求、定位异常根因、检查 SQL 执行或参数流转,以及生成简洁的诊断结论与修复方案。
- 封装可变组件:在构造器中创建防御性副本,不直接保存传入的可变对象
-
返回不可变视图:访问器返回
Collections.unmodifiableList(...)等包装视图 - 避免暴露内部可变状态:不提供任何返回可变对象引用的 getter(除非明确文档说明)
示例:
public record User(String name, List<string> roles) {
public User {
// 防御性复制:防止外部传入的 roles 被后续修改影响 record
this.roles = List.copyOf(roles); // Java 10+ 推荐方式,安全且高效
}
// 返回不可变视图,防止调用方修改
public List<string> roles() {
return Collections.unmodifiableList(roles);
}
}</string></string>
record 的限制与不可变性边界
record 不是万能的“完全不可变”解决方案,需注意以下边界:
-
不能继承其他类(隐式继承
java.lang.Record),也不可被继承(默认final) -
不能定义普通字段(只能有组件字段),也不能在组件字段上加
static或transient - 自定义逻辑必须通过构造器或静态工厂方法:如验证、转换、默认值设置,应在紧凑构造器中完成
- 组件类型自身必须行为得当:若组件是自定义类,该类也应是不可变的,否则 record 整体不可变性失效
何时用 record,何时选传统 class?
record 最适合表达“纯数据、无行为、结构简单”的不可变值对象,比如 DTO、返回值、配置片段、元组等。遇到以下情况,应回归传统 final class:
- 需要定义额外非组件字段(如缓存、懒加载值)
- 需要多种构造方式(如 Builder 模式、不同参数组合)
- 组件间存在复杂约束或业务校验逻辑(超出紧凑构造器合理范围)
- 需实现接口以外的父类,或需序列化定制(record 对
Serializable支持良好,但特殊场景仍需手动控制)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










