1. 程式人生 > >Java的淺度克隆和深度克隆

Java的淺度克隆和深度克隆

前言:

protected native Object clone() throws CloneNotSupportedException

方法由protected修飾,只能由子類重寫和呼叫,而且為本地方法。

Object提供clone方法,生成物件的一個副本,來實現物件的一次淺度克隆。但是物件的引用型別不會拷貝一份,引用地址與原物件相同。

實現物件的克隆需要實現Cloneable介面,重寫clone方法,通過super.clone()呼叫Object類的clone方法得到當前物件的副本,並放回該副本。

淺度克隆與深度克隆的區別:

淺度克隆:基本型別都會拷貝一份,引用型別引用地址不變,還是同一個物件。

深度克隆:基本型別和引用型別都會拷貝一份,完完全全的是不同的兩份,引用地址不一樣。

下面結合一個具體的例子:(兩者結合)

定義person類:(實現了Cloneable介面)

class Person implements Cloneable{
    int id;

    public Person(int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
定義Student類(實現Cloneable介面)
public class Student implements Cloneable{
    private int age;  //基本型別
    private String name;  //引用型別,未實現自己的深度拷貝
    private Person person;  //引用型別,Person類實現了Cloneable介面,深度拷貝

    public Student(int age, String name, Person person) {
        this.age = age;
        this.name = name;
        this.person = person;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Person getPerson() {
        return person;
    }

    public void setPerson(Person person) {
        this.person = person;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Student student = (Student) super.clone();  
        Person person = (Person) student.getPerson().clone();  //這個為重點,把student裡面的引用型別person深度克隆
        student.setPerson(person);
        return student;
    }

    public static void main(String[] args) throws Exception{
        Person person = new Person(2);
        Student student1 = new Student(10,"wqh", person);
        Student student2 = (Student) student1.clone();
        System.out.println(student1 == student2);  //false
        System.out.println(student1.getName()==student2.getName()); //true
        System.out.println(student1.getPerson() == student2.getPerson());  //false
    }
}

輸出結果:
false
true
false

分析:

Student中有兩個引用型別:String和Person,在Person中實現了clone介面,在Student的clone方法中我們對person屬性也進行了一次深度克隆,這樣對student1拷貝成student2後,各自的person屬性是單獨的兩份,引用地址不一樣,所以為false,但是string屬性是同一份,故為true。綜述,對student2而言,這是一次不徹底的深度克隆,因為部分引用屬性沒有進行深度克隆。

畫個圖來明瞭下:


student1和student2對應於堆中的同一個name,不同的person。