java继承中的 equals + hashCode+toString
【0】README
0.1)本文轉自 core java volume 1, 旨在理清 equals + hashCode方法;
0.2) 特別說明: 在java中, 只有基本類型不是對象, 例如,數值, 字符和布爾類型的值都不是對象; 但是所有的數組類型,不管是對象數組還是基本類型的數組都擴展于Object類;
0.3) 最后,我們還補充了對 toString 方法的描述;
【1】equals方法
1.1) Object中的 equals 方法用于檢測一個對象是否等于另外一個對象;(在Object類中, 這個方法比較的是內存地址, 判斷的是兩個對象是否具有相同引用)
1.2)看個荔枝:
Hint)
- H1)為了防備name 或 hireDay 可能為null的情況: 需要使用 Objects.equals 方法;如果兩個參數都為 null, Objects.equals(a, b) 返回 true; 如果其中一個為null, 則返回 false; 否則,兩個參數都不為null, 則調用 a.equals(b);(注意是Objects not Object)
- H2)利用這個方法, Employee.equals 方法的最后一條語句要改寫為:
1.3)在子類中定義 equals 方法時, 首先調用超類的 equals; 如果檢測失敗,對象就不可能相等, 如果超類中的域都相等, 就需要比較子類中的實例域;
1.4)java語言規范要求 equals 具有以下特性:
- 自反性:非空引用 x,x.equals(x) 應該返回true;
- 對稱性:非空引用x 和 y, x.equals(y) 返回ture, 那么 y.equals(x) 也應該返回ture;
- 傳遞性:對于 非空引用x 、y 和 z, x.equals(y) 返回ture, 那么 y.equals(z) 也應該返回ture, 則 x.equals(z) 也應該返回 true;
- 一致性:如果x 和 y 引用的對象沒有發生變化, 反復調用 x.equals(y) 應該返回同樣的結果;
- 對于任意非空應用x, x.equals(null) 應該返回false;
1.5)出現的問題+解決方法
- 當子類和父類對象分別作為equals 的隱式參數,導致不滿足對稱性的情況: e.equals(m), 這里的e是一個 Employee對象,m是一個 Manager對象,并且兩個對象具有相同的屬性值;如果在 Employee.equals中用 instanceof 檢測,就會返回 true, 然而這意味著反過來調用: m.equals(e) 也需要返回true; 對稱性不允許這個方法調用返回 false, 或者拋出異常;猛然間,讓人感覺到 instanceof 并不是那么完美;
- 下面從兩個截然不同的情況看一下這個問題:
- 1)如果子類能夠擁有自己的相等概念, 則對稱性需要將強制采用getClass 進行檢測;
- 2)如果由超類決定相等的概念, 那么就可以使用 instanceof 進行檢測, 這樣可以在不同子類的對象間進行相等的比較;
【2】下面給出編寫一個完美的 equals 方法的建議:
- 2.1)顯式參數命名為 otherObject, 稍后需要將它轉換為另一個叫做 other的變量;
- 2.2)檢測this 與 otherObject 是否引用同一個對象:
if(this == otherObject) return true; 實際上, 這是一種經常采用的形式, 因為計算這個等式要比一個一個地比較類中的域所付出的代價小得多; - 2.3)檢測otherObject 是否為 null, 如果為 null ,則返回 false, 這項檢測很有必要:
if(otherObject == null) return false; - 2.4)比較this 與 otherObject 是否屬于同一個類: 如果equals 的語義在每個子類中有所改變,就是用 getClass 進行檢測:if(getClass() != otherObject.getClass()) return false;
- 2.5) 將 otherObject 轉換為 相應的類類型變量:
ClassName other = (ClassName)otherObject; - 2.6)現在開始對所有需要比較的域進行比較了:
使用 == 比較基本類型域, 使用 equals比較對象域, 如果所有的域都匹配返回 true, 否則返回 false;
return field1 == other.field1 && Objects.equals(field2, other.field2) && ……;
如果在子類中重新定義 equals, 就要在其中包含 調用 super.equals(other);
Hint)對于數組類型的域, 可以使用靜態的Arrays.equals 方法檢測相應的 數組元素是否相等;
Alert)看個荔枝(下面是實現 equals 方法的一種常見的錯誤):
代碼分析(Analysis):
- A1)這個方法聲明的顯式參數類型是 Employee, 其結果并沒有 覆蓋 Object類的 equals 方法,而是定義了一個完全無關的方法;
- A2)為了避免發生類型錯誤, 可以使用 @Override 對覆蓋超類的方法進行標記;
- A3)如果出現了錯誤,并且正在定義一個新方法,編譯器就會給出 錯誤報告;如, 假設將下面的聲明添加到 Employee類中, 就會看到一個錯誤報告, 這是因為這個方法并沒有覆蓋超類Object 中的 任何方法:
【3】hashCode 方法
3.1)定義:散列碼是由對象導出的一個整型值, 散列碼沒有規律的;如,x和y 是兩個不同的對象, x.hashCode() 和 y.hashCode() 基本上不會相同的;
3.2)String 類的 hashCode 散列碼:
- 3.2.1)String類通過下列算法計算散列碼:
3.2.2) hashCode方法定義在了 Object類, 因此每個對象都有一個默認的散列碼, 其值為對象的存儲地址:
對以上打印結果的分析(Analysis):- A1)字符串s 和 t 擁有相同的散列碼, 這是因為字符串的散列碼是由內容導出的, 而字符串緩沖sb 和 tb 卻有著不同的散列碼,這是因為 在StringBuffer 類中沒有定義hashCode 方法,它的散列碼是有 Object類的默認 hashCode 方法,以便用戶可以 將對象插入到散列表中;
- A2) hashCode方法應該返回一個整型數值,并合理的組合實例域的散列碼, 以便能夠讓各個不同的對象產生的散列碼更加均勻;
3.3)看個荔枝, 下面是 Employee類 的 hashCode方法:
3.4)還可以在java7中做兩個改進(improvement):
- I1) 最好使用 null 安全 的方法 Objects.hashCode , 如果參數為null, 這個方法會返回0, 否則返回對參數調用 hashCode的結果;
- I2)還有一個方法: 需要組合多個散列值, 可以調用 Objects.hash 并提供多個參數,這個方法會對各個參數調用 Objects.hashCode, 并組合這些散列值; 如 Employee.hashCode 的方法可以簡寫為:
Attention)
- A1) equals 方法與 hashCode 的定義必須一致, 如果x.equals(y) 返回 true, 那么 x.hashCode() 就必須與 y.hashCode() 具有相同的值;
- A2) 如, 如果用定義的Employee.equals 比較雇員的Id, 那么 hashCode就需要散列Id, 而不是 雇員的 姓名或存儲地址;
Hint)如果存在數組類型的域, 那么可以使用靜態的 Arrays.hashCode 方法計算一個散列碼, 這個散列碼由數組元素的散列碼組成;
【4】toString方法
4.1)隨處可見toString 方法的原因是: 只要對象與一個字符串通過操作符 + 連接起來,java編譯器就會自動地調用 toString 方法, 以便獲得這個對象的字符串描述;
Hint)在調用它 x.toString()的地方可以用 “”+x替代, 這條語句將一個空串與 x 的字符串表示相連接, 這里的x就是 x.toString();
4.2)Object定義了toString()方法,用來打印輸出對象所屬的類名和散列碼;如,
Warning)
- W1)數組繼承了 Object類的 toString方法, 數組類型將按照舊格式打印;
- W2)修正的方式是調用靜態方法 Arrays.toString
- W3)要想打印多維數組, 需要調用 Arrays.deepToString 方法;
總結
以上是生活随笔為你收集整理的java继承中的 equals + hashCode+toString的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 电脑在正常使用中突然蓝屏电脑在正常使用中
- 下一篇: 电脑开机进系统出现蓝屏的解决方法电脑一开