【CoreJava】equals()和==的比较

1.equals()和==是什么?

equals():是方法,定义在超类Object中的一个方法,用来比较两个对象。
==:是操作符,用来比较两个对象。

为什么会将一个操作符和一个方法进行比较呢?
因为它们都是用来比较两个对象的,但它们在用法上又有些区别。
这些区别如果不稍加注意,在开发的过程中就很容易翻车。

个人理解:
==和equals()作用是相同的。
但是equals()是一个方法,可以重写equals()方法,实现不同的作用。比如String类中的eqauls()方法。

2.为什么说它们是一样的?

我们来看一下Object中是如何实现eqauls()方法,代码如下:

public boolean equals(Object obj) {
    return (this == obj);
}

很明显,在源代码中,equals()方法返回的就是使用 == 比较两个对象后的结果。
从这个角度来说,我认为它们的作用是一样的,都是用来比较两个对象的引用的。

3.它们的区别在哪?

最直观的来看,==是操作符,eqauls()是超类Object中的方法,这是最基本的区别。
正是由于这个区别,才有了== 和eqauls()的不同用法。
首先,eqauls()方法是超类Object中的方法,而Java中所有的对象都是继承自Object类的,所以子类是可以重写eqauls()方法而实现不同的功能。
最典型的就是String类中的equals()方法,代码如下:

public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = value.length;
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
     return false;
}

通过代码我们发现,String重写了equals()方法,从引用地址比较变成引用内容的比较。

4.栗子

4.1.第一个栗子:

我们先来比较下基本数据类型:

public static void main(String[] args) {
    int a = 3;
    int b = 3;
    System.out.println(a == b);
}
console:
true

对8种基本数据类型使用==比较。

Tips:首先基本数据类型不是对象,也就不是Object类的子类了,所以没有equals()方法。
但是这好像又违反了Java的“一切都是对象”的准则,所以,Java就给基本数据类型提供了包装类型。

4.2.第二个栗子:

我们来比较下String类型:

public static void main(String[] args) {
    String str1 = "abc";
    String str2 = new String("abc");
    System.out.println(str1 == str2);
    System.out.println(str1.equals(str2));

    String str3 = "abc";
    System.out.println(str1 == str3);
    System.out.println(str1.equals(str3));
}
console:
false
true
true
true

String类型为什么会产生这种结果呢?
首先我们来了解String的两种创建方式:

  • 使用引号创建:创建的对象会存放在字符串缓冲池中。当创建str1时,JVM会在字符串缓冲池中寻找”abc”,没有找到,则创建”abc”,然后将str1指向”abc”。创建str2的过程和创建str1是一样的,只不过此时字符串缓冲池中包含了”abc”,则直接将str2指向”abc”。
  • 使用new创建:使用new来创建对象,存放在堆中,而且每次都会重新创建一个对象。

再结合我们对 == 和equals()的分析,以及String类对equals()的重写,我们很容易就会明白为什么会是这种结果了。

Tips: String类提供了一个intern()的方法。如果对str2使用intern()方法,代码如下:

public static void main(String[] args) {
    String str1 = "abc";
    String str2 = new String("abc");
    str2 = str2.intern();
    System.out.println(str1 == str2);
    System.out.println(str1.equals(str2));
}
console:
true
true

为什么会产生这种结果呢?我们来看下官方是怎么说的。
在String类的源码中,我们找到intern()方法,官方提供了一段注释,其中有一段是这么写的

/* When the intern method is invoked, if the pool already contains a
* string equal to this {@code String} object as determined by
* the {@link #equals(Object)} method, then the string from the pool is
* returned. Otherwise, this {@code String} object is added to the
* pool and a reference to this {@code String} object is returned.
*/

大概意思就是说:调用intern()方法的时候,会首先查找缓冲池中是否有这个字符串,如果有,直接返回;如果没有,则在缓冲池中创建这个字符串,然后返回对象的引用。

4.3.第三个栗子:

我们首先定义一个Cat类

public class Cat {
}

然后我们在方法中进行比较:

public static void main(String[] args) {
    Cat cat1 = new Cat();
    Cat cat2 = new Cat();
    System.out.println(cat1 == cat2);
    System.out.println(cat1.equals(cat2));

    Cat cat3 = cat1;
    System.out.println(cat1 == cat3);
    System.out.println(cat1.equals(cat3));
}
console:
false
false
true
true

首先我们创建了cat1对象,并在堆中为其分配了一块地址,而cat1存储的是指向这块地址的引用。
然后我们创建了cat2对象,和cat1一样,在堆中分配了一块地址,然后cat2存储了指向这块地址的引用。
这样,我们在堆中有了两个Cat类的实例对象。而根据==和equals()的规则,cat1和cat2的比较自然不相同。
在创建cat3的时候,我们直接将cat1赋值给了cat3,则cat3中也是存储了和cat1一样的地址。

5.什么时候用?

==:通常我们用在基础数据类型的比较上,因为基础类型的特殊性,所以可以直接使用==来比较基础数据类型的值。同时我们也可以用来比较两个对象是不是同一个对象。
equasl():我们通常用来比较两个对象,或者用来比较String对象的内容。更重要的是,如果有特殊的业务需求,我们可以重写equasl()方法,来实现我们的目的。

6.结语

在equals()和==的比较中,其实涉及到很多JVM内存的知识。关于内存的问题,我会在后期专门写出来和大家分享的。
关于equals()和==的比较,我已经简单的说完了,但是由于个人水平有限,其中难免会出现不够严谨和错误的地方,希望大家不吝赐教。

你可能感兴趣的:(Java基础)