重新編寫Object類中的方法

Object類是所有類的超類,也就是說,Java中的每一個類都是由Object擴展而來的。因而每當你創建一個對象,它都將擁有Object類中的全部方法。讓我們先來看看java.lang.Object的中的主要方法有哪些:

public class Object{
//公共構造函數
public Object();
//公共實例方法
public boolean equals(Object obj);
public native int hashCode();
public final native Class getClass();
public String toString();
public final native void notify();
public final native void notifyAll();
public final void wait() throws InterruptedException;
public final native void wait(long timeout) throws InterruptedException;
public final void wait(long timeout, int nanos) throws InterruptedException;
//保護實例方法
protected native Object clone();
protected void finalize() throws Throwable; }

方法equals測試的是兩個對象是否相等,方法clone進行對象拷貝,方法getClass返回和當前對象相關的Class對象,方法notify,notifyall,wait都是用來對給定對象進行線程同步的。

然而Object類所提供的只是一些基本的方法,我們在編寫自己的類時經常需要覆蓋這些方法,一方面是加強功能,另一方面也是爲了適應當前的情況。這就引出了本文的主題――

如何編寫自己的equals方法
首先要明確一個問題,Object類中的equals方法是怎樣判斷兩個對象是否相等的呢?讓我們來看看它的源代碼:

public boolean equals(Object obj)
{
return (this == obj);
}


不 難看出,它僅僅判斷了兩個對象的引用是否相等,這在很多情況下並沒有太大的實際意義。比如,我們需要一個比較兩個字符串是否相等,在String類中就覆 蓋了這個方法。標準的java類庫中有超過150個equals方法的實現。然而java語言規範要求equals方法具有以下性質:

自反性:對於任何非空引用x, x.equals(x)返回true。
對稱性:對於任何非空引用x和y, 當且僅當y.equals(x)返回true時,x.equals(y)返回true。
傳遞性:對於任何引用x,y和z,如果x.equals(y)返回true並且y.equals(z)也返回true,那麼x.equals(z)應該返回true。
一致性:如果x和y引用的對象沒有改變,那麼x.equals(y)的重複調用應該返回同一結果。
對於任何非空引用x, x.equals(null)應該返回false。

這5條規則顯示良好的邏輯性,想要寫好自己的equals方法,必須滿足它們。那麼如何才能做到這些規則呢?

假設當前equals方法傳進來的參數名爲otherObject,

測試this是否與otherObject相等。
測試otherObject是否爲null,如果是,一定要返回false。
測試this通otherObject是否同屬於一個類。這是爲了滿足規則2。
把 otherObject強制轉換爲另一個變量(這裏稱爲other):Classname other = (Classname) otherObject。然後比較所有的字段。使用 == 比較基本類型字段,使用equals方法比較對象字段。如果所有的字段都匹配返回true,否則返回false。
下面舉一個簡單的例子:

class TestEqual{
int number;
String s;
public boolean equals(Object otherObject){
//首先看看這兩個對象引用是否相等
if (this == otherObject)
return true;
//這是爲了滿足規則5
if(otherObject == null)
return false;
//如果兩個對象所屬類型不同,它們不可能相等
if(getClass() != otherObject.getClass())
return false;
TestEqual other = (TestEqual)otherObject;
//比較所有字段
return s.equals(other.s) && number == other.number;
}
}

這個例子雖然很簡單,但卻清晰地反映出了以上所說的要點。

還有一個需要注意的地方是,在子類中,首先要調用超類的equals方法,如果這項測試無法通過,那麼兩個對象不可能相等。也就是:
例如

public class TestEqualSub extends TestEqual{

public boolean equals(Object otherObject)
{
if (!super.equals(otherObject)) return false;

// super.equals checked that this and otherObject belong to the same class
  TestEqualSub other = (TestEqualSub) otherObject;
  return s.equals(other.s) && number == other.number;
}
}


到這裏相信大家已經對編寫自己的equals方法有了一定的認識,只要在平時寫程序的時候多注意一下,寫好它並不難。

再來看看本文的第二大主題:



如何編寫自己的clone方法
類 似equals方法,我們先來討論一下Object的clone方法效果是怎樣的。不過在此之前,有個問題要提一下,所有使用clone方法的類,不論是 繼承Object.clone()還是覆蓋它,都必須實現一個名爲cloneable的接口。如果你打開java類庫查一下,會發現這個接口裏什麼也沒 有,它僅僅是表明某個類具有被clone的能力。

用一個例子來研究一下Object.clone()的複製效果:

public class TestClone1 implements Cloneable{
int count;
TestClone1 next;

public TestClone1(int count) {
this.count=count;
if(count>0)
next=new TestClone1(count-1);
}

void add(){
count++;
if(next!=null)
next.count++;
}

public String toString(){
String s=String.valueOf(count)+" ";
if(next!=null)
s+=next.toString();
return s;
}

public Object clone(){
Object o=null;
//如果沒有實現cloneable,將會拋出CloneNotSupported異常
try{
o=super.clone();
}
catch(CloneNotSupportedException e){
System.err.println("cannot clone");
}
return o;
}

public static void main(String[] args){
TestClone1 t=new TestClone1(1);
System.out.println("t="+t);
TestClone1 t1=(TestClone1)t.clone();
System.out.println("t1="+t1);
t.add();
System.out.println("after added\nt t="+t+"\nt1="+t1)
}
}


在這個例子中創建t相當於兩個相連的TestClone1實例,而在調用了t的add方法之後,意想不到的結果出現了:
t=1 0
t1=1 0
after added
t t=2 1
t1=1 1
t1 也發生了改變,這點也許有些出乎你的意料。實際上Object.clone()進行的複製有着"bitwise"原則,也就是逐位複製。對於一個對象中定 義的對象,它只是簡單的複製這個對象的引用。這也就是常說的淺層拷貝(shallow copy),明白了這一點,想要執行深層拷貝(deep copy)也就不難了。

只需要在TestClone1 t1=(TestClone1)t.clone();後面加上t1.next=(TestClone1)t.next.clone();就能得到:

t=1 0
t1=1 0
after added
t t=2 1
t1=1 0
這個正確的結果。

接下來要介紹的是,如何控制clone能力。從前面可以知道,clone()是protected的,你必須覆蓋並實現cloneable接口並處理必要的異常才能使用它。這樣,你在設計自己的類時,有以下幾種風格來控制clone能力:

不理會clone(),也就是在你的類中不涉及任何有關clone()的內容,如果有人想實現clone功能,必須寫一個子類並完成剛纔所說的要求。這適合於Object.clone()可以勝任複製你的類中全部字段的情況。
支持clone()。你在你的類中實現cloneable接口並且覆蓋clone方法以及捕獲相應的異常。
有 條件的支持clone()。這種情況有些特殊,也就是你的類中保存着一些可能不能被複制的對象的引用(這裏是指它們沒有實現cloneable)。這時候 你只是在clone()中複製所有這些對象,如果遇到異常,拋出所有這些異常給使用你的類的人。舉個例子,假設你在寫一個類似於ArrayList的類, 你不知道別人會在把什麼樣的對象保存在你的ArrayList裏,所以你也不知道他們是否能夠被複制。
不實現cloneable接口但覆蓋clone方法爲protected。這樣你雖然讓你的類不能被複制,但卻提供了正確的clone方法,這樣如果有人想複製你的類,寫一個子類實現cloneable接口就行了。
不實現cloneable接口並覆蓋clone方法直接拋出異常來防止你的類被複制。不過這並不是根本的解決方法,只能對付那些直接調用你的clone方法或是在子類中調用super.clone()的人。
使用final修飾你的類來防止你的類被複制。如果你的超類中有覆蓋clone方法的,再重新覆蓋clone方法並直接拋出異常。這也是最徹底的辦法。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章