What does super.clone() do?

English 简体中文 Tiếng Việt
Summary

The Object.clone() method provides a shallow, binary copy of an object, necessitating the implementation of the Cloneable marker interface. When overriding clone(), developers typically call super.clone() to obtain this initial shallow copy, then proceed to manually deep copy any mutable fields to ensure independent object states. It's important to note that super.clone() returns an instance of the actual class invoking it, not merely the superclass, which allows for direct casting. A common pitfall is neglecting to explicitly deep copy mutable objects, such as arrays, leading to shared references between the original and cloned instances and potential side effects. Consequently, extreme caution is advised when implementing clone(), often making it preferable to explore alternative object copying mechanisms.

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() 方法的作用。Objectclone() 的实现会检查实际类是否实现了 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()。

JAVA CLONEABLE SUPER.CLONE() CLONE

  RELATED

  COMMENTS

0

No comment for this article.