java的clone()、浅拷贝与深拷贝

     clone()方法是Object的native方法。protected native Object clone() throws CloneNotSupportedException;  声明为protected,表明子类必须重新实现该方法,除非是与Obeject类在一个包里,后者是不可能的。而实际上,作为native方法clone()已经有一份field to field的浅拷贝实现,实际上是不需要一定重写的。这种情况下,需要的做法就是覆写clone()方法,在方法里通过super.clone()调用Object的clone()。

     而Cloneable是标记型接口,实现了Cloneable才可以实现clone()方法。否则使用clone()方法会报错。

     下面是ArrayList的clone()

    /**
     * Returns a shallow copy of this <tt>ArrayList</tt> instance.  (The
     * elements themselves are not copied.)
     *
     * @return a clone of this <tt>ArrayList</tt> instance
     */
    public Object clone() {
        try {
            ArrayList<?> v = (ArrayList<?>) super.clone();
            v.elementData = Arrays.copyOf(elementData, size);
            v.modCount = 0;
            return v;
        } catch (CloneNotSupportedException e) {
            // this shouldn't happen, since we are Cloneable
            throw new InternalError(e);
        }
    }

 

     这里为何不是简单的super.clone()浅拷贝呢?因为成员变量是复杂类型时(涉及成员变量为对象的引用),就需要深拷贝。

     下面做个小实验,先使用浅拷贝,验证普通的成员变量是ok的:

package a;

public class CloneTest  implements Cloneable {
    private int v_a;
    public void setV_a(int v) {
        v_a = v;
    }
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
    public void print_v_a() {
        System.out.println(v_a);
    }
    public static void main(String[] args) throws CloneNotSupportedException {
        CloneTest ct0 = new CloneTest();
        ct0.setV_a(66);
        CloneTest ct1 = (CloneTest)ct0.clone();
        ct1.print_v_a();
        ct0.setV_a(88);
        ct1.print_v_a();
        ct0.print_v_a();
    }
}

     先设置ct0的v_a对象为66,然后ct1对象是ct0的拷贝,打印ct1的v_a,也为66,说明拷贝成功。 之后重新设置ct0的值为88,ct1的值没变还是66。

package a;

import java.util.Arrays;

class A {
    private int v;
    public void setV(int v) {
        this.v = v;
    }
    public void p_v() {
        System.out.println(v);
    }
}

public class CloneTest  implements Cloneable {
    private A v_a;
    public void setV_a(A v) {
        v_a = v;
    }
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
    public void print_v_a() {
        v_a.p_v();
    }
    public static void main(String[] args) throws CloneNotSupportedException {
        CloneTest ct0 = new CloneTest();
        A a = new A();
        a.setV(66);
        ct0.setV_a(a);
        CloneTest ct1 = (CloneTest)ct0.clone();
        ct1.print_v_a();
        a.setV(88);
        ct0.setV_a(a);
        ct1.print_v_a();
    }
}

     上面这个例子就体现出了浅拷贝的弱点,输出为66 88。

     ct1是浅拷贝的ct0,此时ct0的v_a(成员变量,A对象)的v值为66。拷贝后,ct1的v_a(A对象的v)输出也是66。然后重设ct0的v_a(A对象的v)为88,再输出ct1的v_a(A对象的v),竟然也是88。说明二者的引用指向的是同样的堆内存。

     浅拷贝情况下,两个对象的成员变量(A对象)引用的是同一个堆内存,并没有完全实现拷贝后内存独立。

     这种情况就需要深拷贝。

     例如文章最开始提到的ArrayList的clone()的写法。

posted on 2017-03-21 17:32  J·Marcus  阅读(421)  评论(0编辑  收藏  举报

导航