Java中对象逻辑相等必须重写equals()和hashCode(),因==仅比较引用地址;equals()须满足自反性、对称性、传递性、一致性、非空性五规则,且与hashCode()保持契约:equals为true则hashCode必相等。

Java里对象相等性不能只靠 ==,必须重写 equals() 方法并配套重写 hashCode() —— 否则在集合类(如 HashMap、HashSet)中会出错。
为什么 == 不行?
== 比较的是引用地址,两个内容完全相同但不同实例的对象,== 返回 false。比如:
String a = new String("hello");
String b = new String("hello");
System.out.println(a == b); // false
System.out.println(a.equals(b)); // true
这是语言层面的设计:默认的 Object.equals() 就是用 == 实现的。所以除非你明确要判断“是不是同一个对象”,否则一律不该用 == 判断逻辑相等。
重写 equals() 的五个强制规则
Java规范要求自定义 equals() 必须满足以下五点,否则可能引发 HashMap 查不到、HashSet 重复插入等诡异问题:
立即学习“Java免费学习笔记(深入)”;
- 自反性:
a.equals(a)必须返回true - 对称性:
a.equals(b)为true⇒b.equals(a)也必须为true - 传递性:
a.equals(b)且b.equals(c)⇒a.equals(c) - 一致性:多次调用结果不变(只要对象状态没变)
- 非空性:
a.equals(null)必须返回false,不能抛NullPointerException
最容易破环的是对称性和传递性——比如子类重写 equals() 时直接比较父类字段,又没处理类型检查,就会出问题。
equals() 和 hashCode() 必须成对重写
这是最常被忽略的硬约束。如果只重写 equals() 而不重写 hashCode(),会导致:
-
HashMap中相同逻辑对象被散列到不同桶,get()找不到 -
HashSet允许重复添加“相等”对象 -
HashSet.contains()返回false,即使对象明明存在
核心原则只有一条:两个对象 equals() 返回 true,它们的 hashCode() 必须相等。 反过来不要求(哈希碰撞合法),但相等对象哈希值不同就直接违反契约。
推荐做法:用 Objects.hash(field1, field2, ...) 生成哈希值,和 equals() 里比较的字段严格一致。
IDE 自动生成 vs 手写:哪些地方容易翻车?
IntelliJ / Eclipse 生成的 equals() 大多靠谱,但要注意这几个坑:
- 字段含
null值时,生成代码通常用Objects.equals(a, b),安全;但手写时若用a.equals(b)且a为null,直接 NPE - 继承场景下,生成器可能漏掉
super.equals(other)或类型检查(比如没写if (getClass() != other.getClass()) return false;) - 浮点字段用
Double.compare(a, b) == 0,别用==(NaN == NaN是false) - 集合字段要递归调用
equals(),不能直接list1 == list2
真正复杂的是不可变类或含嵌套对象的类——这时得逐层确认所有参与相等判断的字段都覆盖到了,且没有意外引入可变状态(比如用 Date 字段但没做防御性拷贝)。










