重写 equals() 和 hashcode() 是为保障集合行为可预期、逻辑自洽:相等对象哈希码必须相同;equals() 需满足自反性、对称性、传递性、一致性、非空性五契约;hashcode() 必须与 equals() 字段严格一致,推荐 objects.hash() 防 npe;实体类优先按主键 id 判断,值对象可基于全部 final 字段。

重写 equals() 和 hashCode() 不是为了“看起来像对的”,而是要让对象在集合中行为可预期、逻辑自洽。核心就一条:相等的对象,哈希码必须相同;哈希码不同,对象一定不相等。
equals() 必须满足五条契约
否则 HashMap、HashSet 会出错,比如重复存入、查不到值、Set 去重失效。
-
自反性:任何非 null 对象调用
x.equals(x)必须返回true -
对称性:若
x.equals(y)为true,则y.equals(x)也必须为true -
传递性:若
x.equals(y)和y.equals(z)都为true,则x.equals(z)也得是true - 一致性:只要参与比较的字段没变,多次调用结果不能忽真忽假
-
非空性:任何非 null 对象调用
x.equals(null)必须返回false
hashCode() 必须与 equals() 严格对齐
只要 equals() 返回 true,两个对象的 hashCode() 就不能不同——这是硬性要求,不是建议。
- 用哪些字段判断相等,就用哪些字段算哈希码;多一个、少一个、顺序不一致,都会破环一致性
- 推荐用
Objects.hash(f1, f2, f3),它自动处理 null,比手写f1.hashCode() * 31 + f2.hashCode()更安全 - 如果字段可能为 null(如未保存的 JPA 实体 id),不要直接调用
id.hashCode(),否则 NPE;Objects.hash()能兜底
实体类优先按持久化身份设计
对数据库映射实体(如 JPA/Hibernate 场景),别盲目比所有字段。最稳的方式是只比主键 id:
- 新对象(id == null)之间默认不相等,避免临时状态误判
- 已保存对象靠 id 判断逻辑相等,稳定、唯一、不可变
- 不依赖 name/email 等业务字段,规避唯一约束延迟生效带来的风险
- 示例:
return Objects.equals(this.id, other.id) && this.getClass() == other.getClass();
值对象或 DTO 可按值语义设计
如果对象不可变(final 字段、无 setter)、纯数据载体(如响应 VO、参数 DTO),可以基于全部业务字段重写:
- 确保所有参与
equals()的字段都final或至少不会在生命周期中变更 - 关联对象也要小心:避免用
user.getAddress().equals()引发 NPE 或级联加载 - 时间字段建议用
LocalDateTime而非Date,避免时区和毫秒精度干扰
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











