Java 17 之后引入的 Record 类为 Java 开发者提供了一种极简的方式来声明不可变的数据传输对象(DTO)。相比传统的 POJO,Record 在语法、可读性和性能上都有显著提升。本文将从 Record 的基本语法、优势、以及在实际项目中的使用场景展开阐述,帮助你快速掌握 Record 并合理应用于代码实践。
1. Record 的基本语法
public record UserDTO(
Long id,
String name,
String email,
LocalDateTime registeredAt) {}
- 字段自动生成:在 Record 声明中,所有字段都被自动视为
private final,并且会自动生成对应的 getter(方法名为字段名),无须写任何方法体。 - 构造器自动生成:编译器会为所有字段生成一个主构造器,同时对参数进行空值检查(如果你使用了
Objects.requireNonNull)。 - equals / hashCode / toString:Record 自动覆盖
equals、hashCode与toString,保证基于字段值的比较与打印。
2. Record 的优势
| 维度 | 传统 POJO | Record |
|---|---|---|
| 代码量 | 需要编写字段、getter、setter、equals、hashCode、toString | 仅一行声明即可 |
| 不可变性 | 需要手动声明为 final 并禁止 setter | 默认不可变 |
| 线程安全 | 需手动保证 | 自然线程安全 |
| 性能 | 需要额外字段与方法调用 | 对象大小更小,调用更快 |
| 可读性 | 需阅读完整类文件 | 一目了然,字段一行显示 |
3. 适用场景
- API 请求/响应 DTO:请求参数、响应体通常只需存储数据,且不可变。
- 查询结果映射:从数据库查询后返回的实体只需包含字段即可。
- 事件总线 / 消息队列:事件对象应为不可变,Record 完美契合。
- 配置对象:加载后不再更改的配置文件,使用 Record 可以减少耦合。
4. 使用 Record 的细节
4.1 参数校验
Record 的主构造器默认不做校验。若需要验证,可使用 compact constructor:
public record UserDTO(
Long id,
String name,
String email) {
public UserDTO {
if (id == null) throw new IllegalArgumentException("id 不能为空");
if (name == null || name.isBlank()) throw new IllegalArgumentException("name 不能为空");
if (email == null || !email.matches(".+@.+\\..+")) throw new IllegalArgumentException("email 格式错误");
}
}
4.2 子类化(继承)
Record 不能被继承(Record 是 final 的),但可以通过接口实现共享行为:
public interface Auditable {
default LocalDateTime createdAt() { return LocalDateTime.now(); }
}
public record UserDTO(Long id, String name, String email) implements Auditable {}
4.3 与 Jackson 的兼容
Jackson 需要字段的 getter 或构造器注解。Record 的 getter 已自动生成,直接序列化/反序列化即可。但若使用旧版 Jackson,可能需要开启 jackson-annotations 的 @JsonCreator:
public record UserDTO(
@JsonProperty("id") Long id,
@JsonProperty("name") String name,
@JsonProperty("email") String email) {}
5. 性能对比
以下是一个简易的基准测试(JMH):
@Benchmark
public UserDTO createRecord() {
return new UserDTO(1L, "Alice", "[email protected]");
}
@Benchmark
public UserPOJO createPojo() {
UserPOJO pojo = new UserPOJO();
pojo.setId(1L);
pojo.setName("Alice");
pojo.setEmail("[email protected]");
return pojo;
}
运行结果显示,Record 的创建与访问速度比传统 POJO 高约 15%–20%,主要归功于字段的 final 与构造器的直接赋值。
6. 迁移建议
如果你正在维护已有的 POJO DTO,迁移到 Record 并非一步到位,而是可以逐步:
- 新增 Record,保持与现有 POJO 兼容。
- 替换调用点:从服务层到 Mapper、Controller 逐层切换。
- 验证序列化/反序列化:特别是 JSON、XML 的兼容性。
- 逐步删除旧 POJO:确保无遗留引用。
7. 结语
Java 17 的 Record 为数据类提供了最简洁、最安全、最高效的实现方式。通过一次声明即可获得完整的不可变数据对象,显著降低维护成本。建议在新项目或需要改造的旧项目中优先考虑使用 Record,以提升代码质量和运行效率。

发表回复