首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Effective Java 核心實戰:淺拷貝與深拷貝的猴王七十二變

     編輯:Java 相關知識

情境:孫悟空克隆道士鬥寶葫蘆

以《西遊記》蓮花洞奪寶為隱喻,示範 Object.clone() 淺拷貝導致武器引用共享,以及深拷貝實現武器獨立。

1. 淺拷貝版本:猴王與道士共用同一根金箍棒

class 猴王 implements Cloneable {
    private String name;
    private 武器[] weapon = new 武器[1]; // 陣列包裝,模擬可變引用

    public 猴王(String name) { this.name = name; }
    public Object 變出一個化身() { 
        try { return clone(); } catch (CloneNotSupportedException e) { return null; }
    }
    @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); }
    // getter/setter 略
}

abstract class 武器 {}
class 金箍棒 extends 武器 {}
class 寶葫蘆 extends 武器 {}

測試驗證:

猴王 齊天大聖 = new 猴王("齊天大聖孫悟空");
齊天大聖.取得武器(new 金箍棒());
猴王 空悟孫道士 = (猴王) 齊天大聖.變出一個化身();
空悟孫道士.改名("空悟孫道士");
空悟孫道士.取得武器(new 寶葫蘆()); // 道士換武器

assertFalse(齊天大聖.的武器() instanceof 金箍棒); // 大聖手中也變成寶葫蘆!
assertTrue(空悟孫道士.的武器() instanceof 寶葫蘆);
assertSame(空悟孫道士.的武器(), 齊天大聖.的武器()); // 同一引用
assertEquals(齊天大聖.名字(), "齊天大聖孫悟空"); // String 不可變,名字獨立
assertEquals(空悟孫道士.名字(), "空悟孫道士");

原因:super.clone() 執行淺拷貝——基本型別與不可變物件(String)複製值,可變物件(武器陣列、武器實例)複製引用。故兩個猴王共用同一武器陣列與武器物件。

2. 深拷貝版本:道士擁有獨立金箍棒

abstract class 武器 implements Cloneable {
    @Override public Object clone() {
        try { return super.clone(); } catch (CloneNotSupportedException e) { return null; }
    }
}
class 金箍棒 extends 武器 { @Override public Object clone() { return super.clone(); } }
class 寶葫蘆 extends 武器 { @Override public Object clone() { return super.clone(); } }

class 猴王 implements Cloneable {
    private String name;
    private 武器 weapon; // 直接持有武器引用

    public Object 變出一個新化身() { // 深拷貝版本
        try {
            猴王 clone = (猴王) super.clone();
            if (weapon != null) clone.weapon = (武器) weapon.clone(); // 關鍵:深拷貝武器
            return clone;
        } catch (CloneNotSupportedException e) { return null; }
    }
    // ...
}

測試驗證:

猴王 齊天大聖 = new 猴王("齊天大聖孫悟空");
齊天大聖.取得武器(new 金箍棒());
猴王 空悟孫道士 = (猴王) 齊天大聖.變出一個新化身();
空悟孫道士.改名("空悟孫道士");
齊天大聖.取得武器(new 寶葫蘆()); // 大聖換武器

assertTrue(空悟孫道士.的武器() instanceof 金箍棒); // 道士仍持金箍棒
assertFalse(空悟孫道士.的武器() instanceof 寶葫蘆);
assertNotSame(空悟孫道士.的武器(), 齊天大聖.的武器()); // 不同引用
assertEquals(齊天大聖.名字(), "齊天大聖孫悟空");
assertEquals(空悟孫道士.名字(), "空悟孫道士");

核心結論

  • Object.clone() 預設為淺拷貝,可變物件欄位會共享引用。
  • 實作深拷貝需:
    1. 所有可變欄位類別實作 Cloneable 並覆寫 clone()。
    2. 在外部類 clone() 中逐一深拷貝可變欄位。
    3. 不可變欄位(String、基本型別、final 物件)無需處理。
  • Effective Java 建議:盡量避免使用 clone,改用複製建構子或靜態工廠方法(如 new ArrayList<>(old)),更清晰、安全。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved