關於equals()和==操作的一點理解

經常會碰到一些==跟equals()方法的誤用的例子,有時候自己也說不準就弄混了,特地查了些資料寫個小小的總結;
首先大致聲明一下默認的情況Object的==操作跟equals()是等效的;有些同志會說我錯了,會說==操作是比較是否同
一對象的,equals()是比較兩個對象內容是否相等的;這樣的說法可以說對了一小半,錯了一大半;
首先我們來看下Object對象的equals()方法是怎麼實現的:
    public boolean equals(Object obj) {
    return (this == obj);
    }
 如何?這裏已經很明顯的告訴大家Object的==操作跟equals()是等效的吧.那有朋友就會問那怎麼實現讓equals()跟==操作不一樣呢?
 怎麼讓equals()方法比較兩個對象的內容呢?
 答案就是重寫equals()方法;下面需要提到重寫equals()方法要注意的幾點規範:
 自反reflexive:    x.equals(x)  
 對稱symmetric:    x.equals(y)=>y.equals(x)
 傳遞transitive:    x.equals(y),<code>y.equals(z) =>x.equals(z)
 穩定consistent:    x.equals(y) 每次調用的時候總是確定的返回true or false
 非空        x.equals(null) return false
 從屬於==操作    x==y=> x.equals(y) return true

 還有非常重要的一點:改寫equals()方法後,必須也要改寫hashCode()方法; x.equals(y) => x.hashCode()==y.hashCode();
 上面解釋了一堆關於重寫equals()方法的原則,現在我們就倒過頭來看看一些已經重寫了equals()方法的具體的例子;首先拿String類型來考察
 //*************override equals()****************************
 public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = count;
        if (n == anotherString.count) {
        char v1[] = value;
        char v2[] = anotherString.value;
        int i = offset;
        int j = anotherString.offset;
        while (n-- != 0) {
            if (v1[i++] != v2[j++])
            return false;
        }
        return true;
        }
    }
    return false;
    }
//*************override hashCode()****************************
    public int hashCode() {
    int h = hash;
    if (h == 0) {
        int off = offset;
        char val[] = value;
        int len = count;

            for (int i = 0; i < len; i++) {
                h = 31*h + val[off++];
            }
            hash = h;
        }
        return h;
    }
    String類型通過重寫了equals(),hashCode()方法實現了String對象的內容比較;基本類型的對象型比如Integer,Long,Float等類型都
    實現了自己的equals()方法;正因爲基本類型都實現了對象的內容比較,所以經常給我們一種想當然的equals()是比較對象內容的錯誤觀點.
    如果我們自己寫的類要實現內容比較我們就需要自己重寫equals()跟hashCode()方法,關於hashCode()的重寫建議參考下String的處理方式,
    要儘量避免內容不相等的對象產生相同的hashCode()的錯誤,否則就會在hashTable中碰到散列值重複的問題,這個按下不表.

    最後額外的講一下String的==的問題.經常碰到別人問String str="abc";String str2=new String("abc") String str3="abc"中有幾個對象的無聊問題.
    這裏我給出我自己的解釋共str,str2,str3,"abc", new String("abc")五個對象.其中str,str2,str3是reference對象存於JVM的STACK中,"abc"是一個內存對象容納
    了"abc"這個數據,存在於JVM的String池中(str,str3指向的"abc"是同一內存區--String池中的"abc"),new String("abc")是一個內存對象容納了"abc"這個數據,存在
    於JVM的Heap 堆中;
    按照上面的解釋我們可以明確知道str != str2,因爲他們在內存中是兩個對象(一個在STRING池中一個在HEAP堆中), str == str3因爲他們指向的是String池中的同一對象;
    我們通過str="abc";str3="abc"實例化時,JVM會檢查String池中是否已經存在"abc"對象,如果有那麼就把引用指向已經存在的"abc";否則就在String池中新建立一個.
    我們通過str2=new String("abc")實例化時,首先這裏分配的內存跟STRING池是沒有任何關係的,無論HEAP堆和STRING池中有無"abc" JVM都會在HEAP堆裏新建立一個
    "abc"對象;

 

 

檢查對象是否相等
關係運算符==和!=也適用於所有對象,但它們的含義通常會使初涉Java領域的人找不到北。下面是一個例子:


  Equivalence {
     main(String[] args) {
    Integer n1 =  Integer(47);
    Integer n2 =  Integer(47);
    System.out.println(n1 == n2);
    System.out.println(n1 != n2);
  }
} 


其中,表達式System.out.println(n1 == n2)可打印出內部的布爾比較結果。一般人都會認爲輸出結果肯定先是true,再是false,因爲兩個Integer對象都是相同的。但儘管對象的內容相同,句柄卻是不同的,而==和!=比較的正好就是對象句柄。所以輸出結果實際上先是false,再是true。這自然會使第一次接觸的人感到驚奇。
若想對比兩個對象的實際內容是否相同,又該如何操作呢?此時,必須使用所有對象都適用的特殊方法equals()。但這個方法不適用於“主類型”,那些類型直接使用==和!=即可。下面舉例說明如何使用:


  EqualsMethod {
     main(String[] args) {
    Integer n1 =  Integer(47);
    Integer n2 =  Integer(47);
    System.out.println(n1.equals(n2));
  }
} 


正如我們預計的那樣,此時得到的結果是true。但事情並未到此結束!假設您創建了自己的類,就象下面這樣:


 Value {
   i;
}

  EqualsMethod2 {
     main(String[] args) {
    Value v1 =  Value();
    Value v2 =  Value();
    v1.i = v2.i = 100;
    System.out.println(v1.equals(v2));
  }
} 


此時的結果又變回了false!這是由於equals()的默認行爲是比較句柄。所以除非在自己的新類中改變了equals(),否則不可能表現出我們希望的行爲。不幸的是,要到第7章纔會學習如何改變行爲。但要注意equals()的這種行爲方式同時或許能夠避免一些“災難”性的事件。
大多數Java類庫都實現了equals(),所以它實際比較的是對象的內容,而非它們的句柄。

發佈了2 篇原創文章 · 獲贊 0 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章