super.clone()做了什么

2017-09-03  本文已影响0人  囧囧有神2号

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()实现的。

上一篇下一篇

猜你喜欢

热点阅读