重写equals方法必须严格遵守自反性、对称性、传递性、一致性及对null的处理五条契约,并与hashcode协同;需仅比较影响逻辑相等的字段,用objects.equals和arrays.equals安全比较,且hashcode必须基于相同字段计算。

重写 equals 方法不是“写个比较逻辑就行”,而是要让对象在集合、缓存、映射等场景中行为可预测、不崩溃。核心是守住五条契约,并与 hashCode 协同工作。
必须满足的五个基本契约
这些不是建议,是 Java 规范强制要求。违反任意一条,HashSet 可能漏掉元素,HashMap 可能查不到键,调试时极难定位。
-
自反性:任何非 null 对象调用
x.equals(x)必须返回true。代码里第一行if (this == obj) return true;就是保障它。 -
对称性:若
x.equals(y)为true,则y.equals(x)也必须为true。常见破环点是用instanceof做类型检查时,父类实例能接受子类,但子类可能拒绝父类。推荐用getClass() == obj.getClass()避免这个问题。 -
传递性:若
x.equals(y)且y.equals(z)为true,则x.equals(z)也得为true。可变字段(如后期修改了某个参与比较的属性)或使用instanceof+ 继承层次过深都容易破坏它。 -
一致性:只要参与比较的字段没变,多次调用结果必须稳定。不要在
equals里读数据库、调远程接口或依赖随机值、时间戳等外部状态。 -
对 null 的处理:任何对象调用
x.equals(null)必须返回false,不能抛NullPointerException。所以开头要有if (obj == null) return false;或合并进类型检查中。
字段比较要安全且语义准确
不是所有字段都要比,也不是怎么比都行。关键看业务上“什么算相等”。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 只比较影响逻辑相等性的字段,比如
Person类通常比name和idCard,不比createdAt或临时缓存字段。 - 基本类型用
==,引用类型务必用Objects.equals(a, b)—— 它自动处理任一参数为null的情况,避免空指针。 - 数组字段要用
Arrays.equals(arr1, arr2),不能直接用==或Objects.equals(后者只比引用)。 - 避免在
equals中调用可能被子类重写的方法(如 getter),防止运行期行为不可控。
必须同步重写 hashCode
equals 和 hashCode 是一对绑定操作。Java 明确规定:如果两个对象 equals 返回 true,它们的 hashCode 必须相同。
- 不重写
hashCode,对象放进HashSet或当HashMap的 key 时,大概率找不到自己——因为哈希桶位置错了,即使equals判定相等也没机会走到那一步。 - 推荐用
Objects.hash(field1, field2, ...)生成哈希码,简洁、null-safe、符合散列分布习惯。 - 参与
hashCode计算的字段,必须和equals中比较的字段完全一致,一个都不能多,一个都不能少。
推荐写法模板(可直接套用)
以 Person 类为例:
@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);
}
@Override
public int hashCode() {
return Objects.hash(name, age, idCard);
}
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










