这个方法的要求是对象(包括被引用对象)必须事先了Serializable接口,否则就要用transient关键字将其排除在复制过程中。
克隆就是复制一个对象的复本.但一个对象中可能有基本数据类型,如:int,long,float 等,也同时含有非基本数据类型如(数组,集合等)
被克隆得到的对象基本类型的值修改了,原对象的值不会改变.这种适合shadow clone(浅克隆).
但如果你要改变一个非基本类型的值时,原对象的值却改变了,.比如一个数组,内存中只copy他的地址,而这个地址指向的值并没有copy,当clone时,两个地址指向了一个值,这样一旦这个值改变了,原来的值当然也变了,因为他们共用一个值.,这就必须得用深克隆(deep clone)
以下举个例子,说明以上情况:
被克隆类:ShadowClone.javaclass ShallowClone implements Cloneable { public int a; public int[] b; public ShallowClone() { a = 5; b = new int[] {1, 2, 3, 4, 5}; } // 浅克隆,对于克隆后的对象,只能保证对基础类型成员的修改不会影响原对象的相应成员 // 对类类型和数组类型的成员,只是拷贝了对象的地址,因此对克隆后对象的这些类型成员 // 进行修改会影响原对象 @Override public Object clone() { ShallowClone sc = null; try { sc = (ShallowClone)super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return sc; } }
public class DeepAndShallowClone { public static void main(String[] args) throws Exception { // shallow clone ShallowClone sc = new ShallowClone(); ShallowClone scCopy = (ShallowClone)sc.clone(); System.out.println("Shallow Copy"); System.out.println("--Before clone"); System.out.println("sc.a=" + sc.a); System.out.println("sc.b=" + sc.b[0]); scCopy.a = 1; scCopy.b[0] = 10; System.out.println("--After clone"); System.out.println("sc.a=" + sc.a); System.out.println("sc.b=" + sc.b[0]); System.out.println("scCopy.a=" + scCopy.a); System.out.println("scCopy.b=" + scCopy.b[0]); } }
结果如下:
问题出现了,修改了克隆后的对象scCopy.b[0]的值,但sc.b[0]的值也改变了,与scCopy.b[0]的值相等.
以下针对浅克隆得出结论:基本类型是可以被克隆的,但引用类型只是copy地址,并没有copy这个地址指向的对象的值,这使得两个地址指向同一值,修改其中一个,当然另一个也就变了.
由此可见,浅克隆只适合克隆基本类型,对于引用类型就不能实现克隆了.
那如何实现克隆引用对象呢,以下提供一种方法. 用序列化与反序列化实现深克隆(deep copy)
被克隆对象.DeepClone.java
class DeepClone implements Serializable { private static final long serialVersionUID = 1L; public int a; public int[] b; public DeepClone() { a = 10; b = new int[] {6, 7, 8, 9, 10}; } // 使用ObjectInput(Output)Stream和ByteArrayInput(Output)Stream实现深克隆 public Object deepClone() throws IOException, ClassNotFoundException { DeepClone dc = null; ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(this); oos.close(); ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); ObjectInputStream bis = new ObjectInputStream(bais); dc = (DeepClone)bis.readObject(); return dc; } }
public class DeepAndShallowClone { public static void main(String[] args) throws Exception { DeepClone dc = new DeepClone(); DeepClone dcCopy = (DeepClone)dc.deepClone(); System.out.println("--Before clone"); System.out.println("dc.a=" + dc.a); System.out.println("dc.b=" + dc.b[0]); dcCopy.a = 1; dcCopy.b[0] = 1; System.out.println("Shallow Copy"); System.out.println("--After clone"); System.out.println("dc.a=" + dc.a); System.out.println("dc.b=" + dc.b[0]); System.out.println("dcCopy.a=" + dcCopy.a); System.out.println("dcCopy.b=" + dcCopy.b[0]); } }
writeObject方法会将被克隆类的对象关系网都写出,这样就可以实现深克隆。当然,每个被克隆的成员类型必须实现Serializable接口