深拷貝、淺拷貝與Cloneable介面

wunsiang發表於2020-05-01

深拷貝與淺拷貝

淺拷貝

public class Student implements Cloneable{
    Integer a;
    Integer b;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    String c;
    Student child;
}

public class Main {

    public static void main(String[] args) throws CloneNotSupportedException {
        Student s = new Student();
        Student s2 = (Student) s.clone();
        System.out.println(s == s2);
        System.out.println(s.child == s2.child);
    }
}
/**********************************************************************/
false
true

由上述程式碼及執行結果我們可以看出,呼叫clone方法之後,確實s2是一個新的物件,記憶體地址已經發生了改變,但s和s2的child屬性仍然指向相同的地址,這便是淺拷貝,當然8種基本資料型別是深拷貝,String則是例外。

深拷貝

public class Teacher implements Cloneable{
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}


public class Student implements Cloneable{
    Integer a;
    Integer b;

    public Student(Teacher t) {
        this.t = t;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Student s = (Student) super.clone();
        s.t = (Teacher)s.t.clone();//徹底深拷貝關鍵
        return s;
    }

    String c;
    Teacher t;
}


public class IntersectionofTwoArraysII {

    public static void main(String[] args) throws CloneNotSupportedException {
        Teacher t = new Teacher();
        Student s = new Student(t);
        Student s2 = (Student) s.clone();
        System.out.println(s == s2);
        System.out.println(s.t == s2.t);
    }
}
/**********************************************************************/
false
false

如上程式碼執行結果所示為深拷貝,想要實現深拷貝,就需要在重寫Cloneable介面的clone()方法,並呼叫屬性的clone()方法,因此關聯類也要實現Cloneable介面,從而形成拷貝鏈,實現徹底深拷貝
實現徹底的深拷貝並不容易,因為它要求涉及的所有類都實現了cloneable介面,但是如StringBuffer由於其沒有實現Cloneable介面,想要實現深拷貝效果,則需要做特殊處理從而實現深拷貝的效果。可以在clone方法中實現
sb=new StringBuffer(sb.toString());

Cloneable介面

Cloneable其實就是一個標記介面,只有實現這個介面後,然後在類中重寫Object中的clone方法,然後通過類呼叫clone方法才能克隆成功,如果不實現Cloneable其實就是一個標記介面介面,呼叫clone方法則會丟擲CloneNotSupportedException(克隆不被支援)異常。

如何判斷類是否實現了cloneable介面呢?

/**
     * Creates and returns a copy of this {@code Object}. The default
     * implementation returns a so-called "shallow" copy: It creates a new
     * instance of the same class and then copies the field values (including
     * object references) from this instance to the new instance. A "deep" copy,
     * in contrast, would also recursively clone nested objects. A subclass that
     * needs to implement this kind of cloning should call {@code super.clone()}
     * to create the new instance and then create deep copies of the nested,
     * mutable objects.
     *
     * @return a copy of this object.
     * @throws CloneNotSupportedException
     *             if this object's class does not implement the {@code
     *             Cloneable} interface.
     */
    protected Object clone() throws CloneNotSupportedException {
        if (!(this instanceof Cloneable)) {
            throw new CloneNotSupportedException("Class doesn't implement Cloneable");
        }
 
        return internalClone((Cloneable) this);
    }
 
    /*
     * Native helper method for cloning.
     */
    private native Object internalClone(Cloneable o);

clone方法首先會判物件是否實現了Cloneable介面,若無則丟擲CloneNotSupportedException, 最後會呼叫internalClone. intervalClone是一個native方法,一般來說native方法的執行效率高於非native方法。

參考資料

詳解Java中的clone方法 -- 原型模式

相關文章