equals() 方法
equals 是什么?
equals() 用来判断两个对象是否「相等」。Object 类的默认实现用 == 比较地址,所以不重写的话,两个对象永远不可能相等(除非是同一个引用)。
实际开发中,我们经常需要判断「内容相等」——比如两个订单号相同的订单被认为是同一个订单。
默认实现
java
public class Object {
public boolean equals(Object obj) {
return (this == obj); // 只是比较地址
}
}重写 equals
基本重写模式
java
class Person {
private String name;
private int age;
@Override
public boolean equals(Object o) {
// 1. 自反性:自己和自己比
if (this == o) return true;
// 2. 非空检查
if (o == null) return false;
// 3. 类型检查(或者用 instanceof)
if (getClass() != o.getClass()) return false;
// 4. 比较属性
Person other = (Person) o;
return age == other.age && Objects.equals(name, other.name);
}
}equals 契约(五条规则)
| 规则 | 含义 | 示例 |
|---|---|---|
| 自反性 | x.equals(x) 必须 true | 自己和自己相等 |
| 对称性 | x.equals(y) 和 y.equals(x) 结果相同 | A==B 则 B==A |
| 传递性 | A==B 且 B==C,则 A==C | 相等关系可传递 |
| 一致性 | 多次调用结果不变 | 状态不变则相等性不变 |
| 非空性 | x.equals(null) 必须 false | 任何对象都不等于 null |
用 instanceof 还是 getClass()?
java
// 方式一:getClass() — 严格相等,子类不兼容
class Person {
@Override
public boolean equals(Object o) {
if (getClass() != o.getClass()) return false;
// ...
}
}
class Employee extends Person { } // Employee.equals(Person) 永远 falsejava
// 方式二:instanceof — 宽松相等,子类也兼容
class Person {
@Override
public boolean equals(Object o) {
if (!(o instanceof Person p)) return false;
// ...
}
}
class Employee extends Person { } // Employee 可以和 Person 比较通常用 getClass() 更安全,避免子类破坏对称性。
常见错误
错误一:只比较部分属性
java
class Point {
private int x, y, z;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Point p = (Point) o;
return x == p.x && y == p.y;
// ❌ 忘记比较 z!三个点的对象会不相等
}
}错误二:不处理 null 属性
java
@Override
public boolean equals(Object o) {
// ❌ 直接调用 name.equals(),如果 name 是 null 会 NPE
return name.equals(((Person) o).name);
}
// ✅ 正确做法:使用 Objects.equals()
return Objects.equals(name, other.name); // 内部已做 null 检查错误三:equals 和 hashCode 不一致
java
class Broken {
private String name;
@Override
public boolean equals(Object o) {
return o instanceof Broken b && name.equals(b.name);
}
@Override
public int hashCode() {
return name.hashCode() + new Random().nextInt(100);
// ❌ hashCode 可能不同,违反约定!
}
}
// 使用时出问题
Broken a = new Broken("test");
Broken b = new Broken("test");
Set<Broken> set = new HashSet<>();
set.add(a);
System.out.println(set.contains(b)); // 可能是 false!HashMap 的噩梦IDE 自动生成
实际开发中,用 IDE 生成 equals 和 hashCode 是标准做法:
java
// IntelliJ: Alt+Insert → equals() and hashCode()
@Data
class Person {
private String name;
private int age;
}IDE 生成的方法通常比手写的更完善。
equals vs ==
java
String a = new String("hello");
String b = new String("hello");
a == b; // false,地址不同
a.equals(b); // true,内容相同
String c = "hello";
String d = "hello";
c == d; // true,字符串常量池复用
c.equals(d); // true,内容相同记住:== 比较地址,equals 比较内容(重写后)。
总结
重写 equals 的检查清单:
- ✅ 用
this == o检查自反性 - ✅ 检查 null
- ✅ 用
getClass()或instanceof检查类型 - ✅ 比较所有「有意义」的属性
- ✅ 使用
Objects.equals()处理可能的 null - ✅ 同时重写 hashCode,保持一致
