重写 equals 时必须同时重写 hashcode,否则 hashmap、hashset 中对象会“消失”或重复;equals 返回 true 的对象 hashcode 必须相等,且参与 equals 比较的所有字段也必须参与 hashcode 计算。

重写 equals 时必须同时重写 hashCode
不这么做,对象放进 HashMap、HashSet 就会“消失”或重复——不是 bug,是契约失效。Java 规定:两个对象 equals 返回 true,它们的 hashCode 必须相等;反过来不强制,但不满足会导致哈希表性能退化成链表。
常见错误现象:set.add(obj1); set.contains(obj2) 返回 false,即使 obj1.equals(obj2) 是 true。
- 先写
equals,再用 IDE(如 IntelliJ)自动生成hashCode,别手写——手写极易漏字段或逻辑错位 - 参与
equals比较的**所有字段**,也必须参与hashCode计算;反之,hashCode里多加字段不报错,但违反契约 - 避免在
hashCode中调用可能抛异常或变值的方法(比如懒加载的 getter),否则哈希表操作中途崩溃
equals 的 null 安全和类型检查不能省
直接 other == null || getClass() != other.getClass() 是最简且安全的起点。用 instanceof 看似方便,但在继承场景下会破坏对称性——子类实例 equals 父类实例返回 true,反过来却返回 false。
常见错误现象:new Person("a").equals(new Student("a")) 和 new Student("a").equals(new Person("a")) 结果不一致,违反 equals 合同的对称性。
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
- 用
getClass() != other.getClass()保证类型严格一致,适合大多数不可继承的类(加final更好) - 如果必须支持继承,改用
instanceof+ 双向检查,但极难维护,建议用组合代替继承 - 字段比较优先用
Objects.equals(a, b),它自动处理null,比a == null ? b == null : a.equals(b)简洁可靠
哪些字段该进 equals 和 hashCode
只包含**业务上定义“相等”的字段**。数据库主键、UUID、自然键(如邮箱+手机号组合)可以,但创建时间、更新人、缓存标记、临时计算字段一律排除。
使用场景举例:DTO 传参校验、集合去重、Mockito 断言匹配对象——这些地方都依赖你挑对了字段。
- 忽略 getter 带副作用的字段(比如每次调用都触发远程查询)
- 忽略 volatile 或可能被并发修改的字段,否则
equals结果不稳定 - 忽略非持久化字段(如
transient字段),除非业务明确要求它们参与比较 - 注意浮点数字段:用
Double.compare(a, b) == 0,别用==;或者转成BigDecimal再比
IDE 自动生成后还得人工核对的三件事
IntelliJ 的 Generate → equals() and hashCode() 很快,但默认行为不一定贴合你的模型。
容易踩的坑:生成时勾选了不该比的字段、用了 instanceof、没排除 static 或 final 非业务字段。
- 检查字段列表——删掉日志对象、上下文引用、Spring
@Autowired的 bean - 确认生成逻辑用的是
getClass()判等,不是instanceof - 如果类有父类且父类已重写
equals/hashCode,记得在子类方法里显式调用super.equals()和super.hashCode()
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










