核心区别一句话总结
- :是一个运算符,用于比较两个变量是否引用同一个对象(即它们在内存中的地址是否相同),对于基本数据类型,它比较的是值是否相等。
equals():是一个方法,用于比较两个对象在逻辑上是否“相等”,这个方法的默认行为(继承自Object类)也是比较内存地址,但很多类(如String、Integer、Date等)都重写(Override)了该方法,以实现自定义的“相等”逻辑。
详细对比
为了更清晰地理解,我们分情况讨论。

比较基本数据类型 (Primitive Types)
当 用于比较基本数据类型(如 int, double, char 等)时,它比较的是两个变量的值是否相等。
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的值是10,c的值是20
注意:基本数据类型没有 equals() 方法,如果你尝试在一个基本类型上调用 .equals(),编译器会报错。
// int a = 10; // a.equals(10); // 编译错误: int cannot be dereferenced
比较引用数据类型 (Reference Types)
当 用于比较引用数据类型(如 String, Object, Integer 等)时,它比较的是两个变量是否指向同一个内存地址,也就是判断它们是不是同一个对象。
equals() 方法默认的行为(从 Object 类继承)也是比较内存地址,和 的效果完全一样。

代码示例演示
让我们通过一个完整的例子来直观感受它们的区别。
public class EqualsVsEquals {
public static void main(String[] args) {
// 1. 创建两个内容相同的 String 对象
String s1 = new String("hello");
String s2 = new String("hello");
// 2. 创建一个引用,指向 s1 指向的对象
String s3 = s1;
// --- 使用 == 进行比较 ---
System.out.println("--- 使用 == 比较 ---");
System.out.println("s1 == s2: " + (s1 == s2)); // false, s1和s2是两个不同的对象,内存地址不同
System.out.println("s1 == s3: " + (s1 == s3)); // true, s3和s1指向同一个对象,内存地址相同
// --- 使用 equals() 进行比较 ---
System.out.println("\n--- 使用 equals() 比较 ---");
// String 类重写了 equals() 方法,用于比较字符串内容是否相同
System.out.println("s1.equals(s2): " + s1.equals(s2)); // true, s1和s2的内容都是"hello"
System.out.println("s1.equals(s3): " + s1.equals(s3)); // true, s1和s3的内容相同,且是同一个对象
// 3. 使用字面量创建 String (JVM会优化)
String s4 = "hello";
String s5 = "hello";
System.out.println("\n--- 字面量 String ---");
System.out.println("s4 == s5: " + (s4 == s5)); // true, JVM会使用常量池,相同字面量指向同一个对象
System.out.println("s4.equals(s5): " + s4.equals(s5)); // true, 内容相同
// 4. 创建自定义对象
Person p1 = new Person("张三", 25);
Person p2 = new Person("张三", 25);
Person p3 = p1;
System.out.println("\n--- 自定义对象 Person ---");
// Person 类没有重写 equals(),所以使用 Object 类的默认实现(比较内存地址)
System.out.println("p1 == p2: " + (p1 == p2)); // false, p1和p2是两个不同的对象
System.out.println("p1.equals(p2): " + p1.equals(p2)); // false, 默认equals()也是比较内存地址
System.out.println("p1 == p3: " + (p1 == p3)); // true, p3和p1指向同一个对象
System.out.println("p1.equals(p3): " + p1.equals(p3)); // true
}
}
// 一个简单的Person类,没有重写equals方法
class Person {
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
输出结果分析:
--- 使用 == 比较 ---
s1 == s2: false
s1 == s3: true
--- 使用 equals() 比较 ---
s1.equals(s2): true
s1.equals(s3): true
--- 字面量 String ---
s4 == s5: true
s4.equals(s5): true
--- 自定义对象 Person ---
p1 == p2: false
p1.equals(p2): false
p1 == p3: true
p1.equals(p3): true
深入探讨:equals() 的约定和最佳实践
equals() 方法在 Object 类中定义,Java 官方文档(Object.java)对它提出了一个通用约定,任何重写此方法的类都应遵守:
- 自反性:对于任何非
null的引用值x,x.equals(x)必须返回true。 - 对称性:对于任何非
null的引用值x和y,当且仅当y.equals(x)返回true时,x.equals(y)必须返回true。 - 传递性:对于任何非
null的引用值x、y和z,x.equals(y)返回truey.equals(z)返回true,x.equals(z)也必须返回true。 - 一致性:对于任何非
null的引用值x和y,只要比较操作中用于equals比较的信息没有被修改,多次调用x.equals(y)必须一致地返回true或false。 - 非空性:对于任何非
null的引用值x,x.equals(null)必须返回false。
一个标准的 equals() 重写示例
为了满足上述约定,特别是为了在 HashSet、HashMap 等哈希集合中正确工作,重写 equals() 时通常也需要重写 hashCode() 方法。

import java.util.Objects;
class Person {
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// 正确地重写 equals() 方法
@Override
public boolean equals(Object o) {
// 1. 检查是否是同一个对象的引用
if (this == o) return true;
// 2. 检查参数是否为null,或者是否是同一个类
if (o == null || getClass() != o.getClass()) return false;
// 3. 类型转换
Person person = (Person) o;
// 4. 比较类的“关键字段”
return age == person.age && Objects.equals(name, person.name);
}
// 必须同时重写 hashCode(),以保证“相等的对象必须有相同的哈希码”
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
总结表格
| 特性 | 运算符 | equals() 方法 |
|---|---|---|
| 本质 | 运算符 | 方法 |
| 基本类型:比较值是否相等。 引用类型:比较内存地址(是否为同一个对象)。 |
引用类型:比较对象在逻辑上是否相等,默认比较内存地址。 | |
| 可重写性 | 不可重写。 的行为是固定的。 | 可以重写,类可以自定义“相等”的逻辑。 |
| 使用场景 | 比较基本类型。 需要判断两个引用变量是否指向完全相同的对象时。 |
比较对象的内容是否逻辑相等(如 String 的内容)。在集合框架(如 ArrayList, HashMap)中查找元素时。 |
| 最佳实践 | 对于自定义对象,如果只关心是否为同一个实例,用 。 | 对于自定义对象,如果关心其内容是否相等,必须重写 equals() 和 hashCode()。 |
记住这个黄金法则:
- 比较基本数据类型,用 。
- 比较对象(引用类型),如果你想知道它们是不是同一个东西(同一个内存地址),用 。
- 如果你想知道它们是不是一回事(内容逻辑上相等),用
equals(),对于String等常用类,直接用equals();对于你自己的类,记得重写它(和hashCode())。
