在 Java 開發中,物件複製是常見需求。Object.clone() 作為原生複製機制,雖然使用頻率不如建構式複製或序列化高,但理解其運作原理對掌握 Java 記憶體模型至關重要。本文將從方法簽名、淺拷貝與深拷貝差異、實作範例三個維度全面解析。
protected native Object clone() throws CloneNotSupportedException
表示此方法為本地方法,由 C/C++ 實現,直接操作記憶體,執行效率高於純 Java 方法。
預設為 protected,子類覆寫時通常改為 public,才能讓外部類別呼叫。若保持 protected,僅限同套件或子類存取。
類別必須實作 java.lang.Cloneable 介面(標記介面,無方法),否則呼叫 clone() 會拋出此異常。這是 Java 設計的保護機制,防止不可複製的物件被誤複製。
Java 參考型別變數儲存的是記憶體地址。單純使用 = 指派只是複製參考,兩個變數指向同一物件,修改其中一個會影響另一個。
Person p1 = new Person(); Person p2 = p1; // 同一記憶體地址
使用 clone() 可建立獨立副本,兩者擁有不同記憶體地址:
Person p1 = new Person(); Person p2 = p1.clone(); // 不同記憶體地址
僅複製物件本身,不複製內部參考型別欄位。呼叫 super.clone() 即為淺拷貝。複製後物件地址不同,但內部參考欄位仍指向同一記憶體。
public class ShallowClone implements Cloneable {
public String name;
public int age;
public Person person;
@Override
public ShallowClone clone() {
try {
return (ShallowClone) super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
}
執行結果:
before clone: c1.person = Person@2dcb25f1 after clone: c2.person = Person@2dcb25f1 // 同一地址
複製物件的同時,遞迴複製所有參考型別欄位。需手動在 clone() 中複製每個參考欄位。
public class DeepClone implements Cloneable {
public String name;
public int age;
public Person person;
@Override
public DeepClone clone() {
try {
DeepClone c = (DeepClone) super.clone();
c.person = person.clone(); // 手動複製參考欄位
return c;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
}
執行結果:
before clone: c1.person = Person@2dcb25f1 after clone: c2.person = Person@70535b58 // 不同地址
clone() 是 Java 早期提供的物件複製機制,採用淺拷貝預設行為。深拷貝需開發者手動實作。現代開發中,建議優先使用建構式複製或序列化框架,僅在效能極度敏感且物件結構簡單時考慮 clone()。