构造块不能用于执行前校验,必须在构造方法首行进行参数检查;校验需前置到字段赋值和this()调用之前,覆盖null、空白、范围等业务语义非法值,并配合final字段确保“通过构造即合法”。

构造块本身不能用于“执行前”校验——真正起作用的是在构造方法内部、字段赋值之前完成参数检查。所谓“构造块”,包括实例初始化块和静态初始化块,它们的执行时机固定且不可控:实例块在构造方法体中 super() 调用之后、字段显式赋值之后(若存在)、构造体代码之前运行,天然晚于参数校验所需位置。因此,“构造方法执行前的强一致性检查”实际指:在构造方法的第一行开始处,对传入参数做即时、完备、不可绕过的合法性判断。
校验必须落在构造方法内,且严格前置
Java 不允许在字段赋值或 this() 调用之前插入任意逻辑,但允许你在这些动作之前写校验语句。这是唯一可控、可强制的拦截点:
- ✅ 正确顺序:检查 name 是否为 null 或空白 → 检查 age 是否在 0–150 范围内 → 才执行 this.name = name.trim(); this.age = age;
- ❌ 错误模式:先写 this.name = name; 再 if (name == null) throw … —— 此时字段已赋值,校验失去意义;更糟的是,若 name 是空字符串或全空格,对象已持有一个语义非法的值
- record 类型同理:必须重写 compact constructor,并把所有校验逻辑放在 this() 调用之前;一旦 this() 执行,final 字段即固化,无法撤回
覆盖真实业务场景中的典型非法输入
强一致不是只防 null,而是堵住所有可能破坏业务语义的入口:
- 字符串:用 name == null || name.trim().isEmpty(),而非仅 Objects.requireNonNull(name);避免“ ”这种视觉上为空、逻辑上无效的值入库
- 数值范围:age ≤ 0 或 age > 150 应抛 IllegalArgumentException,消息明确如 "age must be between 1 and 150"
- 集合类:List
tags 若要求非空且元素不为空,需双重校验:tags != null && !tags.isEmpty() && tags.stream().noneMatch(String::isBlank),并通过 List.copyOf(tags) 做防御性拷贝
善用工具方法,保持语义清晰与可维护性
不推荐手写冗长 if 块,应借助成熟工具统一风格:
- JDK 原生:Objects.requireNonNull(param, "param must not be null")
- Apache Commons Lang:StringUtils.isNotBlank(name)
- Guava:Preconditions.checkArgument(age > 0 && age
- 自定义工具类:ValidationUtils.requirePositive(id, "id"),便于全局替换校验策略或接入监控埋点
不可变性与校验是天然搭档
一旦校验通过,就该把值安全地塞进 final 字段——这既是封装原则的要求,也使校验结果真正落地:
- 所有参与校验的字段声明为 private final
- 构造方法中只赋值一次,无 setter、无后续修改可能
- 这样,“通过构造即合法”成为可验证的事实,而非开发者的口头承诺










