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()。