最近在学习设计模式中的prototype(原型)模式,提出了浅克隆与深克隆,当时甚是不解.于是在网上找了些资料,研究一下,才顿开茅塞.以下是笔人的一点小经验,可提供大家参考,不到之处还请大家点评.
克隆就是复制一个对象的复本.但一个对象中可能有基本数据类型,如:int,long,float 等,也同时含有非基本数据类型如(数组,集合等)
被克隆得到的对象基本类型的值修改了,原对象的值不会改变.这种适合shadow clone(浅克隆).
但如果你要改变一个非基本类型的值时,原对象的值却改变了,.比如一个数组,内存中只copy他的地址,而这个地址指向的值并没有copy,当clone时,两个地址指向了一个值,这样一旦这个值改变了,原来的值当然也变了,因为他们共用一个值.,这就必须得用深克隆(deep clone)
以下举个例子,说明以上情况.
被克隆类:ShadowClone.java
- package lc.clone.shadow;
- public class ShadowClone implements Cloneable
- {
- // 基本类型
- private int a;
- // 非基本类型
- private String b;
- // 非基本类型
- private int[] c;
- // 重写Object.clone()方法,并把protected改为public
- @Override
- public Object clone()
- {
- ShadowClone sc = null;
- try
- {
- sc = (ShadowClone) super.clone();
- } catch (CloneNotSupportedException e)
- {
- e.printStackTrace();
- }
- return sc;
- }
- public int getA()
- {
- return a;
- }
- public void setA(int a)
- {
- this.a = a;
- }
- public String getB()
- {
- return b;
- }
- public void setB(String b)
- {
- this.b = b;
- }
- public int[] getC()
- {
- return c;
- }
- public void setC(int[] c)
- {
- this.c = c;
- }
- }
测试类Test.java
- package lc.clone.shadow;
- public class Test
- {
- public static void main(String[] args) throws CloneNotSupportedException
- {
- ShadowClone c1 = new ShadowClone();
- //对c1赋值
- c1.setA(100) ;
- c1.setB("clone1") ;
- c1.setC(new int[]{1000}) ;
- System.out.println("克隆前: c1.a="+c1.getA() );
- System.out.println("克隆前: c1.b="+c1.getB() );
- System.out.println("克隆前: c1.c[0]="+c1.getC()[0]);
- System.out.println("-----------") ;
- //克隆出对象c2,并对c2的属性A,B,C进行修改
- ShadowClone c2 = (ShadowClone) c1.clone();
- //对c2进行修改
- c2.setA(50) ;
- c2.setB("clone2");
- int []a = c2.getC() ;
- a[0]=500 ;
- c2.setC(a);
- System.out.println("克隆后: c1.a="+c1.getA() );
- System.out.println("克隆后: c1.b="+c1.getB() );
- System.out.println("克隆后: c1.c[0]="+c1.getC()[0]);
- System.out.println("---------------") ;
- System.out.println("克隆后: c2.a=" + c2.getA());
- System.out.println("克隆后: c2.b=" + c2.getB());
- System.out.println("克隆后: c2.c[0]=" + c2.getC()[0]);
- }
- }
结果:
克隆前: 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
- package lc.clone.deep;
- import java.io.Serializable;
- //要实现深克隆必须实现Serializable接口
- public class DeepClone implements Serializable
- {
- private int a;
- private String b;
- private int[] c;
- public int getA()
- {
- return a;
- }
- public void setA(int a)
- {
- this.a = a;
- }
- public String getB()
- {
- return b;
- }
- public void setB(String b)
- {
- this.b = b;
- }
- public int[] getC()
- {
- return c;
- }
- public void setC(int[] c)
- {
- this.c = c;
- }
- }
测试类Test.java
- package lc.clone.deep;
- import java.io.ByteArrayInputStream;
- import java.io.ByteArrayOutputStream;
- import java.io.IOException;
- import java.io.ObjectInputStream;
- import java.io.ObjectOutputStream;
- public class Test
- {
- public static void main(String[] args) throws CloneNotSupportedException
- {
- Test t = new Test();
- DeepClone dc1 = new DeepClone();
- // 对dc1赋值
- dc1.setA(100);
- dc1.setB("clone1");
- dc1.setC(new int[] { 1000 });
- System.out.println("克隆前: dc1.a=" + dc1.getA());
- System.out.println("克隆前: dc1.b=" + dc1.getB());
- System.out.println("克隆前: dc1.c[0]=" + dc1.getC()[0]);
- System.out.println("-----------");
- DeepClone dc2 = (DeepClone) t.deepClone(dc1);
- // 对c2进行修改
- dc2.setA(50);
- dc2.setB("clone2");
- int[] a = dc2.getC();
- a[0] = 500;
- dc2.setC(a);
- System.out.println("克隆前: dc1.a=" + dc1.getA());
- System.out.println("克隆前: dc1.b=" + dc1.getB());
- System.out.println("克隆前: dc1.c[0]=" + dc1.getC()[0]);
- System.out.println("-----------");
- System.out.println("克隆后: dc2.a=" + dc2.getA());
- System.out.println("克隆后: dc2.b=" + dc2.getB());
- System.out.println("克隆后: dc2.c[0]=" + dc2.getC()[0]);
- }
- // 用序列化与反序列化实现深克隆
- public Object deepClone(Object src)
- {
- Object o = null;
- try
- {
- if (src != null)
- {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- ObjectOutputStream oos = new ObjectOutputStream(baos);
- oos.writeObject(src);
- oos.close();
- ByteArrayInputStream bais = new ByteArrayInputStream(baos
- .toByteArray());
- ObjectInputStream ois = new ObjectInputStream(bais);
- o = ois.readObject();
- ois.close();
- }
- } catch (IOException e)
- {
- e.printStackTrace();
- } catch (ClassNotFoundException e)
- {
- e.printStackTrace();
- }
- return o;
- }
- }
结果:
克隆前: 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,无论是基本类型还是引用类型,他们的值都不会随着一方改变另一方也改变.
总结:当克隆的对象只有基本类型,不含引用类型时,可以用浅克隆实现.
当克隆的对象含有引用类型时,必须使用深克隆实现.