核心区别一句话总结
- :是一个运算符,用于比较两个变量(或表达式)的值是否相等,对于基本数据类型,比较的是值;对于引用数据类型,比较的是内存地址(引用)是否相同。
equals():是一个方法,存在于Object类中,它的默认行为与 一致,即比较两个对象的内存地址,很多类(如String、包装类、集合类等)重写(override)了equals()方法,使其比较的是(值)是否相等。
运算符
的行为取决于它所比较的操作数的类型。

a) 比较基本数据类型
当 用于比较基本数据类型(如 int, double, char, boolean 等)时,它比较的是两个变量的值是否相等。
int a = 10; int b = 10; int c = 20; System.out.println(a == b); // 输出: true,因为 a 和 b 的值都是 10 System.out.println(a == c); // 输出: false,因为 a 和 c 的值不同
b) 比较引用数据类型
当 用于比较引用数据类型(如 String, Object, 自定义类等)时,它比较的是两个对象的内存地址(或称为引用)是否相同,换句话说,它判断的是两个变量是否指向了同一个对象。
// 创建两个 String 对象
String s1 = new String("hello");
String s2 = new String("hello");
String s3 = s1; // s3 指向了 s1 所指向的对象
System.out.println(s1 == s2); // 输出: false
// 解释:s1 和 s2 是两个不同的对象,它们在内存中的地址不同。
System.out.println(s1 == s3); // 输出: true
// 解释:s1 和 s3 指向了同一个对象,它们的内存地址相同。
equals() 方法
equals() 方法在 Object 类中定义,其默认实现就是使用 来比较两个对象的引用。
// Object 类中 equals 方法的默认实现
public boolean equals(Object obj) {
return (this == obj);
}
如果你不重写 equals() 方法,那么对于任何自定义类,equals() 的行为和 是完全一样的。

a) 默认行为(来自 Object 类)
class Person {
String name;
public Person(String name) {
this.name = name;
}
}
public class Test {
public static void main(String[] args) {
Person p1 = new Person("Alice");
Person p2 = new Person("Alice");
Person p3 = p1;
System.out.println(p1.equals(p2)); // 输出: false
// 解释:p1 和 p2 是两个不同的对象,equals 默认比较引用,结果与 p1 == p2 相同。
System.out.println(p1.equals(p3)); // 输出: true
// 解释:p1 和 p3 指向同一个对象,equals 默认比较引用,结果与 p1 == p3 相同。
}
}
b) 重写后的行为(以 String 类为例)
String 类重写了 equals() 方法,使其比较的是字符串的(即字符序列)是否相同,而不管它们是否是同一个对象。
String s1 = new String("hello");
String s2 = new String("hello");
System.out.println(s1 == s2); // 输出: false (比较内存地址)
System.out.println(s1.equals(s2)); // 输出: true (比较字符串内容)
c) 其他常见类的 equals() 实现
-
包装类 (Integer, Double 等):重写了
equals(),用于比较包装的基本值是否相等。Integer i1 = new Integer(10); Integer i2 = new Integer(10); System.out.println(i1 == i2); // 输出: false (不同对象) System.out.println(i1.equals(i2)); // 输出: true (值相等)
注意:对于
Integer,有一个[-128, 127]的缓存池,在此范围内的对象 比较也为true,但这是一种特殊优化,不应依赖此特性进行内容比较。 -
集合类 (ArrayList, HashMap 等):
equals()通常用于比较集合的大小和元素是否相同。List<String> list1 = new ArrayList<>(Arrays.asList("A", "B")); List<String> list2 = new ArrayList<>(Arrays.asList("A", "B")); System.out.println(list1.equals(list2)); // 输出: true
一个非常重要的约定:hashCode()
当你重写 equals() 方法时,必须同时重写 hashCode() 方法,这是一个非常重要的约定,如果不遵守,会导致在使用哈希集合(如 HashMap, HashSet)时出现严重问题。
为什么?
因为 HashMap 和 HashSet 的工作原理是:
- 计算对象的
hashCode()来确定它应该被存放在哪个“桶”(bucket)里。 - 如果两个对象的
hashCode()相同(发生了哈希冲突),它们会被放在同一个桶里。 - 这时,
HashMap会使用equals()方法来比较这两个对象,以确定它们是否是同一个对象,还是仅仅是哈希冲突。
违反约定的后果:
如果你只重写了 equals() 而没有重写 hashCode(),那么两个内容相同的对象可能会因为 hashCode() 不同而被存放在不同的桶里,导致 HashSet 认为它们是两个不同的元素,或者 HashMap 无法正确找到已存在的键。
equals() 和 hashCode() 的约定:
a.equals(b)返回true,a.hashCode()必须等于b.hashCode()。a.equals(b)返回false,a.hashCode()和b.hashCode()不要求一定不同,但为了提高哈希表的性能,最好也让他们不同。
总结与最佳实践
| 特性 | 运算符 | equals() 方法 |
|---|---|---|
| 本质 | 运算符 | 方法 |
| - 基本类型:比较值 - 引用类型:比较内存地址 |
默认比较内存地址,但可被重写以比较 | |
| 能否重写 | 不能 | 可以,而且很多类都重写了 |
| 使用场景 | - 比较基本类型值 - 判断两个引用是否指向同一个对象 |
- 判断两个对象的内容(逻辑上)是否相等 - 比较对象内容时,应始终使用 equals() |
最佳实践
- 比较基本数据类型:永远使用 。
- 比较引用数据类型:
- 如果你关心的是两个变量是否指向同一个对象(用于检查单例、缓存等),使用 。
- 如果你关心的是两个对象在逻辑上是否相等(两个
User对象的用户名和密码是否相同),使用equals()。
- 字符串比较:永远使用
equals()或更安全的equalsIgnoreCase(),为了防止NullPointerException,推荐使用"hello".equals(s1)而不是s1.equals("hello")。 - 自定义类:如果类的实例需要被用于基于内容的比较(作为
HashMap的键或放入HashSet),你必须同时重写equals()和hashCode(),现代IDE(如IntelliJ IDEA)可以一键生成这两个方法的正确实现。
