值对象是ddd中无id、不可变、以属性值定义相等性的业务概念单元;需严格实现不可变性(final字段、不可变类型、无setter)、重写equals/hashcode(仅基于业务属性)、内聚业务行为,并常嵌入实体持久化。

值对象在领域驱动设计(DDD)中不是简单的数据容器,而是一种以业务语义为中心、靠属性值定义身份、且拒绝状态变更的设计单元。Java 中实现它,核心不在语法技巧,而在对“不变性”的严格贯彻和对“相等性逻辑”的重新定义。
不可变性不是加个 final 就完事
仅把字段声明为 final 只是基础;真正的不可变性要求:
- 所有字段必须是
final,且类型本身也应是不可变的(如用String而非StringBuilder,用LocalDateTime而非可变的Date) - 构造过程必须完整初始化,不提供任何 setter、builder 的 mutable 方法或暴露内部可变对象引用(避免防御性拷贝缺失)
- 若含集合类属性(如地址中的街道列表),必须使用
ImmutableList或返回不可修改视图(Collections.unmodifiableList())
相等性必须重写,且只看值,不看引用
Java 默认的 equals() 和 hashCode() 基于内存地址,这与值对象的本质冲突。必须显式重写:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
-
equals()应逐个比较所有业务相关字段(忽略无关元数据,如日志字段) -
hashCode()必须与equals()保持一致:属性值相同 → hashCode 相同 - 推荐用 Lombok 的
@Value注解(自动加final、toString、equals、hashCode),但需确认其生成逻辑符合业务语义(例如是否包含所有关键字段)
封装的是业务概念,不是字段堆砌
一个叫 Money 的类,如果只存 amount 和 currency,还远远不够:
- 应内聚常见业务行为:比如
plus(Money other)返回新Money,而不是修改自身 - 应在构造时做基本验证(如金额不能为负、币种不能为空),失败则抛出
IllegalArgumentException - 命名和结构要让领域专家一眼看懂:“我们用
DeliveryWindow表示配送时间段”,而不是TimeRangeDto
它天然适合嵌入式持久化
因为无 ID、不可变、生命周期依附于实体,值对象在数据库中通常不单独建表:
- JPA/Hibernate 中可用
@Embeddable+@Embedded映射到所属实体的同一张表 - EF Core 中对应
OwnsOne()配置 - 序列化时可直接扁平为 JSON 字段(如 MongoDB 的嵌套文档),无需额外关联查询
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










