java对象如果想被克隆,它对应的类需要implements标志接口Cloneable。如果不重写clone()方法,则在调用clone()方法实现的是浅复制(所有的引用对象保持不变,意思是如果原型里这些对象发生改变会直接影响到复制对象)。重写clone()方法,一般会先调用super.clone()进行浅复制,然后再复制那些易变对象,从而达到深复制的效果。千言万语不如代码:
public class CloneTest implements Cloneable{
private byte[] a = {1, 2, 3, 4};
private byte[] b = {5, 6, 7, 8};
public CloneTest clone() {
CloneTest copy = null;
try {
copy = (CloneTest) super.clone();
copy.a = this.a.clone();
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return copy;
}
// 习惯用上面
// @Override
// protected Object clone() throws CloneNotSupportedException {
// // TODO Auto-generated method stub
// CloneTest copy = (CloneTest) super.clone();
// copy.a = this.a.clone();
// return copy;
// }
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();
System.out.println("original.a == cloned.a : " + (original.getA() == cloned.getA()));
System.out.println("cloned.a[3] = " + cloned.getA()[3]);
original.getA()[3] = 9;
System.out.println("cloned.a[3] = " + cloned.getA()[3]);
System.out.println("original.b == cloned.b : " + (original.getB() == cloned.getB()));
System.out.println("cloned.b[3] = " + cloned.getB()[3]);
original.b[3] = 10;
System.out.println("cloned.b[3] = " + cloned.getB()[3]);
}
}
original.a == cloned.a : false
cloned.a[3] = 4
cloned.a[3] = 4
original.b == cloned.b : true
cloned.b[3] = 8
cloned.b[3] = 10
super.clone(),这个操作主要是来做一次bitwise copy( binary copy ),即浅拷贝,他会把原对象完整的拷贝过来包括其中的引用。这样会带来问题,如果里面的某个属性是个可变对象,那么原来的对象改变,克隆的对象也跟着改变。所以在调用完super.clone()后,一般还需要重新拷贝可变对象。
调用super.clone()后返回的对象满足以下:
x.clone != x
x.clone.getClass() == x.getClass()
以下摘录R大在知乎的回答:
JavaDoc这段
* The method {@code clone} for class {@code Object} performs a
* specific cloning operation. First, if the class of this object does
* not implement the interface {@code Cloneable}, then a
* {@code CloneNotSupportedException} is thrown. Note that all arrays
* are considered to implement the interface {@code Cloneable} and that
* the return type of the {@code clone} method of an array type {@code T[]}
* is {@code T[]} where T is any reference or primitive type.
* Otherwise, this method creates a new instance of the class of this
* object and initializes all its fields with exactly the contents of
* the corresponding fields of this object, as if by assignment; the
* contents of the fields are not themselves cloned. Thus, this method
* performs a "shallow copy" of this object, not a "deep copy" operation.
也就是说JavaDoc指明了Object.clone()有特殊的语义,他就是能把当前对象的整个结构完全浅拷贝一份出来。
* By convention, the returned object should be obtained by calling
* {@code super.clone}. If a class and all of its superclasses (except
* {@code Object}) obey this convention, it will be the case that
* {@code x.clone().getClass() == x.getClass()}.
每层clone()都顺着 super.clone() 的链向上调用的话最终就会来到Object.clone() ,于是根据上述的特殊语义就可以有 x.clone.getClass() == x.getClass() 。
至于如何实现的,可以把JVM原生实现的Object.clone()的语义想象成拿到this引用后通过反射去找到该对象实例的所有字段,然后逐一字段拷贝。
HotSpot vm中,Object.clone()在不同的优化层级上有不同的实现。在其中最不优化的版本是这样做的:拿到this引用,通过对象头里记录的Klass信息去找出这个对象有多大,然后直接分配一个新的同样大的空对象并且把Klass信息塞进对象头(这样就已经实现了x.clone.getClass() == x.getClass()这部分语义),然后直接把对象体 的内容看作数组拷贝一样从源对象“盲”拷贝到目标对象,bitwise copy。然后就完事啦。
-----------------------------------------------------------------------------------
我的理解是super.clone() 的调用就是沿着继承树不断网上递归调用直到Object 的clone方法,而跟据JavaDoc所说Object.clone()根据当前对象的类型创建一个新的同类型的空对象,然后把当前对象的字段的值逐个拷贝到新对象上,然后返回给上一层clone() 调用。
也就是说super.clone() 的浅复制效果是通过Object.clone()实现的。
网友评论