杰瑞科技汇

Java中equals与=的根本区别是什么?

核心区别一句话总结

  • :是一个运算符,用于比较两个变量是否引用同一个对象(即它们在内存中的地址是否相同),对于基本数据类型,它比较的是是否相等。
  • equals():是一个方法,用于比较两个对象在逻辑上是否“相等”,这个方法的默认行为(继承自 Object 类)也是比较内存地址,但很多类(如 StringIntegerDate 等)都重写(Override)了该方法,以实现自定义的“相等”逻辑。

详细对比

为了更清晰地理解,我们分情况讨论。

Java中equals与=的根本区别是什么?-图1
(图片来源网络,侵删)

比较基本数据类型 (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 类继承)也是比较内存地址,和 的效果完全一样。

Java中equals与=的根本区别是什么?-图2
(图片来源网络,侵删)

代码示例演示

让我们通过一个完整的例子来直观感受它们的区别。

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)对它提出了一个通用约定,任何重写此方法的类都应遵守:

  1. 自反性:对于任何非 null 的引用值 xx.equals(x) 必须返回 true
  2. 对称性:对于任何非 null 的引用值 xy,当且仅当 y.equals(x) 返回 true 时,x.equals(y) 必须返回 true
  3. 传递性:对于任何非 null 的引用值 xyzx.equals(y) 返回 true y.equals(z) 返回 truex.equals(z) 也必须返回 true
  4. 一致性:对于任何非 null 的引用值 xy,只要比较操作中用于 equals 比较的信息没有被修改,多次调用 x.equals(y) 必须一致地返回 truefalse
  5. 非空性:对于任何非 null 的引用值 xx.equals(null) 必须返回 false

一个标准的 equals() 重写示例

为了满足上述约定,特别是为了在 HashSetHashMap 等哈希集合中正确工作,重写 equals() 时通常也需要重写 hashCode() 方法。

Java中equals与=的根本区别是什么?-图3
(图片来源网络,侵删)
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())。
分享:
扫描分享到社交APP
上一篇
下一篇