超類(lèi) Object 中有這個(gè) equals() 方法,該方法主要用于比較兩個(gè)對(duì)象是否相等。該方法的源碼如下:
public boolean equals(Object obj) {
return (this == obj);
}
我們知道所有的對(duì)象都擁有標(biāo)識(shí)(內(nèi)存地址)和狀態(tài)(數(shù)據(jù)),同時(shí)“==”比較兩個(gè)對(duì)象的的內(nèi)存地址,所以說(shuō)使用 Object 的 equals() 方法是比較兩個(gè)對(duì)象的內(nèi)存地址是否相等,即若 object1.equals(object2) 為 true,則表示 equals1 和 equals2 實(shí)際上是引用同一個(gè)對(duì)象。雖然有時(shí)候 Object 的 equals() 方法可以滿足我們一些基本的要求,但是我們必須要清楚我們很大部分時(shí)間都是進(jìn)行兩個(gè)對(duì)象的比較,這個(gè)時(shí)候 Object 的 equals() 方法就不可以了,實(shí)際上 JDK 中,String、Math 等封裝類(lèi)都對(duì) equals() 方法進(jìn)行了重寫(xiě)。下面是 String 的 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;
}
對(duì)于這個(gè)代碼段:if (v1[i++] != v2[j++])return false;我們可以非常清晰的看到 String 的 equals() 方法是進(jìn)行內(nèi)容比較,而不是引用比較。至于其他的封裝類(lèi)都差不多。
在 Java 規(guī)范中,它對(duì) equals() 方法的使用必須要遵循如下幾個(gè)規(guī)則:
equals 方法在非空對(duì)象引用上實(shí)現(xiàn)相等關(guān)系:
1、自反性:對(duì)于任何非空引用值 x,x.equals(x) 都應(yīng)返回 true。
2、對(duì)稱(chēng)性:對(duì)于任何非空引用值 x 和 y,當(dāng)且僅當(dāng) y.equals(x) 返回 true 時(shí),x.equals(y) 才應(yīng)返回 true。
3、傳遞性:對(duì)于任何非空引用值 x、y 和 z,如果 x.equals(y) 返回 true,并且 y.equals(z) 返回 true,那么 x.equals(z) 應(yīng)返回 true。
4、一致性:對(duì)于任何非空引用值 x 和 y,多次調(diào)用 x.equals(y) 始終返回 true 或始終返回 false,前提是對(duì)象上 equals 比較中所用的信息沒(méi)有被修改。
5、對(duì)于任何非空引用值 x,x.equals(null) 都應(yīng)返回 false。
對(duì)于上面幾個(gè)規(guī)則,我們?cè)谑褂玫倪^(guò)程中最好遵守,否則會(huì)出現(xiàn)意想不到的錯(cuò)誤。
在 java 中進(jìn)行比較,我們需要根據(jù)比較的類(lèi)型來(lái)選擇合適的比較方式:
1) 對(duì)象域,使用 equals 方法 。
2) 類(lèi)型安全的枚舉,使用 equals 或== 。
3) 可能為 null 的對(duì)象域 : 使用 == 和 equals 。
4) 數(shù)組域 : 使用 Arrays.equals 。
5)除 float 和 double 外的原始數(shù)據(jù)類(lèi)型 : 使用 == 。
6) float 類(lèi)型: 使用 Float.foatToIntBits 轉(zhuǎn)換成 int 類(lèi)型,然后使用==。
7) double 類(lèi)型: 使用 Double.doubleToLongBit 轉(zhuǎn)換成 long 類(lèi)型,然后使用==。
至于6)、7)為什么需要進(jìn)行轉(zhuǎn)換,我們可以參考他們相應(yīng)封裝類(lèi)的 equals() 方法,下面的是 Float 類(lèi)的:
public boolean equals(Object obj) {
return (obj instanceof Float)
&& (floatToIntBits(((Float)obj).value) == floatToIntBits(value));
}
原因嘛,里面提到了兩點(diǎn):
However, there are two exceptions:
If f1 and f2 both represent
Float.NaN, then the equals method returns
true, even though Float.NaN==Float.NaN
has the value false.
If <code>f1 represents +0.0f while
f2 represents -0.0f, or vice
versa, the equal test has the value
false, even though 0.0f==-0.0f
has the value true.
我們?cè)诟矊?xiě) equals() 方法時(shí),一般都是推薦使用 getClass 來(lái)進(jìn)行類(lèi)型判斷,不是使用 instanceof。我們都清楚 instanceof 的作用是判斷其左邊對(duì)象是否為其右邊類(lèi)的實(shí)例,返回 boolean 類(lèi)型的數(shù)據(jù)。可以用來(lái)判斷繼承中的子類(lèi)的實(shí)例是否為父類(lèi)的實(shí)現(xiàn)。注意后面這句話:可以用來(lái)判斷繼承中的子類(lèi)的實(shí)例是否為父類(lèi)的實(shí)現(xiàn),正是這句話在作怪。我們先看如下實(shí)例(摘自《高質(zhì)量代碼 改善 Java 程序的 151 個(gè)建議》)。
父類(lèi):Person
public class Person {
protected String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Person(String name){
this.name = name;
}
public boolean equals(Object object){
if(object instanceof Person){
Person p = (Person) object;
if(p.getName() == null || name == null){
return false;
}
else{
return name.equalsIgnoreCase(p.getName ());
}
}
return false;
}
}
子類(lèi):Employee
public class Employee extends Person{
private int id;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Employee(String name,int id){
super(name);
this.id = id;
}
/**
* 重寫(xiě)equals()方法
*/
public boolean equals(Object object){
if(object instanceof Employee){
Employee e = (Employee) object;
return super.equals(object) && e.getId() == id;
}
return false;
}
}
上面父類(lèi) Person 和子類(lèi) Employee 都重寫(xiě)了 equals(),不過(guò) Employee 比父類(lèi)多了一個(gè)id屬性。測(cè)試程序如下:
public class Test {
public static void main(String[] args) {
Employee e1 = new Employee("chenssy", 23);
Employee e2 = new Employee("chenssy", 24);
Person p1 = new Person("chenssy");
System.out.println(p1.equals(e1));
System.out.println(p1.equals(e2));
System.out.println(e1.equals(e2));
}
}
上面定義了兩個(gè)員工和一個(gè)普通人,雖然他們同名,但是他們肯定不是同一人,所以按理來(lái)說(shuō)輸出結(jié)果應(yīng)該全部都是 false,但是事與愿違,結(jié)果是:true、true、false。
對(duì)于那 e1!=e2 我們非常容易理解,因?yàn)樗麄儾粌H需要比較 name,還需要比較 ID。但是 p1 即等于 e1 也等于 e2,這是非常奇怪的,因?yàn)?e1、e2 明明是兩個(gè)不同的類(lèi),但為什么會(huì)出現(xiàn)這個(gè)情況?首先 p1.equals(e1),是調(diào)用 p1 的 equals 方法,該方法使用 instanceof 關(guān)鍵字來(lái)檢查 e1 是否為 Person 類(lèi),這里我們?cè)倏纯?instanceof:判斷其左邊對(duì)象是否為其右邊類(lèi)的實(shí)例,也可以用來(lái)判斷繼承中的子類(lèi)的實(shí)例是否為父類(lèi)的實(shí)現(xiàn)。他們兩者存在繼承關(guān)系,肯定會(huì)返回 true 了,而兩者 name 又相同,所以結(jié)果肯定是 true。
所以出現(xiàn)上面的情況就是使用了關(guān)鍵字 instanceof,這是非常容易“專(zhuān)空子”的。故在覆寫(xiě) equals 時(shí)推薦使用 getClass 進(jìn)行類(lèi)型判斷。而不是使用 instanceof。
鞏固基礎(chǔ),提高技術(shù),不懼困難,攀登高峰?。。。。?!