java 14记录类默认不可变,java 16起支持紧凑构造器作为全参构造器的前置钩子,用于统一预处理字段校验与标准化,但不可访问未赋值字段或调用this()。

Java 14 引入的记录类(record)天然适合做不可变数据载体,但默认构造器不支持自定义逻辑。Java 16 起支持紧凑构造器(compact constructor),它在字段赋值前执行,是统一预处理属性的理想位置。
紧凑构造器的本质与触发时机
紧凑构造器不是独立构造器,而是对隐式生成的全参构造器的“前置钩子”。它没有参数列表、不调用 this(...)、也不显式赋值字段——这些由编译器自动完成。你只需写逻辑,编译器会在你代码执行完后,自动将参数按名赋给对应字段。
例如:
record User(String name, int age) {
// 紧凑构造器:无参数,无显式赋值
User {
if (name == null || name.trim().isEmpty()) {
throw new IllegalArgumentException("Name cannot be blank");
}
if (age 150) {
throw new IllegalArgumentException("Age must be between 0 and 150");
}
}
}
统一预处理的典型场景与写法
利用紧凑构造器可集中处理校验、标准化、转换等操作,避免在 getter 或业务层重复分散处理。
-
空值与格式归一化:如将
null名称转为"Anonymous",或对邮箱自动小写化 - 数值范围约束:如金额必须 ≥ 0,状态码限定在枚举范围内,超出则抛异常或截断
-
时间/日期标准化:接收
Instant或字符串,统一转为带时区的ZonedDateTime并设为 UTC -
敏感字段脱敏:如手机号只保留前后两位,中间用
*替代(注意:仅适用于非核心安全场景,生产需更严格方案)
注意事项与常见陷阱
紧凑构造器看似简洁,但有几处关键限制需留意:
- 不能访问
this.name等字段——因为此时字段尚未被赋值,访问会报编译错误 - 不能调用
this(...),也不能声明为private或public;它始终具有与记录相同的访问级别 - 若需复杂初始化(如依赖外部服务、IO),不建议放在这里——紧凑构造器应轻量、纯内存、无副作用
- 若字段类型是泛型或数组,注意防御性拷贝:例如
List<string></string>参数应通过List.copyOf(...)存储,防止外部修改影响不可变性
结合静态工厂方法增强灵活性
紧凑构造器负责“强制性”预处理,而静态工厂方法可封装“可选”转换逻辑,两者互补:
record Email(String address) {
Email {
if (address == null || !address.contains("@")) {
throw new IllegalArgumentException("Invalid email format");
}
}
public static Email of(String input) {
return new Email(input != null ? input.trim().toLowerCase() : "");
}
}
这样既保住了紧凑构造器的校验能力,又通过 Email.of(" USER@EXAMPLE.COM ") 提供了便捷入口。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











