《Java基礎知識》Java 淺拷貝與深拷貝

前言

在學習ArrayList的時候,發現ArrayList繼承了Cloneable接口,於是就想着需要了解一下該接口是什麼作用。

淺拷貝

我們先寫一段代碼,來看看什麼是淺拷貝

public class DogSchool {
    public String address;

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}
public class Dog implements Cloneable{
    public int age;
    public String name;
    public DogSchool dogSchool;

    public Dog(int age, String name){
        this.age = age;
        this.name = name;
    }

    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 DogSchool getDogSchool() {
        return dogSchool;
    }

    public void setDogSchool(DogSchool dogSchool) {
        this.dogSchool = dogSchool;
    }

    @Override
    public Object clone() throws CloneNotSupportedException
    {
        return super.clone();
    }
}
public class CloneTest {

    public static void main(String[] args) throws CloneNotSupportedException {
        /*************** 設置好原對象信息 *****************/
        Dog dogSource = new Dog(12,"li");
        DogSchool dogSchool = new DogSchool();
        dogSchool.setAddress("hangzhou");
        dogSource.setDogSchool(dogSchool);

        /*************** 進行對象拷貝,並且修改原對象的值 *****************/
        Dog dogTarget = (Dog) dogSource.clone();
        dogSource.getDogSchool().setAddress("shanghai");
        dogSource.setAge(14);

        System.out.println("dogSource.age:"+dogSource.getAge());
        System.out.println("dogSource.dogSchool.address:"+dogSource.getDogSchool().getAddress());
        System.out.println("dogTarget.age:"+dogTarget.getAge());
        System.out.println("dogTarget.dogSchool.address:"+dogTarget.getDogSchool().getAddress());
        System.out.println("dogTarget.dog:"+dogSource.getDogSchool());
        System.out.println("dogTarget.dog:"+dogTarget.getDogSchool());
    }
}

運行結果:

從結果看,我們的年齡age拷貝之後對原對象賦值沒有影響到複製出來的對象,但是引用對象裏面的屬性值被修改之後卻影響了複製出來的對象。結論:在淺拷貝下,基本類型是獨立的,拷貝前後互不干擾;引用類型是不獨立的,拷貝前後互干擾;String類型也是獨立的,拷貝前後互不干擾。淺拷貝是拷貝了棧裏面的信息。

這邊對String爲什麼在淺拷貝下,也能不受影響的原因說明一下,因爲String雖然是引用類型,但是String是被final修飾,導致他是不能被修改的,當你修改String的值,是在常量池裏面,又產生一個新的字符串常量,然後引用地址指向新的字符串常量地址。

深拷貝

在上面代碼的基礎上,我們修改一下拷貝的邏輯,只需要修改Dog 類和 DogSchool 類。

DogSchool 加上支持拷貝的接口,並且實現接口

public class DogSchool implements Cloneable{
    public String address;

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

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

Dog 類裏面修改一下拷貝邏輯:

public class Dog implements Cloneable{
    public int age;
    public String name;
    public DogSchool dogSchool;

    public Dog(int age, String name){
        this.age = age;
        this.name = name;
    }

    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 DogSchool getDogSchool() {
        return dogSchool;
    }

    public void setDogSchool(DogSchool dogSchool) {
        this.dogSchool = dogSchool;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        Dog dog = (Dog)super.clone();
        dog.setDogSchool((DogSchool) this.dogSchool.clone());
        return dog;
    }
}

運行結果:

從淺拷貝我們可以看到只有引用類型會出現指向內存地址是同一個的情況,所以我們處理一下引用類型即可。就達到了深拷貝的效果。

總結

上述拷貝是比較簡單和易操作的,同時注意一下他的特性,我們的拷貝還有通過序列化實現的拷貝。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章