java实现shadow clone(浅克隆)与深克隆(deep clone)

时间:2021-07-02 05:35:19

最近在学习设计模式中的prototype(原型)模式,提出了浅克隆与深克隆,当时甚是不解.于是在网上找了些资料,研究一下,才顿开茅塞.以下是笔人的一点小经验,可提供大家参考,不到之处还请大家点评.

 

克隆就是复制一个对象的复本.但一个对象中可能有基本数据类型,如:int,long,float    等,也同时含有非基本数据类型如(数组,集合等)
被克隆得到的对象基本类型的值修改了,原对象的值不会改变.这种适合shadow clone(浅克隆).


但如果你要改变一个非基本类型的值时,原对象的值却改变了,.比如一个数组,内存中只copy他的地址,而这个地址指向的值并没有copy,当clone时,两个地址指向了一个值,这样一旦这个值改变了,原来的值当然也变了,因为他们共用一个值.,这就必须得用深克隆(deep clone)


以下举个例子,说明以上情况.

被克隆类:ShadowClone.java

  1. package lc.clone.shadow;
  2. public class ShadowClone implements Cloneable
  3. {
  4.     // 基本类型
  5.     private int a;
  6.     // 非基本类型
  7.     private String b;
  8.     // 非基本类型
  9.     private int[] c;
  10.     // 重写Object.clone()方法,并把protected改为public
  11.     @Override
  12.     public Object clone()
  13.     {
  14.         ShadowClone sc = null;
  15.         try
  16.         {
  17.             sc = (ShadowClone) super.clone();
  18.         } catch (CloneNotSupportedException e)
  19.         {
  20.             e.printStackTrace();
  21.         }
  22.         return sc;
  23.     }
  24.     public int getA()
  25.     {
  26.         return a;
  27.     }
  28.     public void setA(int a)
  29.     {
  30.         this.a = a;
  31.     }
  32.     public String getB()
  33.     {
  34.         return b;
  35.     }
  36.     public void setB(String b)
  37.     {
  38.         this.b = b;
  39.     }
  40.     public int[] getC()
  41.     {
  42.         return c;
  43.     }
  44.     public void setC(int[] c)
  45.     {
  46.         this.c = c;
  47.     }
  48. }

测试类Test.java

 

  1. package lc.clone.shadow;
  2. public class Test
  3. {
  4.     public static void main(String[] args) throws CloneNotSupportedException
  5.     {
  6.         ShadowClone c1 = new ShadowClone();
  7.         //对c1赋值
  8.         c1.setA(100) ;
  9.         c1.setB("clone1") ;
  10.         c1.setC(new int[]{1000}) ;
  11.         
  12.         System.out.println("克隆前: c1.a="+c1.getA() );
  13.         System.out.println("克隆前: c1.b="+c1.getB() );
  14.         System.out.println("克隆前: c1.c[0]="+c1.getC()[0]);
  15.         System.out.println("-----------") ;
  16.         
  17.         //克隆出对象c2,并对c2的属性A,B,C进行修改
  18.         
  19.         ShadowClone c2 = (ShadowClone) c1.clone();
  20.         
  21.         //对c2进行修改
  22.         c2.setA(50) ;
  23.         c2.setB("clone2");
  24.         int []a = c2.getC() ;
  25.         a[0]=500 ;
  26.         c2.setC(a);
  27.         
  28.         System.out.println("克隆后: c1.a="+c1.getA() );
  29.         System.out.println("克隆后: c1.b="+c1.getB() );
  30.         System.out.println("克隆后: c1.c[0]="+c1.getC()[0]);
  31.         System.out.println("---------------") ;
  32.         
  33.         System.out.println("克隆后: c2.a=" + c2.getA());
  34.         System.out.println("克隆后: c2.b=" + c2.getB());
  35.         System.out.println("克隆后: c2.c[0]=" + c2.getC()[0]);
  36.     }
  37. }

结果:
克隆前: c1.a=100
克隆前: c1.b=clone1
克隆前: c1.c[0]=1000
-----------
克隆后: c1.a=100
克隆后: c1.b=clone1
克隆后: c1.c[0]=500
---------------
克隆后: c2.a=50
克隆后: c2.b=clone2
克隆后: c2.c[0]=500

 

问题出现了,我指修改了克隆后的对象c2.c的值,但c1.c的值也改变了,与c2的值相等.
以下针对浅克隆得出结论:基本类型是可以被克隆的,但引用类型只是copy地址,并没有copy这个地址指向的对象的值,这使得两个地址指向同一值,修改其中一个,当然另一个也就变了.
由此可见,浅克隆只适合克隆基本类型,对于引用类型就不能实现克隆了.

那如何实现克隆引用对象呢,以下提供一种方法.    用序列化与反序列化实现深克隆(deep copy)

被克隆对象.DeepClone.java

 

  1. package lc.clone.deep;
  2. import java.io.Serializable;
  3. //要实现深克隆必须实现Serializable接口
  4. public class DeepClone implements Serializable
  5. {
  6.     private int a;
  7.     private String b;
  8.     private int[] c;
  9.     public int getA()
  10.     {
  11.         return a;
  12.     }
  13.     public void setA(int a)
  14.     {
  15.         this.a = a;
  16.     }
  17.     public String getB()
  18.     {
  19.         return b;
  20.     }
  21.     public void setB(String b)
  22.     {
  23.         this.b = b;
  24.     }
  25.     public int[] getC()
  26.     {
  27.         return c;
  28.     }
  29.     public void setC(int[] c)
  30.     {
  31.         this.c = c;
  32.     }
  33. }

测试类Test.java

 

  1. package lc.clone.deep;
  2. import java.io.ByteArrayInputStream;
  3. import java.io.ByteArrayOutputStream;
  4. import java.io.IOException;
  5. import java.io.ObjectInputStream;
  6. import java.io.ObjectOutputStream;
  7. public class Test
  8. {
  9.     public static void main(String[] args) throws CloneNotSupportedException
  10.     {
  11.         Test t = new Test();
  12.         DeepClone dc1 = new DeepClone();
  13.         // 对dc1赋值
  14.         dc1.setA(100);
  15.         dc1.setB("clone1");
  16.         dc1.setC(new int[] { 1000 });
  17.         System.out.println("克隆前: dc1.a=" + dc1.getA());
  18.         System.out.println("克隆前: dc1.b=" + dc1.getB());
  19.         System.out.println("克隆前: dc1.c[0]=" + dc1.getC()[0]);
  20.         System.out.println("-----------");
  21.         DeepClone dc2 = (DeepClone) t.deepClone(dc1);
  22.         // 对c2进行修改
  23.         dc2.setA(50);
  24.         dc2.setB("clone2");
  25.         int[] a = dc2.getC();
  26.         a[0] = 500;
  27.         dc2.setC(a);
  28.         System.out.println("克隆前: dc1.a=" + dc1.getA());
  29.         System.out.println("克隆前: dc1.b=" + dc1.getB());
  30.         System.out.println("克隆前: dc1.c[0]=" + dc1.getC()[0]);
  31.         System.out.println("-----------");
  32.         System.out.println("克隆后: dc2.a=" + dc2.getA());
  33.         System.out.println("克隆后: dc2.b=" + dc2.getB());
  34.         System.out.println("克隆后: dc2.c[0]=" + dc2.getC()[0]);
  35.     }
  36.     // 用序列化与反序列化实现深克隆
  37.     public Object deepClone(Object src)
  38.     {
  39.         Object o = null;
  40.         try
  41.         {
  42.             if (src != null)
  43.             {
  44.                 ByteArrayOutputStream baos = new ByteArrayOutputStream();
  45.                 ObjectOutputStream oos = new ObjectOutputStream(baos);
  46.                 oos.writeObject(src);
  47.                 oos.close();
  48.                 ByteArrayInputStream bais = new ByteArrayInputStream(baos
  49.                         .toByteArray());
  50.                 ObjectInputStream ois = new ObjectInputStream(bais);
  51.                 o = ois.readObject();
  52.                 ois.close();
  53.             }
  54.         } catch (IOException e)
  55.         {
  56.             e.printStackTrace();
  57.         } catch (ClassNotFoundException e)
  58.         {
  59.             e.printStackTrace();
  60.         }
  61.         return o;
  62.     }
  63. }

结果:
克隆前: dc1.a=100
克隆前: dc1.b=clone1
克隆前: dc1.c[0]=1000
-----------
克隆前: dc1.a=100
克隆前: dc1.b=clone1
克隆前: dc1.c[0]=1000
-----------
克隆后: dc2.a=50
克隆后: dc2.b=clone2
克隆后: dc2.c[0]=500

 

深克隆后:修改dc1或者dc2,无论是基本类型还是引用类型,他们的值都不会随着一方改变另一方也改变.

 

总结:当克隆的对象只有基本类型,不含引用类型时,可以用浅克隆实现.
     当克隆的对象含有引用类型时,必须使用深克隆实现.