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