新對象的所有字段值與原對象相同,但引用類型字段仍指向原對象引用的同一個堆內存地址。即「只複製對象本身,不複製引用對象」。
新對象的所有字段值與原對象相同,引用類型字段指向「被遞歸複製的新對象」,而非原引用對象。即「對象圖整體複製」。
x.clone() != x:新舊對象非同一實例x.clone().getClass() == x.getClass():類型一致x.clone().equals(x):若 equals 正確實現,應為 true關鍵規則:覆寫 clone() 必須調用 super.clone(),由 Object 分配內存並逐字段複製;實現 Cloneable 標記接口,否則拋出 CloneNotSupportedException。
class Student implements Cloneable {
String name; int age;
// super.clone() 即為深拷貝(String 不可變)
}
class Professor implements Cloneable { ... }
class Student implements Cloneable {
Professor p;
public Object clone() {
Student o = (Student) super.clone(); // 淺拷貝
o.p = (Professor) p.clone(); // 手動深拷貝引用對象
return o;
}
}
若省略 o.p = p.clone(),修改 s2.p.name 會影響 s1.p,驗證了默認 clone() 為淺拷貝。
原理:將對象寫入字節流(序列化/「腌鹹菜」),再讀出(反序列化/「回鮮」),獲得完整對象圖副本。
public Object deepClone() throws IOException, ClassNotFoundException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return ois.readObject();
}
前提:對象圖中所有類必須實現 Serializable。若含不可序列化字段,需標記 transient 並自行處理。
| 方案 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
| 手動 clone() | 性能高、可控、無第三方依賴 | 代碼繁瑣、維護成本高、易漏字段 | 核心領域模型、高頻調用、對象圖較簡單 |
| 序列化 | 通用、自動處理複雜對象圖、代碼極少 | 性能差(IO/反射開銷)、要求 Serializable、無法處理 transient 邏輯 | 臨時需求、對象圖複雜、非熱點路徑、跨 JVM 複製 |
| 第三方庫 | API 簡潔、支持多種策略、性能優化 | 引入依賴、學習成本 | 通用工具類、項目已引入相關庫 |
clone()** 並配合單元測試驗證深度。serialVersionUID 顯式聲明與 transient 字段邏輯補全。