在 Java 開發(fā)中,判斷兩個(gè)對象是否「相等」是高頻操作,但很多新手甚至資深開發(fā)者都踩過 == 和 equals() 的坑:明明值一樣卻返回 false,明明是不同對象卻返回 true,到底差在哪?
今天用最通俗的語言+實(shí)戰(zhàn)代碼,把 == 和 equals() 的區(qū)別、使用場景、底層邏輯講透,看完再也不會(huì)用錯(cuò)!
一、核心結(jié)論(先記牢)
-
==:既可以比較基本數(shù)據(jù)類型,也可以比較引用數(shù)據(jù)類型基本類型:比較值是否相等
引用類型:比較對象的內(nèi)存地址是否相等
-
equals():是Object類的方法,只能比較引用數(shù)據(jù)類型未重寫時(shí):和
==作用一樣,比較內(nèi)存地址重寫后(如
String、Integer):比較對象的內(nèi)容是否相等
一句話總結(jié):== ** 看地址,重寫后 ** equals() ** 看內(nèi)容**。
二、逐點(diǎn)拆解+代碼實(shí)戰(zhàn)
1. 基本數(shù)據(jù)類型:只用 ==
Java 中的基本類型(byte、short、int、long、float、double、char、boolean),沒有對象概念,只能用 == 比較值。
equals() 是對象方法,基本類型不能用!
public class Test {
public static void main(String[] args) {
int a = 10;
int b = 10;
double c = 10.0;
// == 比較值,完全相等
System.out.println(a == b); // true
System.out.println(a == c); // true(int自動(dòng)轉(zhuǎn)double,值相等)
}
}
2. 引用數(shù)據(jù)類型:== 比較內(nèi)存地址
引用類型(對象、數(shù)組、字符串等),== 比較的是兩個(gè)變量指向的是不是同一個(gè)內(nèi)存地址,而非內(nèi)容。
public class Test {
public static void main(String[] args) {
// 兩個(gè)新的User對象,地址不同
User user1 = new User("張三");
User user2 = new User("張三");
// == 比較地址,false
System.out.println(user1 == user2);
}
}
// 自定義類
class User {
String name;
public User(String name) {
this.name = name;
}
}
輸出:false
原因:new 關(guān)鍵字會(huì)在堆內(nèi)存創(chuàng)建新對象,user1 和 user2 是兩個(gè)不同的內(nèi)存地址,== 直接判定不相等。
3. equals():未重寫 = 比較地址
所有 Java 類默認(rèn)繼承 Object 類,Object 中的 equals() 底層就是 ==,沒重寫時(shí)和 == 完全一樣。
Object 類源碼(JDK1.8):
public boolean equals(Object obj) {
return (this == obj);
}
用上面的 User 類測試:
// 未重寫equals(),比較地址
System.out.println(user1.equals(user2)); // false
結(jié)果還是 false,因?yàn)榇藭r(shí) equals() 本質(zhì)還是 ==。
4. 重寫equals():比較內(nèi)容
實(shí)際開發(fā)中,我們需要判斷對象內(nèi)容是否相等(比如兩個(gè)用戶姓名相同就視為同一人),這時(shí)候必須**手動(dòng)重寫 ** equals()。
重寫 User 類的 equals():
重點(diǎn)規(guī)范:重寫 equals() 時(shí),hashCode() **必須同時(shí)重寫 **!這是 Java 規(guī)范要求,否則會(huì)導(dǎo)致 HashMap、HashSet 等集合類無法正常工作(集合判斷元素是否重復(fù),會(huì)先比較 hashCode,再比較 equals)。
class User {
String name;
public User(String name) {
this.name = name;
}
// 重寫equals():比較name內(nèi)容
@Override
public boolean equals(Object obj) {
// 1. 地址相同,直接返回true
if (this == obj) return true;
// 2. 為空或類型不同,返回false
if (obj == null || getClass() != obj.getClass()) return false;
// 3. 強(qiáng)制轉(zhuǎn)換,比較內(nèi)容
User user = (User) obj;
return name.equals(user.name);
}
// 必須同步重寫hashCode(),與equals()邏輯一致
@Override
public int hashCode() {
// 以name為基準(zhǔn)生成hash值,確保equals()相等的對象,hashCode()也相等
return Objects.hash(name);
}
}
再次測試:
User user1 = new User("張三");
User user2 = new User("張三");
System.out.println(user1 == user2); // false(地址不同)
System.out.println(user1.equals(user2)); // true(內(nèi)容相同)
5. 經(jīng)典案例:String 類型
String 是 Java 最常用的類,**已經(jīng)默認(rèn)重寫了 ** equals(),專門用來比較字符串內(nèi)容,這是最容易踩坑的地方!
public class Test {
public static void main(String[] args) {
String s1 = "hello";
String s2 = "hello";
String s3 = new String("hello");
// 1. == 比較地址
System.out.println(s1 == s2); // true(字符串常量池,地址相同)
System.out.println(s1 == s3); // false(s3是new的新對象,地址不同)
// 2. equals() 比較內(nèi)容
System.out.println(s1.equals(s2)); // true
System.out.println(s1.equals(s3)); // true
}
}
重點(diǎn):字符串比較equals() **優(yōu)先用 **(需比較內(nèi)容時(shí));若明確需要判斷兩個(gè)字符串是否指向同一個(gè)對象(比較內(nèi)存地址),也可使用 ==,但這種場景極少,開發(fā)中需謹(jǐn)慎使用。
三、一張表總結(jié)區(qū)別
| 比較方式 | 適用類型 | 比較內(nèi)容 | 備注 |
|---|---|---|---|
== |
基本類型+引用類型 | 基本類型:值;引用類型:地址 | 速度快,不看內(nèi)容 |
equals() |
僅引用類型 | 默認(rèn):地址;重寫后:內(nèi)容 | 自定義對象必須重寫,且需同步重寫hashCode() |
四、開發(fā)避坑指南
字符串比較:優(yōu)先用
equals()(比較內(nèi)容);若需判斷是否為同一個(gè)對象(比較地址),可使用==(場景極少,如單例對象判斷)基本類型比較:只能用
==,equals()會(huì)報(bào)錯(cuò)自定義對象:需要比較內(nèi)容時(shí),
equals()hashCode()**必須重寫 ,且必須同步重寫 **(規(guī)范要求:equals() 相等的對象,hashCode() 必須相等;hashCode() 相等的對象,equals() 不一定相等)空指針判斷:調(diào)用
equals()前,先判斷對象非空(推薦用Objects.equals(a,b),JDK1.7+)
// 安全的equals()寫法,避免空指針
Objects.equals(user1, user2);
總結(jié)
==:基本類型比值,引用類型比內(nèi)存地址;equals():默認(rèn)比地址,重寫后比內(nèi)容(String、包裝類已重寫);開發(fā)口訣:基本類型用 ==,對象內(nèi)容用 equals(),對象地址用 ==。
關(guān)鍵點(diǎn)回顧
==引用類型比地址,equals()重寫后比內(nèi)容字符串、自定義對象比較優(yōu)先用 equals()(需比較內(nèi)容時(shí));若需比較內(nèi)存地址,仍可使用
==基本類型只能用
==,equals()不支持基本類型優(yōu)先使用
Objects.equals()避免空指針異常,自定義對象重寫equals()必須同步重寫hashCode()