java中判断对象相等必须正确覆盖equals方法并遵守五条契约:自反性、对称性、传递性、一致性和非空性;同时必须同步重写hashcode,且仅在对象作为值使用或用于哈希集合等场景才需覆盖。

Java 中判断两个对象是否“相等”,不能依赖 ==,而应靠正确覆盖的 equals 方法。核心不是“写个比较逻辑就行”,而是遵守一套被 JVM 和集合框架依赖的契约——违反它,轻则结果错乱,重则 HashMap 找不到键、HashSet 重复添加。
必须满足的五条数学契约
这些不是建议,是 Java 规范强制要求。任意一条不满足,就可能在多线程或集合操作中引发隐蔽 bug:
-
自反性:任何非空对象调用
x.equals(x)必须返回true -
对称性:若
x.equals(y)为true,则y.equals(x)也必须为true -
传递性:若
x.equals(y)和y.equals(z)都为true,则x.equals(z)也得为true -
一致性:只要参与比较的字段没变,反复调用
x.equals(y)结果不能变 -
非空性:任何对象调用
x.equals(null)必须返回false(不能抛NullPointerException)
覆盖 equals 的标准四步写法
跳过任意一步都可能破坏契约。推荐按顺序写,逻辑清晰且不易遗漏:
-
先判自身引用:
if (this == obj) return true;(满足自反性,也提升性能) -
再判 null:
if (obj == null) return false;(保障非空性) -
严格类型检查:
if (getClass() != obj.getClass()) return false;(不用instanceof,避免子类误判破坏对称性) -
安全转型 + 字段逐一对比:转型后,基本类型用
==,引用类型统一用Objects.equals(a, b)(自动处理 null)
equals 和 hashCode 必须同步更新
这不是可选项。只要覆盖了 equals,就必须覆盖 hashCode,否则对象放进 HashMap 或 HashSet 后行为不可预测:
- 如果
a.equals(b)返回true,那么a.hashCode()和b.hashCode()必须相等 - 反之不强制——不同对象可以有相同 hash 值,但尽量让它们不同,以提升哈希表性能
- IDE(如 IntelliJ)通常能一键生成两者,确保字段选取一致
哪些场景必须覆盖 equals
不是所有类都需要。只在以下情况才真正需要:
- 你把对象当作“值”来用(比如
Point、Money、Person),关心内容而非身份 - 对象会放进基于哈希或 equals 的集合(
HashSet、HashMap、ArrayList.contains()) - 需要在单元测试中用
assertEquals断言对象逻辑相等 - 框架(如 Jackson、Hibernate)需根据内容识别或合并对象时
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











