java中object类的equals默认比较内存地址,需重写以实现逻辑相等;重写必须满足自反性、对称性、传递性、一致性及对null的正确处理,并同步重写hashcode。

Java中Object类的equals方法默认确实比较的是两个对象的内存地址(即是否为同一个对象实例),这在大多数业务场景下并不符合需求。要让equals按“逻辑相等”判断(比如两个User对象只要id和name相同就视为相等),就必须重写该方法。
重写 equals 的核心原则
必须同时满足以下五点,否则可能引发不可预期的行为(如集合操作异常、哈希表查找失败等):
-
自反性:对任意非空引用
x,x.equals(x)必须返回true -
对称性:对任意非空引用
x和y,x.equals(y)与y.equals(x)结果一致 -
传递性:若
x.equals(y)且y.equals(z)为true,则x.equals(z)也应为true - 一致性:多次调用结果不变(除非参与比较的属性被修改)
-
对null的处理:对任意非空引用
x,x.equals(null)必须返回false
标准重写步骤(以 User 类为例)
假设User有id(Long)、name(String)、age(int)三个字段,只希望用id和name判断相等:
-
先判 null 和类型:
if (obj == null || getClass() != obj.getClass()) return false; -
强转为当前类型:
User other = (User) obj; -
逐字段比较:
- 基本类型用
==(如age) - 引用类型用
Objects.equals(a, b)(自动处理null,推荐)或手动判空后调用.equals() - 避免直接调用
field.equals(...)——如果field为null会抛NullPointerException
- 基本类型用
-
返回最终结果:所有关键字段都相等才返回
true
示例代码:
@Override
public boolean equals(Object obj) {
if (obj == null || getClass() != obj.getClass()) return false;
User user = (User) obj;
return age == user.age &&
Objects.equals(id, user.id) &&
Objects.equals(name, user.name);
}
必须同步重写 hashCode 方法
equals和hashCode必须成对重写。否则放入HashSet、HashMap等集合时会出现“能存不能查”或重复添加的问题。
规则是:如果两个对象equals返回true,它们的hashCode必须相同。
推荐使用Objects.hash(...)生成哈希值,参数顺序需与equals中比较的字段完全一致:
@Override
public int hashCode() {
return Objects.hash(id, name, age);
}
更安全的实践建议
- 使用 IDE(如 IntelliJ IDEA)自动生成
equals和hashCode,它会严格遵循规范并处理边界情况 - 若类是不可变的(final 字段、无 setter),可考虑用 Lombok 的
@EqualsAndHashCode注解简化 - 避免在
equals中访问可能为null的外部资源(如数据库、文件),保持方法纯且高效 - 子类继承父类时,若需扩展比较逻辑,应调用
super.equals(obj)并确保不破坏对称性和传递性
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











