以《西遊記》蓮花洞奪寶為隱喻,示範 Object.clone() 淺拷貝導致武器引用共享,以及深拷貝實現武器獨立。
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)複製值,可變物件(武器陣列、武器實例)複製引用。故兩個猴王共用同一武器陣列與武器物件。
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() 預設為淺拷貝,可變物件欄位會共享引用。Cloneable 並覆寫 clone()。clone() 中逐一深拷貝可變欄位。new ArrayList<>(old)),更清晰、安全。