1 equals()方法
Object類中的方法,預設檢測一個對象是否等于另外一個對象,即判斷兩個對象是否具有相同的引用。
public class Employee {
@Override
public boolean equals(Object obj) {
// TODO Auto-generated method stub
return super.equals(obj);
}
}
public class Test {
public static void main(String[] args) {
Employee employee1 = new Employee();
Employee employee2 = new Employee();
Employee employee3 = employee1;
System.out.println(employee1.equals(employee2)); // false
System.out.println(employee1.equals(employee3)); // true
System.out.println(employee3.equals(employee2)); // false
}
}
在實際使用過程中,比較兩個對象的引用往往沒有任何意義,需要比較的是對象的狀态,即對象的屬性是否相等。是以在需要比較類之間是否相等時,都需要重寫equals方法。
public class Employee {
String name;
String salary;
@Override
public boolean equals(Object object) {
if (this == object) return true; // 與自身比較傳回true,同時處理null與null的比較
if (object == null) return false;
if (!(object instanceof Employee))
return false;
Employee employee = (Employee)object;
if (this.name == employee.name) return true;
return false;
}
}
public class Test {
public static void main(String[] args) {
Employee employee1 = new Employee();
employee1.name = "xiaoLi";
Employee employee2 = new Employee();
employee2.name = "xiaoLi";
Employee employee3 = new Employee();
employee3.name = "xiaoZhao";
System.out.println(employee1.equals(employee2)); // true
System.out.println(employee1.equals(employee3)); // false
System.out.println(employee3.equals(employee2)); // false
}
}
如何處理子類與父類之間equals()?
現在有一個子類Manager繼承Employee類,并重寫父類的equals方法:
public class Manager extends Employee {
int age;
@Override
public boolean equals(Object object) {
if (!super.equals(object)) return false;
Manager manager = (Manager) object;
return name == manager.name && age == manager.age;
}
}
但是下面的測試很有意思:
public class Test {
public static void main(String[] args) {
Employee employee1 = new Employee();
Employee employee2 = new Manager();
Manager manager = new Manager();
employee1.name = "xiaoLi";
employee2.name = "xiaoHua";
manager.name = "xiaoLi";
manager.age = 20;
System.out.println(employee1.equals(manager)); // true
System.out.println(manager.equals(employee1)); // 抛異常
}
}
manager.equals(employee1)抛出類不能轉換的異常。我們知道java語言規範要求equals方法具有自反性、對稱性,傳遞性和一緻性;這裡明顯違反了對稱性,即對于任何引用x和y,當且僅當y.equals(x)傳回true,x.equals(y)也應該傳回true。
那麼是哪裡出了問題呢?
子類Manager重寫equlas(),會把object參數轉換成Manager類型的對象,這裡就是父類employee1轉換成子類manager,明顯會報異常,也就是說equals方法沒有校驗到object參數和this不屬于同一個類的情況。即instanceof關鍵詞并不能解決子類的問題。可以把
換成
通過類名進一步判斷兩個類是否相等。但具體的使用還需搭配使用場景。
是以,正确重寫自定義對象的equals方法的步驟:
- 顯式參數類型應該為Object類型,以便覆寫Object類的equals方法;
- 檢測this與顯式參數object是否為同一個引用; if (this == object) return true;
- 檢測object是否為null,如果是則傳回fasle; if (object == null) return false;
-
比較thsi與object是否屬于同一個類;
如果equals的語義在每個子類中有所改變,就使用getClass檢測。
if (getClass() != object.getClass()) return false;
如果所有的子類都擁有統一的語義,就使用instanceOf檢測。
if (!(object instanceOf ClassName)) return false;
-
将object轉換為相應的類類型變量;
ClassName objectName = (ClassName)object;
-
對需要比較的屬性進行比較,基本資料類型用==,對象類型用equals,相等傳回true,否則傳回false.
注意:如果在子類中重新定義equals,則需要包含調用super.equals(other).
2 hashCode()方法
hashCode()方法也是Object類中的一個預設方法,它預設作用是傳回對象的存儲位址,方法傳回一個整型值的散列碼;如果某個對象重寫了equals()方法,如上面Employee類通過name屬性比較兩個employee對象是否相等,由于Employee類沒有重寫hashCode()方法,是以即使employee1.equals(employe2)傳回true,employee1和employee2的hashCode值也不一樣,即在記憶體中可以同時存在,那麼比較的意義也就不存在了。是以,重寫equals()方法同時也需要重寫hashCode()方法,即如果x.equals(y)傳回true,那麼x.hashCode()值必須與y.hashCode()值相同。
看個示例:
public class HashCode {
public static void main(String[] args) {
String str = "china";
StringBuffer sBuffer = new StringBuffer(str);
System.out.println(str.hashCode() + " , " + sBuffer.hashCode());
//result: 94631255 , 366712642
String stri = new String("china");
StringBuffer sBuffer2 = new StringBuffer(stri);
System.out.println(stri.hashCode() + " , " + sBuffer2.hashCode());
//result: 94631255 , 1829164700
}
}
str對象和stri對象的hashCode值相等,這是因為String類重寫了hashCode方法;而sBuffer和sBuffer2的hashCode值不相等,這是因為StringBuffer類繼承Object類預設的hashCode(),傳回的是對象的存儲位址。
是以,如果要比較兩個對象是否相等,除了重寫equals()方法還需要重寫hashCode()方法。注意equal()比較的屬性值與計算hashCode的屬性值要一緻;如Employee類根據name屬性判斷對象是否相等,則hashCode()方法也需要根據name屬性值計算hashCode值。
那麼如何重寫hashCode()方法呢?
hashCode方法應該傳回一個整形數值,也可以是負數,并合理的組合屬性的散列碼,以便能夠讓各個不同的對象産生均勻的散列碼。下面有常用的幾種方法作為參考:
public int hashCode() {
return 7*Objects.hashCode(name)
+ 11*Double.hashCode(height)
+ 13*Integer.hashCode(age);
}
這個方法的好處是當name,height或者age屬性為null時,hashCode值為0.否則傳回參數的hashCode值。
public int hashCode() {
return Objects.hash(name, height, age);
}
組合各屬性的散列碼,計算hashCode值的方法與第一種方法類似。感興趣的可以檢視Objects類的源碼。
3 ==符号
Java中,對于八大基本資料類型,= = 符号比較的是字面值;對于引用資料類型,==符号比較的是引用的位址。