讀古今文學網 > 編寫高質量代碼:改善Java程序的151個建議 > 建議44:推薦使用序列化實現對象的拷貝 >

建議44:推薦使用序列化實現對象的拷貝

上一個建議說了對象的淺拷貝問題,實現Cloneable接口就具備了拷貝能力,那我們來思考這樣一個問題:如果一個項目中有大量的對象是通過拷貝生成的,那我們該如何處理?每個類都寫一個clone方法,並且還要深拷貝?想想看這是何等巨大的工作量呀,是否有更好的方法呢?

其實,可以通過序列化方式來處理,在內存中通過字節流的拷貝來實現,也就是把母對像寫到一個字節流中,再從字節流中將其讀出來,這樣就可以重建一個新對象了,該新對象與母對像之間不存在引用共享的問題,也就相當於深拷貝了一個新對象,代碼如下:


public class CloneUtils{

//拷貝一個對像

@SuppressWarnings("unchecked")

public static<T extends Serializable>T clone(T obj){

//拷貝產生的對象

T clonedObj=null;

try{

//讀取對像字節數據

ByteArrayOutputStream baos=new ByteArrayOutputStream();

ObjectOutputStream oos=new ObjectOutputStream(baos);

oos.writeObject(obj);

oos.close();

//分配內存空間,寫入原始對象,生成新對像

ByteArrayInputStream bais=new ByteArrayInputStream(baos.toByteArray());

ObjectInputStream ois=new ObjectInputStream(bais);

//返回新對象,並做類型轉換

clonedObj=(T)ois.readObject();

ois.close();

}catch(Exception e){

e.printStackTrace();

}

return clonedObj;

}

}


此工具類要求被拷貝的對象必須實現Serializable接口,否則是沒辦法拷貝的(當然,使用反射那是另外一種技巧),上一個建議中的例子只要稍微修改一下即可實現深拷貝,代碼如下:


class Person implements Serializable{

private static final long serialVersionUID=1611293231L;

/*刪除掉clone方法,其他代碼保持不變*/

}


被拷貝的類只要實現Serializable這個標誌性接口即可,不需要任何實現,當然serialVersionUID常量還是要加上去的,然後我們就可以通過CloneUtils工具進行對象的深拷貝了。用此方法進行對像拷貝時需要注意兩點:

(1)對象的內部屬性都是可序列化的

如果有內部屬性不可序列化,則會拋出序列化異常,這會讓調試者很納悶:生成一個對像怎麼會出現序列化異常呢?從這一點來考慮,也需要把CloneUtils工具的異常進行細化處理。

(2)注意方法和屬性的特殊修飾符

比如final、static變量的序列化問題會被引入到對像拷貝中來(參考第1章),這點需要特別注意,同時transient變量(瞬態變量,不進行序列化的變量)也會影響到拷貝的效果。

當然,採用序列化方式拷貝時還有一個更簡單的辦法,即使用Apache下的commons工具包中的SerializationUtils類,直接使用更加簡潔方便。