深刻探究Java中equals()和==的區別是什麼

「判斷兩個事物是否相等」,是編程中最多見的操做之一,在Java中,判斷是否相等有兩種方法,一種是使用「==」判斷符,另外一種是使用「equals()」方法,你是否曾因混用兩者致使難以想象的bug?本篇文章將帶你深刻兩者背後的判斷原理。java

相等判斷符"=="

"=="相等判斷符用於比較基本數據類型和引用類型數據。 當比較基本數據類型的時候比較的是數值,當比較引用類型數據時比較的是引用(指針)。程序員

"=="判斷基本類型數據

基本數據類型指的是Java中的八大數據類型:byte,short,int,long,float,double,char,boolean編程

這八大基本數據類型有個共同的特色是它們在內存中是有具體值的, 好比說一個 int 類型的數據"2",它在8位數據總線的機器上保存形式爲 0000 0010。(8位機器是假設的)數組

當使用 == 比較兩個基本數據類型的時候, 就是在比較它們各自在內存中的值。大數據

爲了照顧到要刨根問底的同窗,再補充一下兩個數值是怎麼比較的:cpu 在比較的時候會將兩個值做差,而後查看標誌寄存器。標誌寄存器存放的是運算的結果,裏面有一個是否爲0的標誌位,若是該位爲1,證實兩者之差爲0,兩者相等。this

"=="判斷引用類型數據

引用數據類型在字面上也是很好理解的, 它就是一個引用, 指向堆內存中一個具體的對象。spa

好比說Student stu = new Student(); 這裏的 stu 就是一個引用,它指向的是當前 new 出來的 Student 對象. 當咱們想要操做這個 Student 對象時, 只須要操做引用便可, 好比說int age = stu.getAge();操作系統

因此用"=="判斷兩個引用數據類型是否相等的時候,其實是在判斷兩個引用是否指向同一個對象設計

看下面的示例:指針

public static void main(String[] args) {
    String s1 = "hello";	//s1指向字符串常量池中的"hello"字符串對象
    String s2 = "hello";	//s2也指向字符串常量池中的"hello"字符串對象
    System.out.println(s1 == s2);   //true

    String s3 = new String("hello");   //s3指向的是堆內存中的字符串對象 
    System.out.println(s1 == s3);	//false
}
複製代碼

從上面的例子能夠看到,因爲引用"s1"和"s2"指向的都是常量池中的"hello"字符串,因此返回true。(後面我會發布一篇詳細講述Java字符串的文章,涉及字符串初始化和字符串常量池等知識)

而"s3"指向的是新建立字符串對象,由於只要動用了new關鍵字, 就會在堆內存建立一個新的對象。

也就是說 s1 和 s3 指向的是不一樣的字符串對象,因此返回false。

相等判斷方法equals()

equals()和 == 有着本質的區別,== 能夠看做是對「操做系統比較數據手段」的封裝,而equals()則是每一個對象自帶的比較方法,它是Java自定義的比較規則。

equals()和 == 的本質區別更通俗的說法是:==的比較規則是定死的,就是比較兩個數據的值。

而 equals() 的比較規則是不固定的,能夠由用戶本身定義。

看下面的例子:

public static void main(String[] args) {
    String s1 = "hello";
    String s3 = new String("hello");    
    System.out.println(s1.equals(s3));	//true
}
複製代碼

回想前面的案例:用 == 比較的時候, 上面 s1 和 s3 比較出的結果爲false。而當用 equals() 比較的時候,得出的結果爲 true。

想知道緣由咱們還得看源碼,下面是 String 類中的 equals() 方法的源碼。

public boolean equals(Object anObject) {
    if (this == anObject) {	//先比較兩個字符串的引用是否相等(是否指向同一個對象), 是直接返回true
        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() 方法時,首先會判斷兩個字符串的引用是否相等,也就是說兩個字符串引用是否指向同一個對象,是則返回true。

若是不是指向同一個對象,則把兩個字符串中的字符挨個進行比較。因爲 s1 和 s3 字符串都是 "hello",是能夠匹配成功的,因此最終返回 true。

思考:爲何要設計equals()方法?

經過上面的講解,相信你已經知道 == 和 equals() 的區別了:一個的比較規則是定死的,一個是能夠由編程人員本身定義的。

但是爲何會有 equals() 方法, 並且還能夠被自由定製呢?

這個問題要落到Java語言的核心 —— 面向對象思想了。

Java 不一樣於面向過程的C語言,Java是一款面向對象的高級語言。若是是面向過程編程,直接操做內存上存儲的數據的話,用 == 所定義的規則來判斷兩個數據是否相等已經足夠了。

而Java中萬物皆對象,咱們常常要面臨的問題是這兩個對象是否相等,而不是這兩串二進制數是否相等,僅有 == 是徹底不夠用的。

因爲Java程序員們會建立各類知足它們業務需求的對象,系統沒法提早知道兩個對象在什麼條件下算相等,Java乾脆把判斷對象是否相等的權力交給編程人員

具體的措施是:全部的類都必須繼承 Object 類,而 Object 類中寫有equals()方法。編程人員能夠經過重寫 equals() 方法來實現本身的比較策略,也能夠不重寫,使用Object類的equals()比較策略。

//Object類中的equals()方法源碼
public boolean equals(Object obj) {
    return (this == obj);
}
複製代碼

從 Object 類的 equals() 源碼能夠看到,若是編程人員沒有顯示地重寫 equals() 方法,則默認比較兩個引用是否指向同一個對象。

補充: 關於基本數據類型包裝類的比較

因爲 Java 中萬物皆對象,就連基本數據類型也有其對應的包裝類,那麼它們對應的比較策略是什麼呢?

public static void main(String[] args) {
    int a = 3;
    Integer b = new Integer(3);
    System.out.println(b.equals(a));	//true, 自動裝箱
}
複製代碼

從上面的代碼能夠看到儘管兩個引用不一樣, 可是輸出的結果仍爲 true, 證實 Integer 包裝類重寫了 equals() 方法,追蹤其源碼:

//Integer類中的equals方法
public boolean equals(Object obj) {
    if (obj instanceof Integer) {
        return value == ((Integer)obj).intValue();
    }
    return false;
}
複製代碼

從源碼看到,基本類型包裝類在重寫equals()後,比較的仍是基本數據類型的值。

結束

經過探索 == 和 equals() 的區別,咱們摸清楚了兩者別後的比較策略,同時也對 Java 中 equals() 方法的設計進行了思考,相信你們在從此的 Java 編程實戰中不會再爲相等判斷而煩惱了。

相關文章
相關標籤/搜索