java 中 deep copy & shallow copy

1. 寫在前面

今天遇到了這樣一個問題,事實上這個問題是之前遇到過的。
java 中列表的賦值的問題。
這個問題核心是 deep copy & shallow copy 的問題


2. 情景再現

public class MikeTest {

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

        class Person{
            private String name;
            private Integer age;

            Person(String name, Integer age){
                this.name = name;
                this.age = age;
            }
        }
        // TODO: 2022/3/8  測試一下這個 bug
        List<Person> rawList = new ArrayList<>();
        Person person1 = new Person("mike",24);
        Person person2 = new Person("John",28);
        rawList.add(person1);
        rawList.add(person2);

        List<Person> updatedList = rawList;
        for (Person person : updatedList){
            person.age = 90;
        }
        System.out.println(updatedList);
        System.out.println(rawList);

    }
}

上述代碼運行之后,問題如下:


bug結果

3. 問題原因

List<Person> updatedList = rawList;

這一句代碼,事實上是將指針給過來了,因此兩個 List 對應的是同樣的內容
update 之后,新的變了,原來的也變了(因為是一個)


4. 怎么解

一開始我以為下面的方式就可以解決(因為大一學c++課的時候,記得有這么講過)

List<Person> updatedList = new ArrayList(rawList);

但是最后搞完了,發現問題還是沒有解決


5. deep copy

后續去查了一下,這里其實是 shallow copy & deep copy 的問題。

shallow copy

這就是在上述 3 部分中所描述的,shallow copy 只會將地址指到相同的地方,不會重新創建對象。

deep copy

deep copy 就是要一份新的引用對象。

實現則需要元素類去實現 Cloneable 接口, @Override 其中的 clone() 方法。
看代碼


/**
 * 實現 Cloneable 的類 對象是人
 * 完成深拷貝
 * 事實上,為了完成深拷貝,是需要被拷貝的元素具有這樣的能力
 *
 * @author mikeshine
 */
@Data
public class Person implements Cloneable{

    /**
     * 名字
     */
    private String name;

    /**
     * 年齡
     */
    private Integer age;

    Person(String name, Integer age){
        this.age = age;
        this.name = name;
    }

    /**
     * clone 方法的核心目的就是 返回一個開辟新地址空間的 對象
     *
     * @return
     * @throws CloneNotSupportedException
     */
    @Override
    protected Person clone() throws CloneNotSupportedException {
        // 首先給新對象開辟一個地址空間
        Person newPerson = (Person)super.clone();
        // 然后依次將屬性 copy
        newPerson.setName(this.name);
        newPerson.setAge(this.age);
        return newPerson;
    }
}

使用如下

  List<Person> rawList = new ArrayList<>();
        Person person1 = new Person("mike",24);
        Person person2 = new Person("John",28);
        rawList.add(person1);
        rawList.add(person2);

        List<Person> updatedList = new ArrayList<>();

        for(Person person : rawList){
            // 這里是重點,相當于用 clone() 方法重新生成一個新對象放在新地址
            updatedList.add(person.clone());
        }
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容