深拷貝指複製對象時,不僅複製基本類型與 String 等不可變對象,還遞歸複製所有引用類型成員,使新舊對象在記憶體中完全獨立,互不影響。對比淺拷貝僅複製引用地址。
原理:實作 Cloneable,覆寫 clone() 調用 super.clone(),對引用類型成員手動深拷貝。
public class SimpleDO implements Cloneable, Serializable {
private int x = 1;
private String s = "simpleDO";
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone(); // 基本類型與 String 直接可用
}
}
public class ComplexDO implements Cloneable, Serializable {
private int x = 1;
private String s = "complex";
private List<SimpleDO> list = new ArrayList<>();
@Override
public Object clone() throws CloneNotSupportedException {
ComplexDO cloned = (ComplexDO) super.clone();
// 手動深拷貝集合與元素
cloned.list = new ArrayList<>();
for (SimpleDO item : this.list) {
cloned.list.add((SimpleDO) item.clone());
}
return cloned;
}
}
優點:JVM 原生支援,效能最佳(直接記憶體複製)。
缺點:需所有層級類別實作 Cloneable、處理 CloneNotSupportedException、易遺漏欄位、破壞封裝性。
原理:將對象序列化為位元組流,再反序列化重建新對象。
public Object serialClone(Object src) {
try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis)) {
oos.writeObject(src);
return ois.readObject();
} catch (Exception e) {
throw new RuntimeException("深拷貝失敗", e);
}
}
優點:通用性強,單一方法處理任意可序列化對象圖,無需修改類別。
缺點:效能最差(IO 開銷、反射)、要求所有類別實作 Serializable、transient 欄位會丟失、不支援單例模式。
核心邏輯:Cloner 類雙遞歸鏈路:
deepClone → cloneInternal → fastClone → cloneInternal:處理實作 IFastCloner 的集合類deepClone → cloneInternal → cloneObject → cloneInternal:反射獲取欄位,用 Objenesis 實例化,遞歸複製// 使用範例
Cloner cloner = new Cloner();
ComplexDO cloned = cloner.deepClone(complexDO);
// 可自訂複製策略
cloner.registerImmutable(String.class); // 標記不可變類
cloner.nullInsteadOfClone(SomeClass.class); // 某類直接複製引用
優點:高度可配置、支援不可變類優化、處理循環引用、非侵入式。
缺點:引入外部依賴、反射仍有開銷、學習成本。
public class ComplexDO {
// ... 欄位
public ComplexDO(ComplexDO other) {
this.x = other.x;
this.s = other.s;
this.list = other.list.stream()
.map(SimpleDO::new) // 假設 SimpleDO 有複製建構子
.collect(Collectors.toList());
}
}
優點:完全可控、編譯期檢查、無反射開銷、IDE 重構友善。
缺點:樣板代碼多、新增欄位需同步修改、繼承體系複雜時維護困難。
測試環境:JDK 17、單次複製複雜對象圖(含集合、巢狀對象)
// 測試代碼骨架
int copyCount = 100_000;
ComplexDO source = createComplexObject();
// 1. Cloneable
long t1 = System.nanoTime();
for (int i = 0; i < copyCount; i++) source.clone();
long t2 = System.nanoTime();
// 2. 序列化
for (int i = 0; i < copyCount; i++) serialClone(source);
long t3 = System.nanoTime();
// 3. cloning 庫
Cloner cloner = new Cloner();
for (int i = 0; i < copyCount; i++) cloner.deepClone(source);
long t4 = System.nanoTime();
// 4. 手動複製建構子
for (int i = 0; i < copyCount; i++) new ComplexDO(source);
long t5 = System.nanoTime();
| 方法 | 耗時 | 相對倍數 |
|---|---|---|
| Cloneable (原生) | ~50 ns | 1x (基準) |
| 手動複製建構子 | ~80 ns | 1.6x |
| cloning 庫 | ~300 ns | 6x |
| 序列化/反序列化 | ~5,000 ns | 100x |
| 場景 | 推薦方案 | 理由 |
|---|---|---|
| 高頻熱點路徑、效能敏感 | Cloneable / 手動建構子 | 納秒級開銷,無反射/IO |
| 通用工具類、不可修改第三方類 | 序列化 / cloning 庫 | 無侵入、零代碼修改 |
| 複雜對象圖、需精細控制 | cloning 庫 / 手動建構子 | 支援循環引用、不可變標記、欄位級策略 |
| 持久化/網路傳輸順便深拷貝 | 序列化 (JSON/Protobuf) | 一石二鳥,跨語言 |
| 簡單 DTO/VO、欄位固定 | 手動建構子 / Lombok @Builder | 可讀性最佳,編譯期安全 |
new ArrayList<>(oldList) 僅複製引用,元素仍共享readResolve()super.clone() 鏈式調用必須鏈路完整實作 Cloneable