1. 程式人生 > >【CoreJava】equals()和==的比較

【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()和==的比較,我已經簡單的說完了,但是由於個人水平有限,其中難免會出現不夠嚴謹和錯誤的地方,希望大家不吝賜教。