還在亂用?Java 中 == 和 equals() 的終極區(qū)別

在 Java 開發(fā)中,判斷兩個(gè)對象是否「相等」是高頻操作,但很多新手甚至資深開發(fā)者都踩過 ==equals() 的坑:明明值一樣卻返回 false,明明是不同對象卻返回 true,到底差在哪?

今天用最通俗的語言+實(shí)戰(zhàn)代碼,把 ==equals() 的區(qū)別、使用場景、底層邏輯講透,看完再也不會(huì)用錯(cuò)!

一、核心結(jié)論(先記牢)

  1. ==:既可以比較基本數(shù)據(jù)類型,也可以比較引用數(shù)據(jù)類型

    • 基本類型:比較值是否相等

    • 引用類型:比較對象的內(nèi)存地址是否相等

  2. equals():是 Object 類的方法,只能比較引用數(shù)據(jù)類型

    • 未重寫時(shí):和 == 作用一樣,比較內(nèi)存地址

    • 重寫后(如 StringInteger):比較對象的內(nèi)容是否相等

一句話總結(jié):== ** 看地址,重寫后 ** equals() ** 看內(nèi)容**。


二、逐點(diǎn)拆解+代碼實(shí)戰(zhàn)

1. 基本數(shù)據(jù)類型:只用 ==

Java 中的基本類型(byte、shortint、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)建新對象,user1user2 是兩個(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ā)避坑指南

  1. 字符串比較:優(yōu)先用 equals()(比較內(nèi)容);若需判斷是否為同一個(gè)對象(比較地址),可使用 ==(場景極少,如單例對象判斷)

  2. 基本類型比較:只能用 ==,equals() 會(huì)報(bào)錯(cuò)

  3. 自定義對象:需要比較內(nèi)容時(shí),equals()hashCode() **必須重寫 ,且必須同步重寫 **(規(guī)范要求:equals() 相等的對象,hashCode() 必須相等;hashCode() 相等的對象,equals() 不一定相等)

  4. 空指針判斷:調(diào)用 equals() 前,先判斷對象非空(推薦用 Objects.equals(a,b),JDK1.7+)


// 安全的equals()寫法,避免空指針
Objects.equals(user1, user2);

總結(jié)

  1. ==:基本類型比,引用類型比內(nèi)存地址;

  2. equals():默認(rèn)比地址,重寫后比內(nèi)容(String、包裝類已重寫);

  3. 開發(fā)口訣:基本類型用 ==,對象內(nèi)容用 equals(),對象地址用 ==。


關(guān)鍵點(diǎn)回顧

  1. == 引用類型比地址,equals() 重寫后比內(nèi)容

  2. 字符串、自定義對象比較優(yōu)先用 equals()(需比較內(nèi)容時(shí));若需比較內(nèi)存地址,仍可使用 ==

  3. 基本類型只能用 ==equals() 不支持基本類型

  4. 優(yōu)先使用 Objects.equals() 避免空指針異常,自定義對象重寫 equals() 必須同步重寫 hashCode()

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容