Java中的equals()與==的區(qū)別與用法
在Java開發(fā)中有有一個看似簡單,但是在網(wǎng)上有大量關(guān)于話題和問題,就是equals() 和 == 操作符有什么區(qū)別
- ==: 操作符用于比較兩個對象的地址是否相等
- equals(): 方法用于比較兩個對象的內(nèi)容是否相等
今日內(nèi)容介紹,大約花費9分鐘
圖片
為了更好地理解這個區(qū)別,讓我們看一個例子:
String str1 = new String("Hello");
String str2 = new String("Hello");
System.out.println(str1.equals(str2)); // 輸出 true
System.out.println(str1 == str2); // 輸出 false
例子中,雖然兩個字符串的內(nèi)容相同,但它們在內(nèi)存中的地址是不同的。因此,使用.equals()方法比較它們的內(nèi)容會返回true,而使用"=="操作符比較它們的地址會返回false
1. 重寫.equals()方法
學習過Java基礎的,應該知道Java所有類都默認繼承Obejct類,Object類中有一個.equals()方法
public boolean equals(Object obj) {
return (this == obj);
}
從代碼大家可以發(fā)現(xiàn).equals()方法默認采用==操作符比較,如果子類沒有重寫equals()方法,那么就使用==操作符和equals()方法結(jié)果完全一樣--用于比較兩個對象內(nèi)存地址是否相等。
但是實際情況是,有很多類重寫equals()方法,這是因為內(nèi)存地址比較要求比較嚴格,不太符合現(xiàn)實中所有的場景需求,比如String類,進行比較時,大多只想判斷內(nèi)容是否相等,并不太想知道內(nèi)存地址是否相等(是否是一個對象)。
并且在深入理解Java字符串常量池文章中我們已經(jīng)知道Java虛擬機為了減少內(nèi)存開銷和提高性能特意給字符串開辟一塊空間-----字符串常量池
即推薦使用 String s = "Hello" 這種形式來創(chuàng)建字符串對象,而不是通過 new 關(guān)鍵字的方式,因為 new 需要在堆上開辟一塊新的空間。
1.1. String類的equals()方法
Jdk11的String類的equals()方法
public boolean equals(Object anObject) {
//如果是同一個對象(即兩個引用指向內(nèi)存中的同一塊地址),則直接返回true
if (this == anObject) {
return true;
}
//如果是String類型的實例
if (anObject instanceof String) {
//Object類型的對象強制轉(zhuǎn)換為String類型
String aString = (String)anObject;
//如果當前字符串對象和傳入的字符串對象的編碼方式相同
if (coder() == aString.coder()) {
//如果當前字符串和傳入的字符串都是Latin1編碼,則調(diào)用StringLatin1類的equals方法進行比較;如果其中一個或兩個字符串是UTF16編碼,則調(diào)用StringUTF16類的equals方法進行比較
return isLatin1() ? StringLatin1.equals(value, aString.value)
: StringUTF16.equals(value, aString.value);
}
}
return false;
}
特別說明:Latin1(也稱為ISO 8859-1)和UTF-16(Unicode轉(zhuǎn)換格式16位)是兩種不同的字符編碼方式
Latin1和UTF-16雖然是兩種編碼方式,但是差別不大,就拿 UTF-16 的來的equals()方法來看
@HotSpotIntrinsicCandidate
public static boolean equals(byte[] value, byte[] other) {
if (value.length == other.length) {
int len = value.length >> 1;
for (int i = 0; i < len; i++) {
if (getChar(value, i) != getChar(other, i)) {
return false;
}
}
return true;
}
return false;
}
注意:Java8和Java11的equals()方法源碼是有區(qū)別的JDK8的equals()方法
public boolean equals(Object anObject) {
// 如果是同一個對象(即兩個引用指向內(nèi)存中的同一塊地址),則直接返回true
if (this == anObject) {
return true;
}
// 如果是String類型的實例
if (anObject instanceof String) {
////Object類型的對象強制轉(zhuǎn)換為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;
}
1.2. 示例說明
示例一:
new String("hello").equals("hello")
輸出結(jié)果是什么?
String類的equals方法比較的是字符串對象的內(nèi)容是否相等,因為都是"Hello",所以結(jié)果是true
示例二:
new String("hello") == "hello";
輸出結(jié)果是什么?
==操作符比較的對象地址是否相等,==左邊是堆中創(chuàng)建對象,右邊是字符串常量池對象,雖然內(nèi)容相等,但是地址不相等,所以結(jié)果是false
示例三:
new String("hello") == new String("hello");
輸出結(jié)果是什么?
new 出來的對象肯定是完全不同的內(nèi)存地址,所以結(jié)果是false
示例四:
"hello" == "h"+"ello"
輸出結(jié)果是什么?
h和ello都在字符串常量池,所以編譯器在遇到+操作符的時候?qū)⑵渥詣觾?yōu)化為hello,所以結(jié)果是true
示例五:
new String("hello").intern() == "hello"
輸出結(jié)果是什么?
new String("hello") 在執(zhí)行的時候,會先在字符串常量池中創(chuàng)建對象,然后再在堆中創(chuàng)建對象;執(zhí)行 intern() 方法的時候發(fā)現(xiàn)字符串常量池中已經(jīng)有了‘hello’這個對象,所以就直接返回字符串常量池中的對象引用了,那再與字符串常量池中的‘hello’比較,所以結(jié)果是true
深入解析 String.intern()已經(jīng)介紹過原因
2. 其他方法比較
除了.equals()方法和"=="操作符外,還有一些其他比較方法可以使用:
- 1.Objects.equals()方法:這個靜態(tài)方法可以用于比較兩個對象是否相等,不需要在調(diào)用之前判斷對象是否為空。
Objects.equals("Hello", new String("Hello")); // 返回 true
- 2.String類的.contentEquals()方法:這個方法可以用于比較字符串與任何字符序列(如StringBuffer、StringBuilder、String、CharSequence)是否相等。
String str = "Hello";
StringBuffer buffer = new StringBuffer("Hello");
System.out.println(str.contentEquals(buffer)); // 輸出 true