《effective java》讀書筆記2(對於所有物件都通用的方法)

狂奔的CD發表於2018-02-26

前言:Object類的所有非final方法(equals,hashCode,toString,clone,finalize)被設計成被覆蓋的,但是它們有明確的通用約定,在覆蓋這些方法時需要遵守,否則會出現和其他類(HashMap,HashSet)配合使用的情況。

第8條:equals的通用約定

1.不需要覆蓋equals的情況:

》類的每個例項唯一,用Object的equals方法即可
》不關心類是否邏輯相等
》從超類繼承過來的equals方法也適合本身
》類是private或者public,別的地方無法訪問,或者確定equals方法不會被呼叫的時候。

2.需要覆蓋equals的清況:

》需要邏輯相等,超類中沒有實現equals方法

3.equals通用約定:

》自反,物件必須等於自身。例如set集合,如果不滿足自反,可能重複新增
》對稱,A.equals(B)且B.equals(A)。書中實現了一個忽略大小寫的CaseInsensitiveString類,讓其可以與普通String類相等,然而反過來String類的equals方法並不能與CaseInsensitiveString類邏輯相等,不滿足自反性
》傳遞,A.equals(B)且B.equals(C),那麼A.equals(C)
》一致,A.equals(B)那麼該句子恆成立
》引數非空,一般首先判斷引數是否instanceof,才進行型別轉換比較。

4.equals最佳實踐

》如果equals代價昂貴,最好加上==檢查是否為本身的引用
》instanceof防止型別錯誤或者null
》接著進行型別轉換
》比較關鍵域

//舉例
class A{
    @Override
    public boolean equals(Object o){
        if(o==this) return true;
        if(!(o instance A)) return false;
        A a = (A)o;
        ...
    }
}

5.注意事項

》覆蓋equals方法時總要覆蓋hashCode
》不要將上例子的equals(Object o)改為equals(A o),這樣看起來減少型別檢查,實際上修改後equals方法會變得很詭異,因為並沒有Override到Object類的equals方法。

第9條:覆蓋equals方法時總要覆蓋hashCode

因為像HashMap,HashTable,HashSet這些基於雜湊的集合需要同時用到equals和hashCode的方法,一般來說,邏輯相等的兩個物件要求hashCode值一樣,不相等的兩個物件hashCode值不要求一定不一樣,但是可以提高效能。

1.如何寫一個好的hashCode方法:

自定義雜湊函式 ,產生的雜湊值,使得邏輯相同的物件有相同的雜湊值(放入同一個桶中),邏輯不同的物件有不同的雜湊值(放入不同的桶中)。

結合equals方法的關鍵域,有如下步驟:

1.把一個非零的常數值,如17,儲存在一個名叫result的int變數中;

2.對於equals方法中的關鍵域產生int型別雜湊值:

boolean型別的域(如名叫f) 計算(f?1:0)

byte,char,short,int型別的域 計算 (int)f

long型別的域 計算 (int)(f^(f>>>32))

float型別的域 計算 Float.floatToIntBits(f)

double型別的域 計算 Double.doubleToLongBits(f)

引用型別的域 使用 引用型別的hashCode方法得到的雜湊值

陣列型別的域 把陣列中的每個元素當成一個關鍵域,計算出雜湊值。

3.對於每一個關鍵域計算出來的雜湊值 (如名叫c)

result = result * 31 + c;

最後放回這個result整數 就是計算出來的當前呼叫hashCode方法得到的雜湊值。

注意不要試圖從雜湊值的計算過程中排除一個關鍵域來提高雜湊值計算的效能。

第10條:始終覆蓋toString方法

主要是列印關鍵資訊方便除錯或日誌記錄

第11條:謹慎的覆蓋clone方法

Object類本身是沒有實現Cloneable介面的,並且clone方法是一個native方法。在預設的情況下,Object類中的clone是返回物件的逐域拷貝的,當然,這必須讓這個類實現了Cloneable介面,如果類不實現這個介面卻呼叫clone介面,就會丟擲CloneNotSupportedException。

所有實現了Cloneable介面的類都應該用一個公有的方法覆蓋clone,此方法首先呼叫super.clone,然後修正任何需要修正的域,例如:

@Override  
public Stack clone() {  
    try {  
        Stack result = (Stack) super.clone();  
        result.elements = elements.clone();  
        return rsult;  
    } catch(CloneNotSupportedException e) {  
        throw new AssertionError();  
    }  

第12條:考慮實現Comparable介面

一旦實現了該介面就可以和許多泛型演算法和支援該介面的集合進行協作。比如排序演算法,或者排序樹,這個用的多。

相關文章