利用反射进行深层克隆

我们大家都知道,对一个对应进行复制有二种比较好的方式,一种就是序列化,另一种就是克隆。使用序列化进行复制很方便,因为此种方式会自动进行深层复制,只需要我们将要序列化的对象所对应的类实现序列化标示性接口Serializable,它就会将对象里所引用的其他对象一并复制,但此种效率不及Object里的clone克隆方法。不过使用clone进行克隆却是浅复制,它不会自动将对象里所引用的其他对象进行深层克隆,所以如果我们想要进行深层复制时,需要覆写Object中的clone方法,对需要进行深层复制的域进行单独处理,所以应用起来比较麻烦,正是因为这样繁琐,下面我采用了反射的方式来进行深层克隆clone,其只需要克隆的类继承该类DeepClone即可。详细过程请参见注释。

 

深层克隆实现:

Java代码   收藏代码
  1. /** 
  2.  *  
  3.  * 利用反射进行深度克隆,只要继承该类的Bean就具有深度克隆的能力。 
  4.  *  
  5.  * 但是不支持克隆父类的属性成员,因为 this.getClass().getDeclaredFields() 
  6.  * 只能获取到自己本身所有定义的属性成员,所以此继承的情况下不支持父类的属性成员深 
  7.  * 度克隆,除非放弃这种反射,为每个Bean覆写clone方法。 
  8.  *  
  9.  * 另外需注意的是,本程序只是对实现了Cloneable接口并重写了clone方法的类实例才进行 
  10.  * 深层克隆,如果你的类里含有未实现Cloneable接口的引用类型,则不会帮你进行深层克隆 
  11.  * (虽然可以做,比如使用序列化与反序列化来创建另一个实例,但这么做违背了这个类最 
  12.  * 初的设计 —— 它本身就是一个不可变类或都是一个不具有状态的如工具类,则创建多个这样 
  13.  * 的实例没有什么好处,反而会占用内存与频繁的调用垃圾回器;如果这个类是可变的而没有 
  14.  * 实现克隆接口,那么这则是设计人员本身的的设计错误,所以这里不会帮你去克隆这些类)。 
  15.  *  
  16.  * 请记住,克隆对那些可变的值类类型的Bean才具有实际意义,对不可变类或者是不具有状态 
  17.  * 的类对象克隆没有意义,Java库里的不可变值类型类就是这么处理的,比如String、基本 
  18.  * 类型的包装类、BigInteger...,它们都不具有克隆能力 
  19.  *  
  20.  * @author jiangzhengjun 2010.5.5 
  21.  */  
  22. public abstract class DeepClone implements Cloneable, Serializable {  
  23.   
  24.     protected Object clone() throws CloneNotSupportedException {  
  25.         Object cloneObj = null;  
  26.         try {  
  27.             // 克隆对象  
  28.             cloneObj = super.clone();  
  29.   
  30.             // 该类的所有属性,包括静态属性  
  31.             Field[] filedArr = this.getClass().getDeclaredFields();  
  32.             Field field;//属性  
  33.             Class fieldType;//属性类型  
  34.             Object filedVal;//属性值  
  35.             for (int i = 0; i < filedArr.length; i++) {  
  36.                 field = filedArr[i];  
  37.                 fieldType = field.getType();  
  38.                 field.setAccessible(true);  
  39.                 filedVal = field.get(this);  
  40.                 /* 
  41.                 下面代码运行的结果可以表明super.clone()只是浅复制,它只是将原始对象 
  42.                 的域成员内存地址对拷到了克隆对象中,所以如果是引用类型则指向同一对象, 
  43.                 若是基本类型,则直接将存储的值复制到克隆对象中,基本类型域成员不需要 
  44.                 再次单独复制处理。然而,引用类型却是线复制,所以我们需要对引用型单独 
  45.                 做特殊的复制处理,即深层克隆。 
  46.                  
  47.                 下面是某次的输出结果,从输出结果可以证实上面的结论:                   
  48.                 i : -1 - -1 
  49.                 ca : CloneA@480457 - CloneA@480457 
  50.                 ca1 : CloneA@47858e - CloneA@47858e 
  51.                 ca2 : CloneA@19134f4 - CloneA@19134f4 
  52.                 cb : CloneB@df6ccd - CloneB@df6ccd 
  53.                 sb :  -  
  54.                 intArr : [[[I@601bb1 - [[[I@601bb1 
  55.                 caArr : [[[LCloneA;@1ea2dfe - [[[LCloneA;@1ea2dfe 
  56.                 cbArr : [[[LCloneB;@17182c1 - [[[LCloneB;@17182c1 
  57.                 int1Arr : [I@13f5d07 - [I@13f5d07 
  58.                 ca1Arr : [LCloneA;@f4a24a - [LCloneA;@f4a24a 
  59.                 cb1Arr : [LCloneB;@cac268 - [LCloneB;@cac268 
  60.                 */  
  61.                 //Field clFiled = cloneObj.getClass().getDeclaredField(  
  62.                 //      field.getName());  
  63.                 //clFiled.setAccessible(true);  
  64.                 //System.out.println(field.getName() + " : " + filedVal + " - "  
  65.                 //      + clFiled.get(cloneObj));  
  66.                 /* 
  67.                  * 如果是静态的成员,则不需要深层克隆,因为静态成员属于类成员, 
  68.                  * 对所有实例都共享,不要改变现有静态成员的引用指向。 
  69.                  *  
  70.                  * 如果是final类型变量,则不能深层克隆,即使复制一份后也不能将 
  71.                  * 它赋值给final类型变量,这也正是final的限制。否则在使用反射 
  72.                  * 赋值给final变量时会抛异常。所以在我们定义一个引用类型是否是 
  73.                  * final时,我们要考虑它是否是真真不需要修改它的指向与指向内 容。 
  74.                  */  
  75.                 if (Modifier.isStatic(field.getModifiers())  
  76.                         || Modifier.isFinal(field.getModifiers())) {  
  77.                     continue;  
  78.                 }  
  79.   
  80.                 //如果是数组  
  81.                 if (fieldType.isArray()) {  
  82.                     /* 
  83.                      * 克隆数组,但只是克隆第一维,比如是三维,克隆的结果就相当于 
  84.                      * new Array[3][][], 即只初始化第一维,第二与第三维 还需进一步 
  85.                      * 初始化。如果某个Class对象是数 组类对象,则 class.getComponen 
  86.                      * tType返回的是复合类型,即元素的类 型,如 果数组是多维的,那么 
  87.                      * 它返回的也是数组类型,类型 比class少一维而已,比如 有 
  88.                      * Array[][][] arr = new Array[3][][],则arr.getClass().getC 
  89.                      * omponentType返回的为二维Array类型的数组,而且我们可以以这个 
  90.                      * 返回的类型来动态创建 三维数组 
  91.                      */  
  92.                     Object cloneArr = Array.newInstance(filedVal.getClass()  
  93.                             .getComponentType(), Array.getLength(filedVal));  
  94.   
  95.                     cloneArr(filedVal, cloneArr);  
  96.   
  97.                     // 设置到克隆对象中  
  98.                     filedArr[i].set(cloneObj, cloneArr);  
  99.                 } else {// 如果不是数组  
  100.                     /* 
  101.                      * 如果为基本类型或没有实现Cloneable的引用类型时,我们不需要对 
  102.                      * 它们做任何克隆处理,因为上面的super.clone()已经对它们进行了 
  103.                      * 简单的值拷贝工作了,即已将基本类型的值或引用的地址拷贝到克隆 
  104.                      * 对象中去了。super.clone()对基本类型还是属于深克隆,而对引用 
  105.                      * 则属于浅克隆。 
  106.                      *  
  107.                      * String、 Integer...之类为不可变的类,它们都没有实现Cloneable, 
  108.                      * 所以对它们进行浅克隆是没有问题的,即它们指向同一不可变对象是没 
  109.                      * 有问题。对不可变对象进行克隆是没有意义的。但要 注意,如果是自己 
  110.                      * 设计的类,就要考虑是否实现Cloneable与重 写clone方法,如果没有 
  111.                      * 这样作,也会进行浅克隆。 
  112.                      *  
  113.                      * 下面只需对实现了Cloneable的引用进行深度克隆。 
  114.                      */  
  115.   
  116.                     // 如果属性对象实现了Cloneable  
  117.                     if (filedVal instanceof Cloneable) {  
  118.   
  119.                         // 反射查找clone方法  
  120.                         Method cloneMethod;  
  121.                         try {  
  122.                             cloneMethod = filedVal.getClass().getDeclaredMethod("clone",  
  123.                                     new Class[] {});  
  124.   
  125.                         } catch (NoSuchMethodException e) {  
  126.                             cloneMethod = filedVal.getClass().getMethod("clone",  
  127.                                     new Class[] {});  
  128.                         }  
  129.                         //调用克隆方法并设置到克隆对象中  
  130.                         filedArr[i].set(cloneObj, cloneMethod.invoke(filedVal,  
  131.                                 new Object[0]));  
  132.                     }  
  133.                 }  
  134.             }  
  135.         } catch (Exception e) {  
  136.             e.printStackTrace();  
  137.         }  
  138.   
  139.         return cloneObj;  
  140.     }  
  141.   
  142.     /** 
  143.      * 多维数组深层克隆,如果数组类型是实现了Cloneable接口的某个类, 
  144.      * 则会调用每个元素的clone方法实现深度克隆 
  145.      *  
  146.      * 虽然数组有clone方法,但我们不能使用反射来克隆数组,因为不能使用 
  147.      * 反射来获取数组的clone方法,这个方法只能通过数组对象本身来调用, 
  148.      * 所以这里使用了动态数组创建方法来实现。 
  149.      *  
  150.      * @param objArr 
  151.      * @param cloneArr 
  152.      * @throws Exception 
  153.      */  
  154.     static private void cloneArr(Object objArr, Object cloneArr) throws Exception {  
  155.         Object objTmp;  
  156.         Object val = null;  
  157.         for (int i = 0; i < Array.getLength(objArr); i++) {  
  158.             //注,如果是非数组的基本类型,则返回的是包装类型  
  159.             objTmp = Array.get(objArr, i);  
  160.   
  161.             if (objTmp == null) {  
  162.                 val = null;  
  163.             } else if (objTmp.getClass().isArray()) {//如果是数组  
  164.   
  165.                 val = Array.newInstance(objTmp.getClass().getComponentType(), Array  
  166.                         .getLength(objTmp));  
  167.                 //如果元素是数组,则递归调用  
  168.                 cloneArr(objTmp, val);  
  169.             } else {//否则非数组  
  170.   
  171.                 /* 
  172.                  * 如果为基本类型或者是非Cloneable类型的引用类型,则直接对拷值 或 
  173.                  * 者是对象的地址。没有实现Cloneable的引用类型会实行浅复制, 这对 
  174.                  * 于像String不可变类来说是没有关系的,因为它们可以多实例或 多线程 
  175.                  * 共享,但如果即没有实现Cloneable,又是可变以的类,浅复制 则会带来 
  176.                  * 危险,因为这些类实例不能共享 ,一个实例里的改变会影响到 另一个实 
  177.                  * 例。所以在使用克隆方案的时候一定要考虑可变对象的可克隆性,即需要 
  178.                  * 实现Cloneable。 
  179.                  *  
  180.                  * 注,这里不能使用 objTmp.getClass.isPrimitive()来判断是元素是 
  181.                  * 否是基本类型,因为objTmp是通过Array.get获得的,而Array.get返 
  182.                  * 回的是Object 类型,也就是说如果是基本类型会自动转换成对应的包 
  183.                  * 装类型后返回,所以 我们只能采用原始的类型来判断才行。 
  184.                  */  
  185.                 if (objArr.getClass().getComponentType().isPrimitive()  
  186.                         || !(objTmp instanceof Cloneable)) {//基本类型或非Cloneable引用类型  
  187.                     val = objTmp;  
  188.                 } else if (objTmp instanceof Cloneable) {//引用类型,并实现了Cloneable  
  189.                     /* 
  190.                      *  用反射查找colone方法,注,先使用getDeclaredMethod获取自 
  191.                      *  己类 中所定义的方法(包括该类所声明的公共、保护、默认访问 
  192.                      *  及私有的 方法),如果没有的话,再使用getMethod,getMethod 
  193.                      *  只能获取公有的方法,但还包括了从父类继承过来的公有方法 
  194.                      */  
  195.                     Method cloneMethod;  
  196.                     try {  
  197.                         //先获取自己定义的clone方法  
  198.                         cloneMethod = objTmp.getClass().getDeclaredMethod("clone",  
  199.                                 new Class[] {});  
  200.   
  201.                     } catch (NoSuchMethodException e) {  
  202.                         //如果自身未定义clone方法,则从父类中找,但父类的clone一定要是public  
  203.                         cloneMethod = objTmp.getClass()  
  204.                                 .getMethod("clone"new Class[] {});  
  205.                     }  
  206.                     cloneMethod.setAccessible(true);  
  207.                     val = cloneMethod.invoke(objTmp, new Object[0]);  
  208.   
  209.                 }  
  210.             }  
  211.             // 设置克隆数组元素值  
  212.             Array.set(cloneArr, i, val);  
  213.         }  
  214.     }  
  215. }  

 

深层克隆测试:

Java代码   收藏代码
  1. //具有克隆能力的测试类  
  2. class CloneA implements Cloneable, Serializable {  
  3.     int intArr[] = new int[] { 1 };  
  4.   
  5.     protected Object clone() throws CloneNotSupportedException {  
  6.         CloneA clone = (CloneA) super.clone();  
  7.         clone.intArr = (int[]) intArr.clone();  
  8.         return clone;  
  9.     }  
  10. }  
  11.   
  12. /* 
  13.  * 不具有克隆能力的测试类,但该类里有一个引用类型intArr,如果共享则 
  14.  * 会有问题,所以该类在克隆的方案里使用(即用在了ValueBean可克隆中) 
  15.  * 就是一个错误,这是设计人员自身的错误,问题由设计人员自已负责。 
  16.  */  
  17. class UnCloneB implements  Serializable{  
  18.     int intArr[] = new int[] { 1 };  
  19. }  
  20.   
  21. class ParentBean extends DeepClone {  
  22.     /* 
  23.      * 使用 new ValueBean().clone()时, 
  24.      * ValueBean的父类ParentBena的属性成员不具有深度克隆的 
  25.      * 能力,但你又不能重写DeepClone父类的clone方法,否则 
  26.      * 反射深层克隆不再起 作用。不知道这个问题能否很好的解 
  27.      * 决。 想了一下,除非不继承自DeepClone,在子类ValueBean 
  28.      * 中重写Object的clone方法,然后在子类中针对该属性做单 
  29.      * 独的克隆处理才可以。 
  30.      */  
  31.     public final CloneA pca = new CloneA();  
  32. }  
  33.   
  34. /** 
  35.  *  用来进行克隆的值Bean 
  36.  *   
  37.  *  能克隆的域会进行深层克隆,不能克隆的域会进行浅复制 
  38.  */  
  39. class ValueBean extends ParentBean {  
  40.     private int i = -1;  
  41.     private String str = new String("string");  
  42.     public static CloneA ca = new CloneA();  
  43.     private final CloneA ca1 = new CloneA();  
  44.     private CloneA ca2 = new CloneA();  
  45.     private UnCloneB cb = new UnCloneB();  
  46.     private StringBuffer sb = new StringBuffer();  
  47.   
  48.     //三维数组  
  49.     private int[][][] intArr;//基本类型数组  
  50.     private CloneA[][][] caArr;//元素实现了Cloneable的数组  
  51.     private UnCloneB[][][] cbArr;//元素未实现了Cloneable的数组  
  52.   
  53.     //一维数组  
  54.     int[] int1Arr;  
  55.     CloneA[] ca1Arr;  
  56.     UnCloneB[] cb1Arr;  
  57.   
  58.     public Object clone() throws CloneNotSupportedException {  
  59.         return super.clone();  
  60.     }  
  61.   
  62.     public ValueBean() {  
  63.         intArr = new int[3][][];  
  64.         intArr[0] = new int[2][];  
  65.         intArr[0][1] = new int[2];  
  66.         intArr[0][1][0] = 1;  
  67.         intArr[1] = new int[1][];  
  68.         intArr[1][0] = new int[2];  
  69.         intArr[1][0][0] = 2;  
  70.         intArr[1][0][1] = 3;  
  71.   
  72.         caArr = new CloneA[3][][];  
  73.         caArr[0] = new CloneA[2][];  
  74.         caArr[0][1] = new CloneA[2];  
  75.         caArr[0][1][0] = new CloneA();  
  76.         caArr[1] = new CloneA[1][];  
  77.         caArr[1][0] = new CloneA[2];  
  78.         caArr[1][0][0] = new CloneA();  
  79.         caArr[1][0][1] = new CloneA();  
  80.   
  81.         cbArr = new UnCloneB[3][][];  
  82.         cbArr[0] = new UnCloneB[2][];  
  83.         cbArr[0][1] = new UnCloneB[2];  
  84.         cbArr[0][1][0] = new UnCloneB();  
  85.         cbArr[1] = new UnCloneB[1][];  
  86.         cbArr[1][0] = new UnCloneB[2];  
  87.         cbArr[1][0][0] = new UnCloneB();  
  88.         cbArr[1][0][1] = new UnCloneB();  
  89.   
  90.         int1Arr = new int[2];  
  91.         int1Arr[0] = 1;  
  92.         int1Arr[1] = 2;  
  93.   
  94.         ca1Arr = new CloneA[3];  
  95.         ca1Arr[0] = new CloneA();  
  96.         ca1Arr[1] = new CloneA();  
  97.   
  98.         cb1Arr = new UnCloneB[3];  
  99.         cb1Arr[0] = new UnCloneB();  
  100.         cb1Arr[2] = new UnCloneB();  
  101.     }  
  102.   
  103.     public static void main(String[] args) throws Exception {  
  104.         ValueBean bt = new ValueBean();  
  105.         //因为是静态属性,防止克隆过程中修改,所以先存储起来,供后面对比  
  106.         CloneA ca = ValueBean.ca;  
  107.         ValueBean btclone = (ValueBean) bt.clone();  
  108.   
  109.         bt.i = 10;  
  110.         System.out.println(btclone.i);//-1 ,基本类型克隆成功  
  111.   
  112.         System.out.println(bt.str == btclone.str);//true,String为不可变类,没有克隆  
  113.   
  114.         System.out.println(ca == ValueBean.ca);//true,静态成员没有克隆  
  115.   
  116.         System.out.println(//true,final类型的引用没有深层复制  
  117.                 bt.ca1 == btclone.ca1);  
  118.   
  119.         System.out.println(//false,可克隆的引用类型已进行深层克隆  
  120.                 bt.ca2 == btclone.ca2);  
  121.   
  122.         bt.ca2.intArr[0] = 2;//试着改变可克隆原始对象的值  
  123.         System.out.println(btclone.ca2.intArr[0]);//1,CloneA里的数组克隆成功  
  124.   
  125.         System.out.println(//true,不可克隆的引用类型还是浅复制  
  126.                 bt.cb == btclone.cb);  
  127.   
  128.         bt.cb.intArr[0] = 2;//试着改变不可克隆原始对象的值  
  129.         System.out.println(btclone.cb.intArr[0]);//2,CloneB里的数组没有深层克隆  
  130.   
  131.         bt.sb.append(1);  
  132.         System.out.println(//1,不可克隆引用只进行浅复制,所以指向原始对象  
  133.                 btclone.sb);  
  134.   
  135.         bt.intArr[0][1][0] = 11;  
  136.         bt.intArr[1][0][0] = 22;  
  137.         bt.intArr[1][0][1] = 33;  
  138.         System.out.println(//11 1,基本类型数组克隆成功  
  139.                 bt.intArr[0][1][0] + " " + btclone.intArr[0][1][0]);  
  140.         System.out.println(//22 2  
  141.                 bt.intArr[1][0][0] + " " + btclone.intArr[1][0][0]);  
  142.         System.out.println(//33 3  
  143.                 bt.intArr[1][0][1] + " " + btclone.intArr[1][0][1]);  
  144.         System.out.println(//null null  
  145.                 bt.intArr[2] + " " + btclone.intArr[2]);  
  146.   
  147.         //Cloneable引用类型数组克隆成功  
  148.         System.out.println(//CloneA@3e25a5 CloneA@19821f  
  149.                 bt.caArr[0][1][0] + " " + btclone.caArr[0][1][0]);  
  150.         System.out.println(//CloneA@addbf1 CloneA@42e816  
  151.                 bt.caArr[1][0][0] + " " + btclone.caArr[1][0][0]);  
  152.         System.out.println(//CloneA@9304b1 CloneA@190d11  
  153.                 bt.caArr[1][0][1] + " " + btclone.caArr[1][0][1]);  
  154.         System.out.println(//null null  
  155.                 bt.caArr[2] + " " + btclone.caArr[2]);  
  156.   
  157.         bt.caArr[0][1][0].intArr[0] = 2;  
  158.         System.out.println(//1,即使原始对象改变了,但这里为深层克隆,所以没影响  
  159.                 btclone.caArr[0][1][0].intArr[0]);  
  160.   
  161.         // 对象数组本身已克隆,好比直接调用数组的 clone方法。  
  162.         System.out.println(//[[[LCloneB;@de6ced [[[LCloneB;@c17164  
  163.                 bt.cbArr + " " + btclone.cbArr);  
  164.         //非Cloneable引用类型数组里克隆后里面的元素指向相同元素  
  165.         System.out.println(//CloneB@de6ced CloneB@de6ced  
  166.                 bt.cbArr[0][1][0] + " " + btclone.cbArr[0][1][0]);  
  167.         System.out.println(//CloneB@c17164 CloneB@c17164  
  168.                 bt.cbArr[1][0][0] + " " + btclone.cbArr[1][0][0]);  
  169.         System.out.println(//CloneB@1fb8ee3 CloneB@1fb8ee3  
  170.                 bt.cbArr[1][0][1] + " " + btclone.cbArr[1][0][1]);  
  171.         System.out.println(//null null  
  172.                 bt.cbArr[2] + " " + btclone.cbArr[2]);  
  173.   
  174.         bt.cbArr[0][1][0].intArr[0] = 2;  
  175.         System.out.println(//2,原始对象改变影响到另一实例,因为UnCloneB不具克隆能力  
  176.                 btclone.cbArr[0][1][0].intArr[0]);  
  177.   
  178.         //一维数组克隆也是没有问题的  
  179.         bt.int1Arr[0] = 11;  
  180.         bt.int1Arr[1] = 22;  
  181.         System.out.println(//11 1  
  182.                 bt.int1Arr[0] + " " + btclone.int1Arr[0]);  
  183.         System.out.println(//22 2  
  184.                 bt.int1Arr[1] + " " + btclone.int1Arr[1]);  
  185.   
  186.         System.out.println(//CloneA@ca0b6 CloneA@10b30a7  
  187.                 bt.ca1Arr[0] + " " + btclone.ca1Arr[0]);  
  188.         System.out.println(//CloneA@1a758cb CloneA@1b67f74  
  189.                 bt.ca1Arr[1] + " " + btclone.ca1Arr[1]);  
  190.   
  191.         System.out.println(//CloneB@69b332 CloneB@69b332  
  192.                 bt.cb1Arr[0] + " " + btclone.cb1Arr[0]);  
  193.         System.out.println(//null null  
  194.                 bt.cb1Arr[1] + " " + btclone.cb1Arr[1]);  
  195.   
  196.         //父类的属性成员没有被深度克隆  
  197.         System.out.println(bt.pca == btclone.pca);//true  
  198.   
  199.         //如果直接创建父类的实例然后对它进行克隆,这与直接创建子类一样也是可以的  
  200.         ParentBean pb1 = new ParentBean();  
  201.         ParentBean pb2 = (ParentBean) pb1.clone();  
  202.         System.out.println(pb1.pca == pb2.pca);//false  
  203.     }  
  204. }  

 

你可能感兴趣的:(java)