我們大家都知道,對一個對應進行復制有二種比較好的方式,一種就是序列化,另一種就是克隆。使用序列化進行復制很方便,因爲此種方式會自動進行深層複製,只需要我們將要序列化的對象所對應的類實現序列化標示性接口Serializable,它就會將對象裏所引用的其他對象一併複製,但此種效率不及Object裏的clone克隆方法。不過使用clone進行克隆卻是淺複製,它不會自動將對象裏所引用的其他對象進行深層克隆,所以如果我們想要進行深層複製時,需要覆寫Object中的clone方法,對需要進行深層複製的域進行單獨處理,所以應用起來比較麻煩,正是因爲這樣繁瑣,下面我採用了反射的方式來進行深層克隆clone,其只需要克隆的類繼承該類DeepClone即可。詳細過程請參見注釋。
深層克隆實現:
- /**
- *
- * 利用反射進行深度克隆,只要繼承該類的Bean就具有深度克隆的能力。
- *
- * 但是不支持克隆父類的屬性成員,因爲 this.getClass().getDeclaredFields()
- * 只能獲取到自己本身所有定義的屬性成員,所以此繼承的情況下不支持父類的屬性成員深
- * 度克隆,除非放棄這種反射,爲每個Bean覆寫clone方法。
- *
- * 另外需注意的是,本程序只是對實現了Cloneable接口並重寫了clone方法的類實例才進行
- * 深層克隆,如果你的類裏含有未實現Cloneable接口的引用類型,則不會幫你進行深層克隆
- * (雖然可以做,比如使用序列化與反序列化來創建另一個實例,但這麼做違背了這個類最
- * 初的設計 —— 它本身就是一個不可變類或都是一個不具有狀態的如工具類,則創建多個這樣
- * 的實例沒有什麼好處,反而會佔用內存與頻繁的調用垃圾回器;如果這個類是可變的而沒有
- * 實現克隆接口,那麼這則是設計人員本身的的設計錯誤,所以這裏不會幫你去克隆這些類)。
- *
- * 請記住,克隆對那些可變的值類類型的Bean才具有實際意義,對不可變類或者是不具有狀態
- * 的類對象克隆沒有意義,Java庫裏的不可變值類型類就是這麼處理的,比如String、基本
- * 類型的包裝類、BigInteger...,它們都不具有克隆能力
- *
- * @author jiangzhengjun 2010.5.5
- */
- public abstract class DeepClone implements Cloneable, Serializable {
- protected Object clone() throws CloneNotSupportedException {
- Object cloneObj = null;
- try {
- // 克隆對象
- cloneObj = super.clone();
- // 該類的所有屬性,包括靜態屬性
- Field[] filedArr = this.getClass().getDeclaredFields();
- Field field;//屬性
- Class fieldType;//屬性類型
- Object filedVal;//屬性值
- for (int i = 0; i < filedArr.length; i++) {
- field = filedArr[i];
- fieldType = field.getType();
- field.setAccessible(true);
- filedVal = field.get(this);
- /*
- 下面代碼運行的結果可以表明super.clone()只是淺複製,它只是將原始對象
- 的域成員內存地址對拷到了克隆對象中,所以如果是引用類型則指向同一對象,
- 若是基本類型,則直接將存儲的值複製到克隆對象中,基本類型域成員不需要
- 再次單獨複製處理。然而,引用類型卻是線複製,所以我們需要對引用型單獨
- 做特殊的複製處理,即深層克隆。
- 下面是某次的輸出結果,從輸出結果可以證實上面的結論:
- i : -1 - -1
- ca : CloneA@480457 - CloneA@480457
- ca1 : CloneA@47858e - CloneA@47858e
- ca2 : CloneA@19134f4 - CloneA@19134f4
- cb : CloneB@df6ccd - CloneB@df6ccd
- sb : -
- intArr : [[[I@601bb1 - [[[I@601bb1
- caArr : [[[LCloneA;@1ea2dfe - [[[LCloneA;@1ea2dfe
- cbArr : [[[LCloneB;@17182c1 - [[[LCloneB;@17182c1
- int1Arr : [I@13f5d07 - [I@13f5d07
- ca1Arr : [LCloneA;@f4a24a - [LCloneA;@f4a24a
- cb1Arr : [LCloneB;@cac268 - [LCloneB;@cac268
- */
- //Field clFiled = cloneObj.getClass().getDeclaredField(
- // field.getName());
- //clFiled.setAccessible(true);
- //System.out.println(field.getName() + " : " + filedVal + " - "
- // + clFiled.get(cloneObj));
- /*
- * 如果是靜態的成員,則不需要深層克隆,因爲靜態成員屬於類成員,
- * 對所有實例都共享,不要改變現有靜態成員的引用指向。
- *
- * 如果是final類型變量,則不能深層克隆,即使複製一份後也不能將
- * 它賦值給final類型變量,這也正是final的限制。否則在使用反射
- * 賦值給final變量時會拋異常。所以在我們定義一個引用類型是否是
- * final時,我們要考慮它是否是真真不需要修改它的指向與指向內 容。
- */
- if (Modifier.isStatic(field.getModifiers())
- || Modifier.isFinal(field.getModifiers())) {
- continue;
- }
- //如果是數組
- if (fieldType.isArray()) {
- /*
- * 克隆數組,但只是克隆第一維,比如是三維,克隆的結果就相當於
- * new Array[3][][], 即只初始化第一維,第二與第三維 還需進一步
- * 初始化。如果某個Class對象是數 組類對象,則 class.getComponen
- * tType返回的是複合類型,即元素的類 型,如 果數組是多維的,那麼
- * 它返回的也是數組類型,類型 比class少一維而已,比如 有
- * Array[][][] arr = new Array[3][][],則arr.getClass().getC
- * omponentType返回的爲二維Array類型的數組,而且我們可以以這個
- * 返回的類型來動態創建 三維數組
- */
- Object cloneArr = Array.newInstance(filedVal.getClass()
- .getComponentType(), Array.getLength(filedVal));
- cloneArr(filedVal, cloneArr);
- // 設置到克隆對象中
- filedArr[i].set(cloneObj, cloneArr);
- } else {// 如果不是數組
- /*
- * 如果爲基本類型或沒有實現Cloneable的引用類型時,我們不需要對
- * 它們做任何克隆處理,因爲上面的super.clone()已經對它們進行了
- * 簡單的值拷貝工作了,即已將基本類型的值或引用的地址拷貝到克隆
- * 對象中去了。super.clone()對基本類型還是屬於深克隆,而對引用
- * 則屬於淺克隆。
- *
- * String、 Integer...之類爲不可變的類,它們都沒有實現Cloneable,
- * 所以對它們進行淺克隆是沒有問題的,即它們指向同一不可變對象是沒
- * 有問題。對不可變對象進行克隆是沒有意義的。但要 注意,如果是自己
- * 設計的類,就要考慮是否實現Cloneable與重 寫clone方法,如果沒有
- * 這樣作,也會進行淺克隆。
- *
- * 下面只需對實現了Cloneable的引用進行深度克隆。
- */
- // 如果屬性對象實現了Cloneable
- if (filedVal instanceof Cloneable) {
- // 反射查找clone方法
- Method cloneMethod;
- try {
- cloneMethod = filedVal.getClass().getDeclaredMethod("clone",
- new Class[] {});
- } catch (NoSuchMethodException e) {
- cloneMethod = filedVal.getClass().getMethod("clone",
- new Class[] {});
- }
- //調用克隆方法並設置到克隆對象中
- filedArr[i].set(cloneObj, cloneMethod.invoke(filedVal,
- new Object[0]));
- }
- }
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- return cloneObj;
- }
- /**
- * 多維數組深層克隆,如果數組類型是實現了Cloneable接口的某個類,
- * 則會調用每個元素的clone方法實現深度克隆
- *
- * 雖然數組有clone方法,但我們不能使用反射來克隆數組,因爲不能使用
- * 反射來獲取數組的clone方法,這個方法只能通過數組對象本身來調用,
- * 所以這裏使用了動態數組創建方法來實現。
- *
- * @param objArr
- * @param cloneArr
- * @throws Exception
- */
- static private void cloneArr(Object objArr, Object cloneArr) throws Exception {
- Object objTmp;
- Object val = null;
- for (int i = 0; i < Array.getLength(objArr); i++) {
- //注,如果是非數組的基本類型,則返回的是包裝類型
- objTmp = Array.get(objArr, i);
- if (objTmp == null) {
- val = null;
- } else if (objTmp.getClass().isArray()) {//如果是數組
- val = Array.newInstance(objTmp.getClass().getComponentType(), Array
- .getLength(objTmp));
- //如果元素是數組,則遞歸調用
- cloneArr(objTmp, val);
- } else {//否則非數組
- /*
- * 如果爲基本類型或者是非Cloneable類型的引用類型,則直接對拷值 或
- * 者是對象的地址。沒有實現Cloneable的引用類型會實行淺複製, 這對
- * 於像String不可變類來說是沒有關係的,因爲它們可以多實例或 多線程
- * 共享,但如果即沒有實現Cloneable,又是可變以的類,淺複製 則會帶來
- * 危險,因爲這些類實例不能共享 ,一個實例裏的改變會影響到 另一個實
- * 例。所以在使用克隆方案的時候一定要考慮可變對象的可克隆性,即需要
- * 實現Cloneable。
- *
- * 注,這裏不能使用 objTmp.getClass.isPrimitive()來判斷是元素是
- * 否是基本類型,因爲objTmp是通過Array.get獲得的,而Array.get返
- * 回的是Object 類型,也就是說如果是基本類型會自動轉換成對應的包
- * 裝類型後返回,所以 我們只能採用原始的類型來判斷才行。
- */
- if (objArr.getClass().getComponentType().isPrimitive()
- || !(objTmp instanceof Cloneable)) {//基本類型或非Cloneable引用類型
- val = objTmp;
- } else if (objTmp instanceof Cloneable) {//引用類型,並實現了Cloneable
- /*
- * 用反射查找colone方法,注,先使用getDeclaredMethod獲取自
- * 己類 中所定義的方法(包括該類所聲明的公共、保護、默認訪問
- * 及私有的 方法),如果沒有的話,再使用getMethod,getMethod
- * 只能獲取公有的方法,但還包括了從父類繼承過來的公有方法
- */
- Method cloneMethod;
- try {
- //先獲取自己定義的clone方法
- cloneMethod = objTmp.getClass().getDeclaredMethod("clone",
- new Class[] {});
- } catch (NoSuchMethodException e) {
- //如果自身未定義clone方法,則從父類中找,但父類的clone一定要是public
- cloneMethod = objTmp.getClass()
- .getMethod("clone", new Class[] {});
- }
- cloneMethod.setAccessible(true);
- val = cloneMethod.invoke(objTmp, new Object[0]);
- }
- }
- // 設置克隆數組元素值
- Array.set(cloneArr, i, val);
- }
- }
- }
深層克隆測試:
- //具有克隆能力的測試類
- class CloneA implements Cloneable, Serializable {
- int intArr[] = new int[] { 1 };
- protected Object clone() throws CloneNotSupportedException {
- CloneA clone = (CloneA) super.clone();
- clone.intArr = (int[]) intArr.clone();
- return clone;
- }
- }
- /*
- * 不具有克隆能力的測試類,但該類裏有一個引用類型intArr,如果共享則
- * 會有問題,所以該類在克隆的方案裏使用(即用在了ValueBean可克隆中)
- * 就是一個錯誤,這是設計人員自身的錯誤,問題由設計人員自已負責。
- */
- class UnCloneB implements Serializable{
- int intArr[] = new int[] { 1 };
- }
- class ParentBean extends DeepClone {
- /*
- * 使用 new ValueBean().clone()時,
- * ValueBean的父類ParentBena的屬性成員不具有深度克隆的
- * 能力,但你又不能重寫DeepClone父類的clone方法,否則
- * 反射深層克隆不再起 作用。不知道這個問題能否很好的解
- * 決。 想了一下,除非不繼承自DeepClone,在子類ValueBean
- * 中重寫Object的clone方法,然後在子類中針對該屬性做單
- * 獨的克隆處理纔可以。
- */
- public final CloneA pca = new CloneA();
- }
- /**
- * 用來進行克隆的值Bean
- *
- * 能克隆的域會進行深層克隆,不能克隆的域會進行淺複製
- */
- class ValueBean extends ParentBean {
- private int i = -1;
- private String str = new String("string");
- public static CloneA ca = new CloneA();
- private final CloneA ca1 = new CloneA();
- private CloneA ca2 = new CloneA();
- private UnCloneB cb = new UnCloneB();
- private StringBuffer sb = new StringBuffer();
- //三維數組
- private int[][][] intArr;//基本類型數組
- private CloneA[][][] caArr;//元素實現了Cloneable的數組
- private UnCloneB[][][] cbArr;//元素未實現了Cloneable的數組
- //一維數組
- int[] int1Arr;
- CloneA[] ca1Arr;
- UnCloneB[] cb1Arr;
- public Object clone() throws CloneNotSupportedException {
- return super.clone();
- }
- public ValueBean() {
- intArr = new int[3][][];
- intArr[0] = new int[2][];
- intArr[0][1] = new int[2];
- intArr[0][1][0] = 1;
- intArr[1] = new int[1][];
- intArr[1][0] = new int[2];
- intArr[1][0][0] = 2;
- intArr[1][0][1] = 3;
- caArr = new CloneA[3][][];
- caArr[0] = new CloneA[2][];
- caArr[0][1] = new CloneA[2];
- caArr[0][1][0] = new CloneA();
- caArr[1] = new CloneA[1][];
- caArr[1][0] = new CloneA[2];
- caArr[1][0][0] = new CloneA();
- caArr[1][0][1] = new CloneA();
- cbArr = new UnCloneB[3][][];
- cbArr[0] = new UnCloneB[2][];
- cbArr[0][1] = new UnCloneB[2];
- cbArr[0][1][0] = new UnCloneB();
- cbArr[1] = new UnCloneB[1][];
- cbArr[1][0] = new UnCloneB[2];
- cbArr[1][0][0] = new UnCloneB();
- cbArr[1][0][1] = new UnCloneB();
- int1Arr = new int[2];
- int1Arr[0] = 1;
- int1Arr[1] = 2;
- ca1Arr = new CloneA[3];
- ca1Arr[0] = new CloneA();
- ca1Arr[1] = new CloneA();
- cb1Arr = new UnCloneB[3];
- cb1Arr[0] = new UnCloneB();
- cb1Arr[2] = new UnCloneB();
- }
- public static void main(String[] args) throws Exception {
- ValueBean bt = new ValueBean();
- //因爲是靜態屬性,防止克隆過程中修改,所以先存儲起來,供後面對比
- CloneA ca = ValueBean.ca;
- ValueBean btclone = (ValueBean) bt.clone();
- bt.i = 10;
- System.out.println(btclone.i);//-1 ,基本類型克隆成功
- System.out.println(bt.str == btclone.str);//true,String爲不可變類,沒有克隆
- System.out.println(ca == ValueBean.ca);//true,靜態成員沒有克隆
- System.out.println(//true,final類型的引用沒有深層複製
- bt.ca1 == btclone.ca1);
- System.out.println(//false,可克隆的引用類型已進行深層克隆
- bt.ca2 == btclone.ca2);
- bt.ca2.intArr[0] = 2;//試着改變可克隆原始對象的值
- System.out.println(btclone.ca2.intArr[0]);//1,CloneA裏的數組克隆成功
- System.out.println(//true,不可克隆的引用類型還是淺複製
- bt.cb == btclone.cb);
- bt.cb.intArr[0] = 2;//試着改變不可克隆原始對象的值
- System.out.println(btclone.cb.intArr[0]);//2,CloneB裏的數組沒有深層克隆
- bt.sb.append(1);
- System.out.println(//1,不可克隆引用只進行淺複製,所以指向原始對象
- btclone.sb);
- bt.intArr[0][1][0] = 11;
- bt.intArr[1][0][0] = 22;
- bt.intArr[1][0][1] = 33;
- System.out.println(//11 1,基本類型數組克隆成功
- bt.intArr[0][1][0] + " " + btclone.intArr[0][1][0]);
- System.out.println(//22 2
- bt.intArr[1][0][0] + " " + btclone.intArr[1][0][0]);
- System.out.println(//33 3
- bt.intArr[1][0][1] + " " + btclone.intArr[1][0][1]);
- System.out.println(//null null
- bt.intArr[2] + " " + btclone.intArr[2]);
- //Cloneable引用類型數組克隆成功
- System.out.println(//CloneA@3e25a5 CloneA@19821f
- bt.caArr[0][1][0] + " " + btclone.caArr[0][1][0]);
- System.out.println(//CloneA@addbf1 CloneA@42e816
- bt.caArr[1][0][0] + " " + btclone.caArr[1][0][0]);
- System.out.println(//CloneA@9304b1 CloneA@190d11
- bt.caArr[1][0][1] + " " + btclone.caArr[1][0][1]);
- System.out.println(//null null
- bt.caArr[2] + " " + btclone.caArr[2]);
- bt.caArr[0][1][0].intArr[0] = 2;
- System.out.println(//1,即使原始對象改變了,但這裏爲深層克隆,所以沒影響
- btclone.caArr[0][1][0].intArr[0]);
- // 對象數組本身已克隆,好比直接調用數組的 clone方法。
- System.out.println(//[[[LCloneB;@de6ced [[[LCloneB;@c17164
- bt.cbArr + " " + btclone.cbArr);
- //非Cloneable引用類型數組裏克隆后里面的元素指向相同元素
- System.out.println(//CloneB@de6ced CloneB@de6ced
- bt.cbArr[0][1][0] + " " + btclone.cbArr[0][1][0]);
- System.out.println(//CloneB@c17164 CloneB@c17164
- bt.cbArr[1][0][0] + " " + btclone.cbArr[1][0][0]);
- System.out.println(//CloneB@1fb8ee3 CloneB@1fb8ee3
- bt.cbArr[1][0][1] + " " + btclone.cbArr[1][0][1]);
- System.out.println(//null null
- bt.cbArr[2] + " " + btclone.cbArr[2]);
- bt.cbArr[0][1][0].intArr[0] = 2;
- System.out.println(//2,原始對象改變影響到另一實例,因爲UnCloneB不具克隆能力
- btclone.cbArr[0][1][0].intArr[0]);
- //一維數組克隆也是沒有問題的
- bt.int1Arr[0] = 11;
- bt.int1Arr[1] = 22;
- System.out.println(//11 1
- bt.int1Arr[0] + " " + btclone.int1Arr[0]);
- System.out.println(//22 2
- bt.int1Arr[1] + " " + btclone.int1Arr[1]);
- System.out.println(//CloneA@ca0b6 CloneA@10b30a7
- bt.ca1Arr[0] + " " + btclone.ca1Arr[0]);
- System.out.println(//CloneA@1a758cb CloneA@1b67f74
- bt.ca1Arr[1] + " " + btclone.ca1Arr[1]);
- System.out.println(//CloneB@69b332 CloneB@69b332
- bt.cb1Arr[0] + " " + btclone.cb1Arr[0]);
- System.out.println(//null null
- bt.cb1Arr[1] + " " + btclone.cb1Arr[1]);
- //父類的屬性成員沒有被深度克隆
- System.out.println(bt.pca == btclone.pca);//true
- //如果直接創建父類的實例然後對它進行克隆,這與直接創建子類一樣也是可以的
- ParentBean pb1 = new ParentBean();
- ParentBean pb2 = (ParentBean) pb1.clone();
- System.out.println(pb1.pca == pb2.pca);//false
- }
- }