转载请注明出处:http://blog.csdn.net/mazhimazh/article/details/16828505
在使用克隆时,我们需要知道使用的目的:就是为了快速构造一个和已有对象相同的副本。如果克隆对象,一般需要先创建一个对象,然后将原对象中的数据导入到新创建的对象中去,而不用根据已有对象进行手动赋值操作。
clone是定义一个Object类下基本方法之一,可以说,任何克隆的过程最终都将到达java.lang.Object 的clone()方法,而其在Object接口中定义如下:
protected native Object clone() throws CloneNotSupportedException;
有如下3点需要提示:
(1)他是一个protected修饰的native方法,因此它的实现是取决于本地代码。native方法的效率一般来说都是远高于java中的非native方法。
(2)Object中的clone方法是protected的,所以要使用clone就必须继承Object类(默认)。并且为了可以使其它类调用该方法,覆写克隆方法时必须将其作用域设置为public.
(3)克隆方法返回的是一个Object对象,所以必须要经过强制类型转换。
通常克隆对象都是通过调用super.clone()方法来获取克隆对象的,所以任何克隆的过程最终都将到达java.lang.Object 的clone()方法。但是在覆写clone()方法时,这个类需要继承Clonable接口,这个接口中没有定义方法,他类似于RandomAccess这些接口类,只做为一种标识存在。如果 clone 类没有实现 Cloneable 接口,并调用了 Object 的 clone() 方法(也就是调用了 super.Clone() 方法),那么Object 的 clone() 方法就会抛出 CloneNotSupportedException 异常。
通过从源码的注释中可以看出,克隆的一些特性:
(1)x.clone() != x 必须为真,也就是对于基础类型来说,其克隆后在堆中有两个独立且内容相同的内存区域。而对于引用类型来说,其引用也不相同。也就是说克隆对象和原始对象在java 堆(heap)中是两个独立的对象
(2)x.clone().getClass() == x.getClass() 他们所属的类是同一个
(3) x.clone().equals(x) 所比较的对象内容相同
从上述的第二和第三点可以看出,克隆完全是拷贝一个独立的副本到内存中。但是由于克隆方法可以覆写,所以并不能保证克隆出来的对象能够达到(2)和(3)要求的标准,所以他们不是克隆方法所必须要求的。
克隆就是复制一个对象的复本.但一个对象中可能有基本数据类型,如:int,long,float等,也同时含有对象数据类型如(数组,集合等)。被克隆得到的对象基本类型的值修改了,原对象的值不会改变.这种适合shadow clone(浅克隆).
但如果你要改变一个非基本类型的值时,原对象的值却改变了,.比如一个数组,内存中只copy他的地址,而这个地址指向的值并没有 copy,当clone时,两个地址指向了一个值,这样一旦这个值改变了,原来的值当然也变了,因为他们共用一个值.,这就必须得用深克隆(deep clone)
public class ShadowClone implements Cloneable{ private int a; // 基本类型 private int[] b; // 非基本类型 // 重写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 int[] getB() { return b; } public void setB(int[] b) { this.b = b; } }
然后进行测试:
public class Test{ public static void main(String[] args) throws CloneNotSupportedException{ ShadowClone c1 = new ShadowClone(); //对c1赋值 c1.setA(100) ; c1.setB(new int[]{1000}) ; System.out.println("克隆前c1: a="+c1.getA()+" b="+c1.getB()[0]); //克隆出对象c2,并对c2的属性A,B,C进行修改 ShadowClone c2 = (ShadowClone) c1.clone(); //对c2进行修改 c2.setA(50) ; int []a = c2.getB() ; a[0]=500 ; c2.setB(a); System.out.println("克隆前c1: a="+c1.getA()+" b="+c1.getB()[0]); System.out.println("克隆后c2: a="+c2.getA()+ " b[0]="+c2.getB()[0]); } }结果为:
克隆前c1: a=100 b=1000
克隆前c1: a=100 b=500
克隆后c2: a=50 b[0]=500
可以看出,基本类型可以使用浅克隆,而对于引用类型,由于引用的是内容相同,所以改变c2实例对象中的属性就会影响到c1。所以引用类型需要使用深克隆。另外,在开发一个不可变类的时候,如果这个不可变类中成员有引用类型,则就需要通过深克隆来达到不可变的目的。
class bottle implements Cloneable { public wine wn; public bottle() { } public bottle(wine wn) { this.wn = wn; } // 覆写clone()方法 protected Object clone() throws CloneNotSupportedException { bottle newBtl = (bottle) super.clone(); return newBtl; } } class wine { int degree; public int getDegree() { return degree; } public void setDegree(int degree) { this.degree = degree; } } public class test04 { public static void main(String[] args) throws CloneNotSupportedException { bottle bottle = new bottle(new wine()); bottle bottle1 = (bottle) bottle.clone(); System.out.println("bottle1.wine : " + bottle1.wn.getDegree() ); bottle1.wn.setDegree(100); System.out.println("bottle1.wine : " + bottle1.wn.getDegree() ); System.out.println("bottle.wine : " + bottle.wn.getDegree()); } }结果如下:
bottle1.wine : 0
bottle1.wine : 100
bottle.wine : 100
这就是浅克隆造成的问题,下面使用clone()来进行深拷贝:
class bottle implements Cloneable { public wine wn; public bottle(wine wn) { this.wn = wn; } // 覆写clone()方法 protected Object clone() throws CloneNotSupportedException { bottle newBtl = (bottle) super.clone(); newBtl.wn = (wine) wn.clone(); return newBtl; } } class wine implements Cloneable { int degree; public int getDegree() { return degree; } public void setDegree(int degree) { this.degree = degree; } // 覆写clone()方法 protected Object clone() throws CloneNotSupportedException { return super.clone(); } }结果如下:
bottle1.wine : 0
bottle1.wine : 100
bottle.wine : 0
结果进行了正确的显示。如果要在wine中还有字符串类型该怎么办?例如:
class wine implements Cloneable { int degree; String name="法国白兰地"; public int getDegree() { return degree; } public void setDegree(int degree) { this.degree = degree; } public String getName() { return name; } public void setName(String name) { this.name = name; } // 覆写clone()方法 protected Object clone() throws CloneNotSupportedException { return super.clone(); } }测试程序如下:
bottle bottle = new bottle(new wine()); bottle bottle1 = (bottle) bottle.clone(); System.out.println("bottle1.wine : " + bottle1.wn.getName() ); bottle1.wn.setName("中国二锅头"); System.out.println("bottle1.wine : " + bottle1.wn.getName() ); System.out.println("bottle.wine : " + bottle.wn.getName());结果如下:
bottle1.wine : 法国白兰地
bottle1.wine : 中国二锅头
bottle.wine : 法国白兰地 为什么会是法国白兰地?
单独对wine进行测试:
wine wn=new wine(); wn.setName("法国葡萄酒"); wine wn2=(wine)wn.clone(); System.out.println(wn.getName()); System.out.println(wn2.getName());则结果如下:
法国葡萄酒
法国葡萄酒
public class DeepClone implements Serializable{ private int a; private int[] b; public int getA() { return a; } public void setA(int a) { this.a = a; } public int[] getB() { return b; } public void setB(int[] b) { this.b = b; } }然后编写测试类:
package test2; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; public class Test2{ public static void main(String[] args) throws CloneNotSupportedException{ Test2 t = new Test2(); DeepClone dc1 = new DeepClone(); // 对dc1赋值 dc1.setA(100); dc1.setB(new int[] { 1000 }); System.out.println("克隆前dc1: a=" + dc1.getA()+"b[0]=" + dc1.getB()[0]); DeepClone dc2 = (DeepClone) t.deepClone(dc1); // 对c2进行修改 dc2.setA(50); int[] a = dc2.getB(); a[0] = 500; System.out.println("克隆后dc1: a=" + dc1.getA()+"b[0]=" + dc1.getB()[0]); System.out.println("克隆后dc2: a=" + dc2.getA()+"b[0]=" + dc2.getB()[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 b[0]=1000
克隆后dc1: a=100 b[0]=1000
克隆后dc2: a=50 b[0]=500
可以看到,两个引用所指向的对象在堆中相互独立,互不干扰,这样就实现了深度克隆。