Java 中的淺拷貝與深拷貝
[toc]
參考:Java 中的淺拷貝與深拷貝
詳細(xì)源碼介紹和示例看:JavaSE學(xué)習(xí)隨筆(一) Cloneable接口源碼分析與技術(shù)細(xì)節(jié)
總結(jié):
java中clone方法是淺拷貝
實現(xiàn)Clonable接口,重寫clone方法的是深拷貝
基本數(shù)據(jù)類型:直接拷貝
對象類型:引用拷貝、對象拷貝
深拷貝和淺拷貝都是對象拷貝
詳情
什么是拷貝?
引用拷貝:
正如它的名稱所表述的意思, 就是創(chuàng)建一個指向?qū)ο蟮囊米兞康目截?。如果我們有一個 Car 對象,而且讓 myCar 變量指向這個變量,這時候當(dāng)我們做引用拷貝,那么現(xiàn)在就會有兩個 myCar 變量,但是對象仍然只存在一個。

對象拷貝:
會創(chuàng)建對象本身的一個副本。因此如果我們再一次服務(wù)我們 car 對象,就會創(chuàng)建這個對象本身的一個副本, 同時還會有第二個引用變量指向這個被復(fù)制出來的對象。

備注:深拷貝和淺拷貝都是對象拷貝
淺拷貝
首先讓我們來說說淺拷貝。對象的淺拷貝會對“主”對象進(jìn)行拷貝,但不會復(fù)制主對象里面的對象。"里面的對象“會在原來的對象和它的副本之間共享。例如,我們會為一個 Person對象創(chuàng)建第二個 Person 對象, 而兩個 Person 會共享相同的 Name 和 Address 對象。

在一下示例 中,我們有一個類 Person,類里面包含了一個 Name 和 Address 對象??截悩?gòu)造器會拿到 originalPerson 對象,然后對其應(yīng)用變量進(jìn)行復(fù)制。
public class Person {
private Name name;
private Address address;
public Person(Person originalPerson) {
this.name = originalPerson.name;
this.address = originalPerson.address;
}
[…]
}
淺拷貝的問題就是兩個對象并非獨立的。如果你修改了其中一個 Person 對象的 Name 對象,那么這次修改也會影響奧另外一個 Person 對象。
讓我們在示例中看看這個問題。假如說我們有一個 Person 對象,然后也會有一個引用變量 monther 來指向它;然后當(dāng)我們對 mother 進(jìn)行拷貝時,創(chuàng)建第二個 Person 對象 son。如果在此后的代碼中, son 嘗試用 moveOut() 來修改他的 Address 對象, 那么 mother 也會跟著他一起搬走!
Person mother = new Person(new Name(…), new Address(…));
[…]
Person son = new Person(mother);
[…]
son.moveOut(new Street(…), new City(…));
這種現(xiàn)象之所以會發(fā)生,是因為 mother 和son 對象共享了相同的 Address 對象,如你在示例中所看到的描述。當(dāng)我們在一個對象中修改了 Address 對象,那么也就表示兩個對象總的 Address 都被修改了。
深拷貝
不同于淺拷貝,深拷貝是一個整個獨立的對象拷貝。如果我們對整個 Person對象進(jìn)行深拷貝,我們會對整個對象的結(jié)構(gòu)都進(jìn)行拷貝。

如上圖所見,對一個 Person 的Address對象進(jìn)行了修改并不會對另外一個對象造成影響。當(dāng)我們觀察示如下代碼,會發(fā)現(xiàn)我們不單單對 Person 對象使用了拷貝構(gòu)造器,同時也會對里面的對象使用拷貝構(gòu)造器。
public class Person {
private Name name;
private Address address;
public Person(Person otherPerson) {
this.name = new Name(otherPerson.name);
this.address = new Address(otherPerson.address);
}
[…]
}
使用這種深拷貝,我們可以重新嘗試之前的示例中的 mother-son 這個用例?,F(xiàn)在 son 可以成功的搬走了!
不過,故事到這兒并沒有結(jié)束。要創(chuàng)建一個真正的深拷貝,就需要我們一直這樣拷貝下去,一直覆蓋到 Person 對象所有的內(nèi)部元素, 最后只剩下原始的類型以及“不可變對象(Immutables)”。讓我們觀察下如下這個 Street 類以獲得更好的理解:
public class Street {
private String name;
private int number;
public Street(Street otherStreet){
this.name = otherStreet.name;
this.number = otherStreet.number;
}
[…]
}
Street 對象有兩個實體變量組成 – String 類型的 name 以及 int 類型的 number。int 類型的 number 是一個原始類型,并非對象。它只是一個簡單的值,不能共享, 因此在創(chuàng)建第二個實體變量時,我們可以自動創(chuàng)建一個獨立的拷貝。String 是一個不可變對象(Immutable)。簡言之,不可變對象也是對象,可一旦創(chuàng)建好了以后就再也不能被修改了。因此,你可以不用為其創(chuàng)建深拷貝就能對其進(jìn)行共享。