杰瑞科技汇

Java中equals()与==究竟有何区别?

  • :是一个运算符,用于比较两个变量是否指向内存中的同一个对象(对于引用类型)或者两个变量的值是否相等(对于基本数据类型)。
  • equals():是一个方法,它被设计用来比较两个对象的内容(值)是否相等,这个方法的行为是可以被重写的。

下面我们通过详细的解释、示例和最佳实践来彻底搞懂它们。

Java中equals()与==究竟有何区别?-图1
(图片来源网络,侵删)

运算符

的行为取决于你比较的是基本数据类型还是引用类型

a) 比较基本数据类型

当 用于比较 int, double, char, boolean 等基本数据类型时,它比较的是它们的值是否相等

int a = 10;
int b = 10;
System.out.println(a == b); // 输出 true,因为值相等
char c1 = 'A';
char c2 = 65; // 'A' 的 ASCII 码就是 65
System.out.println(c1 == c2); // 输出 true,因为值相等

b) 比较引用类型

当 用于比较对象(引用类型)时,它比较的是这两个引用是否指向堆内存中的同一个对象实例,换句话说,它们是否是“同一个东西”。

// 创建两个 String 对象
String s1 = new String("hello");
String s2 = new String("hello");
System.out.println(s1 == s2); // 输出 false
// 解释:s1 和 s2 是两个不同的对象实例,它们在内存中有不同的地址。
// 创建一个引用,指向 s1 指向的对象
String s3 = s1;
System.out.println(s1 == s3); // 输出 true
// 解释:s1 和 s3 指向了内存中同一个 String 对象。

一个常见的特例:字符串常量池

Java中equals()与==究竟有何区别?-图2
(图片来源网络,侵删)

为了优化性能,Java 对字符串有一个特殊的处理机制——字符串常量池。

// 直接使用双引号创建的字符串,JVM 会先在常量池中查找
String s4 = "hello";
String s5 = "hello";
System.out.println(s4 == s5); // 输出 true
// 解释:因为 "hello" 是一个常量,JVM 只会在常量池中创建一个 "hello" 对象。
// s4 和 s5 都被指向了这个常量池中的同一个对象。

equals() 方法

equals() 方法在 Object 类中定义,其默认实现与 完全相同,也是比较两个对象的内存地址是否相同。

// Object 类中的 equals() 方法默认实现
public boolean equals(Object obj) {
    return (this == obj);
}

绝大多数 Java 类(如 String, Integer, Date, List 等)都重写(Override)equals() 方法,使其能够比较对象的内容(值)是否相等,而不是内存地址。

a) String 类的 equals() 方法

String 类重写了 equals() 方法,用来比较两个字符串对象的字符序列是否完全相同

Java中equals()与==究竟有何区别?-图3
(图片来源网络,侵删)
String s1 = new String("hello");
String s2 = new String("hello");
System.out.println(s1 == s2);      // 输出 false (比较内存地址)
System.out.println(s1.equals(s2));  // 输出 true (比较内容)
String s3 = "hello";
String s4 = "hello";
System.out.println(s3 == s4);      // 输出 true (因为常量池)
System.out.println(s3.equals(s4));  // 输出 true (比较内容)

b) 其他常用类的 equals() 方法

  • Integer, Double 等包装类:比较其包装的基本值是否相等。
    Integer i1 = new Integer(100);
    Integer i2 = new Integer(100);
    System.out.println(i1 == i2);      // 输出 false (不同对象)
    System.out.println(i1.equals(i2));  // 输出 true (值相等)
  • ArrayList, HashMap 等集合类:比较集合的元素和顺序是否完全相同。
    List<String> list1 = new ArrayList<>(Arrays.asList("A", "B"));
    List<String> list2 = new ArrayList<>(Arrays.asList("A", "B"));
    System.out.println(list1 == list2);      // 输出 false
    System.out.println(list1.equals(list2));  // 输出 true

核心区别总结表

特性 运算符 equals() 方法
本质 一个运算符 一个方法
比较对象 基本数据类型 / 引用类型 只能是对象
基本类型:比较是否相等。
引用类型:比较内存地址是否指向同一个对象。
默认比较内存地址(来自 Object 类)。
通常被重写,用于比较(值)是否相等。
可重写性 不可重写 可以被重写
使用场景 比较基本类型变量。
判断两个引用是否指向同一个实例(如单例模式检查)。
判断两个对象在逻辑上是否“相等”。
当你需要比较对象内容时,永远应该使用 equals()

最佳实践与“陷阱”

陷阱:忘记检查 null

当你调用一个对象的 equals() 方法时,如果这个对象是 null,会抛出 NullPointerException

String s = null;
String t = "hello";
// s.equals(t); // 这行代码会抛出 NullPointerException!

安全写法: 总是将常量或非可能为 null 的对象放在 equals() 的左侧。

// 安全写法
"hello".equals(s); // s 是 null,这里返回 false,不会报错

更现代的写法是使用 Objects.equals() 工具方法(Java 7+),它会自动处理 null 的情况。

import java.util.Objects;
// Objects.equals() 内部会处理 null,非常安全
boolean result = Objects.equals(s, t); // s 或 t 有一个为 null,返回 false

陷阱:在需要比较内容时误用

这是最常见的错误,尤其是在比较 StringInteger 时。

// 错误示例:比较用户输入的字符串
Scanner scanner = new Scanner(System.in);
System.out.print("请输入密码: ");
String userInput = scanner.nextLine();
String correctPassword = "123456";
if (userInput == correctPassword) { // 错误!比较的是地址,不是内容
    System.out.println("密码正确");
} else {
    System.out.println("密码错误"); // 几乎总是执行到这里
}
// 正确示例
if (userInput.equals(correctPassword)) { // 正确!比较的是内容
    System.out.println("密码正确");
} else {
    System.out.println("密码错误");
}

自定义类的 equals() 重写(非常重要)

当你创建自己的类时,如果你希望这个类的对象能够根据其内容进行比较(在 HashSetHashMap 中使用),你就必须正确地重写 equals()hashCode() 方法。

重写 equals() 的黄金法则:

  1. 自反性x.equals(x) 必须返回 true
  2. 对称性x.equals(y)y.equals(x) 必须返回相同的值。
  3. 传递性x.equals(y)true,且 y.equals(z)truex.equals(z) 也必须为 true
  4. 一致性:只要对象内容没变,多次调用 equals() 必须返回相同的结果。
  5. 非空性x.equals(null) 必须返回 false

一个简单的 equals() 重写模板:

public class Person {
    private String name;
    private int age;
    // 构造函数、getters 省略...
    @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. 比较关键字段
        // 注意:基本类型用 ==,对象类型用 Objects.equals()
        return age == person.age &&
               Objects.equals(name, person.name);
    }
    // 注意:重写了 equals(),就必须重写 hashCode(),否则在集合中会出问题
    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}
情景 应该使用
比较两个基本数据类型int, double 等)的值
判断两个引用是否指向同一个对象(内存地址相同)
判断两个对象逻辑上是否相等相同) equals()
在不确定对象是否为 null 的情况下比较内容 Objects.equals()

记住这个核心原则: 比的是身份(地址),equals() 比的是内容(值),在处理对象时,除非你有明确的理由要判断它们是否是同一个实例,否则都应该使用 equals() 来比较它们的内容。

分享:
扫描分享到社交APP
上一篇
下一篇