杰瑞科技汇

Java中=与equals区别,赋值与内容判断有何不同?

核心区别一句话总结

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

运算符

的行为取决于它所比较的操作数的类型。

Java中=与equals区别,赋值与内容判断有何不同?-图1
(图片来源网络,侵删)

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() 的行为和 是完全一样的。

Java中=与equals区别,赋值与内容判断有何不同?-图2
(图片来源网络,侵删)

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)时出现严重问题。

为什么?

因为 HashMapHashSet 的工作原理是:

  1. 计算对象的 hashCode() 来确定它应该被存放在哪个“桶”(bucket)里。
  2. 如果两个对象的 hashCode() 相同(发生了哈希冲突),它们会被放在同一个桶里。
  3. 这时,HashMap 会使用 equals() 方法来比较这两个对象,以确定它们是否是同一个对象,还是仅仅是哈希冲突。

违反约定的后果: 如果你只重写了 equals() 而没有重写 hashCode(),那么两个内容相同的对象可能会因为 hashCode() 不同而被存放在不同的桶里,导致 HashSet 认为它们是两个不同的元素,或者 HashMap 无法正确找到已存在的键。

equals()hashCode() 的约定:

  1. a.equals(b) 返回 truea.hashCode() 必须等于 b.hashCode()
  2. a.equals(b) 返回 falsea.hashCode()b.hashCode() 不要求一定不同,但为了提高哈希表的性能,最好也让他们不同。

总结与最佳实践

特性 运算符 equals() 方法
本质 运算符 方法
- 基本类型:比较
- 引用类型:比较内存地址
默认比较内存地址,但可被重写以比较
能否重写 不能 可以,而且很多类都重写了
使用场景 - 比较基本类型值
- 判断两个引用是否指向同一个对象
- 判断两个对象的内容(逻辑上)是否相等
- 比较对象内容时,应始终使用 equals()

最佳实践

  1. 比较基本数据类型:永远使用 。
  2. 比较引用数据类型
    • 如果你关心的是两个变量是否指向同一个对象(用于检查单例、缓存等),使用 。
    • 如果你关心的是两个对象在逻辑上是否相等(两个 User 对象的用户名和密码是否相同),使用 equals()
  3. 字符串比较:永远使用 equals() 或更安全的 equalsIgnoreCase(),为了防止 NullPointerException,推荐使用 "hello".equals(s1) 而不是 s1.equals("hello")
  4. 自定义类:如果类的实例需要被用于基于内容的比较(作为 HashMap 的键或放入 HashSet),你必须同时重写 equals()hashCode(),现代IDE(如IntelliJ IDEA)可以一键生成这两个方法的正确实现。
分享:
扫描分享到社交APP
上一篇
下一篇