Object 类有一个受保护的 clone() 方法声明,以便所有类在需要时都可以克隆自身。当需要类的新的实例,同时又需要保持与原始对象相同的状态时,通常使用 clone()。任何想要启用克隆的类都必须实现标记接口 Cloneable。
如果实现 Cloneable 的类没有重写 Object.clone() 方法,则将调用 Object.clone() 方法来只创建一个原始对象的二进制副本,这相当于一个浅克隆,其中所有引用的对象保持不变。这意味着原始对象中的所有引用(包括私有引用)都将被复制到克隆对象中。Object.clone() 是一个本地方法,它执行浅复制。更多信息,请参见 Object.clone() 源代码。
如果实现 Cloneable 的类确实重写了 Object.clone() 方法,通常会首先调用super.clone() 来创建原始对象的二进制副本,然后根据二进制副本执行深度复制。请参见下面的示例:
public class CloneTest implements Cloneable {
private byte[] a = {1, 2, 3, 4, 5};
private byte[] b = {5, 4, 3, 2, 1};
public CloneTest clone(){
CloneTest that = null;
try{
that = (CloneTest)super.clone(); //创建二进制副本
that.b = this.b.clone(); //执行自定义操作
return that;
} catch (CloneNotSupportedException ex){
ex.printStackTrace();
}
return that;
}
public byte[] getA(){
return this.a;
}
public byte[] getB(){
return this.b;
}
public static void main(String[] args){
CloneTest original = new CloneTest();
CloneTest cloned = original.clone();
//关于 original.a
System.out.println("original.a == cloned.a : " + (original.getA() == cloned.getA()));
System.out.println("cloned.a[2] = " + cloned.getA()[2]);
//修改 original.a[2]
original.getA()[2] = 10;
System.out.println("cloned.a[2] = " + cloned.getA()[2]);
//关于 original.b
System.out.println("original.b == cloned.b : " + (original.getB() == cloned.getB()));
System.out.println("cloned.b[2] = " + cloned.getB()[2]);
//修改 original.b[2]
original.getB()[2] = 10;
System.out.println("cloned.b[2] = " + cloned.getB()[2]);
}
}
你是否对 super.clone() 方法感到困惑?为什么 super.clone() 可以转换为 CloneTest?通常 super 表示父类,这意味着我们只会得到父类实例的副本,对吗?然后对象被向下转换为对象,这应该是不正确的,对吗?事实上,super.clone() 将返回调用 super.clone() 方法的对象。要理解原因,请仔细阅读 Object.clone() 方法的作用。Object 中 clone() 的实现会检查实际类是否实现了 Cloneable,并创建该实际类的实例。这意味着下面的关系应该是正确的:
x.clone() != x x.clone().getClass() == x.getClass()
关于 clone() 的另一个重要说明是,在重写 clone() 方法时,应显式克隆任何可变对象。上面的示例有一个会引起问题的错误。可变对象数组a没有被显式克隆,因此克隆对象将与原始对象共享相同的数组引用。如果数组a中的元素发生更改,此更改也会反映在克隆对象中。
程序执行的结果显示了这种确切的行为:
original.a == cloned.a : true cloned.a[2] = 3 cloned.a[2] = 10 original.b == cloned.b : false cloned.b[2] = 3 cloned.b[2] = 3
从输出中,original.a == cloned.a : true 表示对数组 a 的引用在原始对象和克隆对象中是相同的。并且 cloned.a[2] 也被更改为 10。
为了减轻这个问题,需要在 clone(0 方法中添加 that.a = this.a.clone();。
关于使用 clone() 的一个一般性提示是在重写 clone() 方法时要格外小心。检查类中的每个可变对象,并查看是否需要对其进行深度复制。尽可能不要使用 clone()。
No comment for this article.