构造代码块不适合数据校验,因其执行过早、无参数上下文、无法抛受检异常;校验应放在构造函数或record的compact constructor中,或由dto注解+controller统一拦截,确保在数据进入边界时立即执行。

构造代码块不适合做任何字段级校验
构造代码块在 JVM 中的执行时机是:对象内存分配 → 字段设默认值(null、0、false)→ 执行构造代码块 → 进入构造方法体。此时:
- 你拿不到构造参数(比如
name、age),只能看到字段当前值——而它们全是默认值,if (name == null)恒为真,毫无业务意义; - 无法抛出受检异常,也不能返回标准错误格式,更无法联动 Spring 的
@Valid或 Hibernate Validator; - 若校验失败后设默认值(如
name = "未知"),等于掩盖问题,让非法输入悄然进入系统。
校验必须发生在构造函数或 record 的 compact constructor 中
只有构造函数能直接访问传入参数,且校验逻辑必须放在字段赋值之前。这是唯一能真正“拒绝非法对象”的窗口。
- 对普通类:校验形参,再赋值。例如:
if (email == null || !email.contains("@")) throw new IllegalArgumentException("邮箱格式错误"); this.email = email; - 对 record:使用显式 compact constructor,并把所有检查写在
this(...)调用前;一旦this()执行,final字段就固化了,无法回退; - 推荐用
Objects.requireNonNull()、Preconditions.checkArgument()等工具,语义清晰、避免手写冗长if块。
DTO 层推荐用注解驱动 + Controller 统一拦截
面向 API 入口的 DTO,不建议在构造逻辑里硬编码校验——应交给框架自动处理,既轻量又标准。
- 字段加
@NotBlank、@Min(1)、@Email等注解;嵌套对象标@Valid实现级联校验; - Controller 方法参数加
@Valid @RequestBody,Spring 自动捕获并返回400 Bad Request和结构化错误体; - 无需手写一行校验代码,规则集中、可复用、支持国际化,也兼容 OpenAPI 文档生成。
来源非 JSON 时(如 Entity → DTO),校验放在映射之后、业务之前
当 DTO 是由 Service 层从数据库实体转换而来,校验时机就不再是创建瞬间,而是映射完成后的“可信输入确认”阶段。
- 用 MapStruct 映射后,在
@Mapper接口中定义默认校验方法,手动调用validator.validate(dto); - 或在 Service 方法内,将 DTO 作为参数传入,用注入的
Validator实例验证; - 避免把校验塞进构造代码块或字段初始化逻辑——那会污染对象模型,也违背单一职责。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











