是時候捋一捋 Java 的深淺複製了

豆芽59發表於2021-02-01

在開發、刷題、面試中,我們可能會遇到將一個物件的屬性賦值到另一個物件的情況,這種情況就叫做複製。複製與Java記憶體結構息息相關,搞懂Java深淺複製是很必要的!

在物件的複製中,很多初學者可能搞不清到底是複製了引用還是複製了物件。在複製中這裡就分為引用複製、淺複製、深複製進行講述。

引用複製

引用複製會生成一個新的物件引用地址,但是兩個最終指向依然是同一個物件。如何更好的理解引用複製呢?很簡單,就拿我們人來說,通常有個姓名,但是不同場合、人物對我們的叫法可能不同,但我們很清楚哪些名稱都是屬於"我"的!

當然,透過一個程式碼示例讓大家領略一下(為了簡便就不寫get、set等方法):

class Son {

    String name;

    int age;

 

    public Son(String name, int age) {

        this.name = name;

        this.age = age;

    }

}public class test {

    public static void main(String[] args) {

        Son s1 = new Son("son1", 12);

        Son s2 = s1;

        s1.age = 22;

        System.out.println(s1);

        System.out.println(s2);

        System.out.println("s1的age:" + s1.age);

        System.out.println("s2的age:" + s2.age);

        System.out.println("s1==s2" + (s1 == s2));//相等

    }

}

輸出的結果為:

Son@135fbaa4

Son@135fbaa4

s1的age:22

s2的age:22true

淺複製

如何建立一個物件,將目標物件的內容複製過來而不是直接複製引用呢?

這裡先講一下淺複製,淺複製會建立一個新物件,新物件和原物件本身沒有任何關係,新物件和原物件不等,但是新物件的屬性和老物件相同。具體可以看如下區別:

 

如果屬性是基本型別(int,double,long,boolean等),複製的就是基本型別的值;

 

 

如果屬性是引用型別,複製的就是記憶體地址(即複製引用但不復制引用的物件) ,因此如果其中一個物件改變了這個地址,就會 影響到另一個物件。

 

如果用一張圖來描述一下淺複製,

如何實現淺複製呢?也很簡單,就是在需要複製的類上實現Cloneable介面並重寫其clone()方法。

@Overrideprotected Object clone() throws CloneNotSupportedException {

  return super.clone();

}

在使用的時候直接呼叫類的clone()方法即可。具體案例如下:

class Father{

    String name;

    public Father(String name) {

        this.name=name;

    }

    @Override

    public String toString() {

        return "Father{" +

                "name='" + name + '\'' +

                '}';

    }

}class Son implements Cloneable {

    int age;

    String name;

    Father father;

    public Son(String name,int age) {

        this.age=age;

        this.name = name;

    }

    public Son(String name,int age, Father father) {

        this.age=age;

        this.name = name;

        this.father = father;

    }

    @Override

    public String toString() {

        return "Son{" +

                "age=" + age +

                ", name='" + name + '\'' +

                ", father=" + father +

                '}';

    }

    @Override

    protected Son clone() throws CloneNotSupportedException {

        return (Son) super.clone();

    }

}public class test {

    public static void main(String[] args) throws CloneNotSupportedException {

        Father f=new Father("bigFather");

        Son s1 = new Son("son1",13);

        s1.father=f;

        Son s2 = s1.clone();

 

        System.out.println(s1);

        System.out.println(s2);

        System.out.println("s1==s2:"+(s1 == s2));//不相等

        System.out.println("s1.name==s2.name:"+(s1.name == s2.name));//相等

        System.out.println();

 

        //但是他們的Father father 和String name的引用一樣

        s1.age=12;

        s1.father.name="smallFather";//s1.father引用未變

        s1.name="son222";//類似 s1.name=new String("son222") 引用發生變化

        System.out.println("s1.Father==s2.Father:"+(s1.father == s2.father));//相等

        System.out.println("s1.name==s2.name:"+(s1.name == s2.name));//不相等

        System.out.println(s1);

        System.out.println(s2);

    }

}

執行結果為:

Son{age=13, name='son1', father=Father{name='bigFather'}}

Son{age=13, name='son1', father=Father{name='bigFather'}}

s1==s2:false

s1.name==s2.name:true//此時相等

 

s1.Father==s2.Father:true

s1.name==s2.name:false//修改引用後不等

Son{age=12, name='son222', father=Father{name='smallFather'}}

Son{age=13, name='son1', father=Father{name='smallFather'}}

不出意外,這種淺複製除了物件本身不同以外,各個零部件和關係和複製物件都是相同的,就好像雙胞胎一樣,是兩個人,但是其開始的樣貌、各種關係(父母親人)都是相同的。需要注意的是其中name初始==是相等的,是因為初始淺複製它們指向一個相同的String,而後s1.name="son222" 則改變引用指向。

 


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69990088/viewspace-2755272/,如需轉載,請註明出處,否則將追究法律責任。

相關文章