重写 equals 是为了按业务逻辑判断对象相等性,必须遵守自反性、对称性、传递性、一致性及对 null 的处理五项合约,并同步重写 hashcode;推荐用 getclass() 类型检查、objects.equals 比较字段、objects.hash 生成哈希码,避免 instanceof 和可变字段引发的陷阱。

Java 中重写 equals 方法是为了让两个对象在“业务意义上”相等时能被正确识别,而不是仅靠内存地址(即默认的 == 行为)。但重写不当容易引发逻辑错误、哈希冲突甚至集合操作异常,关键在于遵守约定并同步处理 hashCode。
必须遵守的 equals 合约
Java 规定 equals 方法需满足五个基本性质,否则可能破坏集合类(如 HashMap、HashSet)的行为:
-
自反性:任何非 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,不能抛NullPointerException
标准重写模板与关键步骤
以一个 Person 类为例,按规范重写 equals:
-
第一步:判空 + 类型检查
先用
==判断是否为同一引用;再用instanceof或getClass() == obj.getClass()确保类型一致(推荐后者,避免子类误判) -
第二步:强制转换
安全转为当前类型,如
Person other = (Person) obj; -
第三步:逐字段比较
基本类型用
==;引用类型用Objects.equals(a, b)(自动处理 null);数组用Arrays.equals()
示例:
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Person person = (Person) obj;
return age == person.age &&
Objects.equals(name, person.name) &&
Objects.equals(idCard, person.idCard);
}
必须同步重写 hashCode
只要重写了 equals,就一定要重写 hashCode,否则违反“相等对象必须有相同哈希码”的约定,会导致对象在 HashMap 中无法被正确查找或去重。
- 使用
Objects.hash(...)是最安全简洁的方式,参数顺序需与equals中字段比较顺序一致 - 避免在
hashCode中使用可变字段(如后续会修改的属性),否则对象放入哈希集合后可能再也找不到了
对应上面的 Person,hashCode 可写为:
@Override
public int hashCode() {
return Objects.hash(name, idCard, age);
}
常见陷阱与规避建议
实际开发中容易踩坑的地方:
-
只重写 equals 忘了 hashCode:这是最常见错误,尤其在使用
HashSet或作为Map键时表现异常 -
用
instanceof导致不对称:父类equals接受子类实例,但子类equals可能拒绝父类,破坏对称性 -
比较字段不一致:比如
equals比了name和age,hashCode却只用了name,必然出错 -
忽略浮点数精度问题:用
Double.compare(a, b) == 0替代a == b,避免NaN和精度误差
不复杂但容易忽略。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











