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

Java 深拷貝完整解析:四種實現方式與效能比較

     編輯:Java 相關知識

深拷貝核心概念

深拷貝指複製對象時,不僅複製基本類型與 String 等不可變對象,還遞歸複製所有引用類型成員,使新舊對象在記憶體中完全獨立,互不影響。對比淺拷貝僅複製引用地址。

四種主流實現方案

1. 實作 Cloneable 介面(原生克隆)

原理:實作 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、易遺漏欄位、破壞封裝性。

2. 序列化與反序列化

原理:將對象序列化為位元組流,再反序列化重建新對象。

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 欄位會丟失、不支援單例模式。

3. 第三方庫 cloning(號稱最快)

核心邏輯: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); // 某類直接複製引用

優點:高度可配置、支援不可變類優化、處理循環引用、非侵入式。
缺點:引入外部依賴、反射仍有開銷、學習成本。

4. 手動複製建構子/工廠方法(最原始也最可控)

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 ns1x (基準)
手動複製建構子~80 ns1.6x
cloning 庫~300 ns6x
序列化/反序列化~5,000 ns100x

選型決策指南

場景推薦方案理由
高頻熱點路徑、效能敏感Cloneable / 手動建構子納秒級開銷,無反射/IO
通用工具類、不可修改第三方類序列化 / cloning 庫無侵入、零代碼修改
複雜對象圖、需精細控制cloning 庫 / 手動建構子支援循環引用、不可變標記、欄位級策略
持久化/網路傳輸順便深拷貝序列化 (JSON/Protobuf)一石二鳥,跨語言
簡單 DTO/VO、欄位固定手動建構子 / Lombok @Builder可讀性最佳,編譯期安全

常見陷阱與注意事項

  • String 非深拷貝:賦值只是複製引用,但 String 不可變故語義上等同深拷貝
  • 集合淺拷貝:new ArrayList<>(oldList) 僅複製引用,元素仍共享
  • 循環引用:手動/Cloneable 易導致 StackOverflow,cloning 庫內建處理
  • 單例模式:序列化會破壞單例,需實作 readResolve()
  • final 欄位:Cloneable/反射需特殊處理,手動建構子天然支援
  • 繼承體系:super.clone() 鏈式調用必須鏈路完整實作 Cloneable

現代替代方案

  • 不可變資料結構:Immutables、Vavr、Record (Java 14+) —— 從根源避免拷貝需求
  • 序列化庫:Jackson、Gson、Protobuf、Kryo —— 高效跨進程/機器複製
  • MapStruct:編譯期生成映射代碼,DTO 轉換首選
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved