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

Java 對象拷貝深度解析:clone()、反射、序列化、第三方庫全方位對比

     編輯:Java 相關知識

對象拷貝是業務開發中高頻操作(DTO 轉 VO、快照保存、緩存隔離等)。本文系統對比 clone()、反射拷貝、序列化深拷貝、MapStruct、BeanUtils、Hutool 等主流方案的原理、效能、深/淺拷貝支援度與適用場景,助你選型最優方案。

一、核心概念釐清

術語定義典型場景
淺拷貝僅拷貝引用型別字段的引用值,引用同一物件不可變物件、字符串、基本型別包裝類
深拷貝遞歸拷貝所有引用型別字段指向的物件,完全隔離可變集合、嵌套物件、業務快照

二、原生 clone() 機制與局限

2.1 實作規範

  1. 實作 Cloneable 標記介面(否則拋 CloneNotSupportedException)。
  2. 重寫 clone() 為 public,內部調用 super.clone()。
  3. super.clone() 為 native 方法,執行淺拷貝(逐位複製)。
  4. 引用型別字段需手動深拷貝:this.list = (List) list.clone();。

2.2 缺陷

  • 破壞封裝性(需拋棄建構子、可變性控制)。
  • 繼承鏈中所有父類必須協同實作,維護成本高。
  • 無法處理 final 字段(拷貝後無法重新賦值)。
  • 集合/Map 深拷貝需手寫遞歸代碼,極易出錯。

三、反射拷貝工具對比

3.1 Apache Commons BeanUtils

BeanUtils.copyProperties(dest, src); // 僅淺拷貝,忽略 null/類型不匹配
  • 基於 PropertyDescriptor 反射,效能較差(每次解析類元資料)。
  • 不支援深拷貝、泛型集合類型擦除導致類型轉換失敗。

3.2 Spring BeanUtils

BeanUtils.copyProperties(src, dest, "id", "createTime"); // 支援忽略字段
  • 緩存 PropertyDescriptor,效能優於 Commons 版。
  • 同樣僅淺拷貝,不處理集合深拷貝。

3.3 Hutool BeanUtil

BeanUtil.copyProperties(src, dest); // 支援 Map↔Bean、字段別名、日期格式化
BeanUtil.toBean(src, Dest.class);   // 新建目標對象
  • 功能最豐富,支援 CopyOptions 控制編輯策略。
  • 仍屬淺拷貝,深拷貝需自行遞歸。

3.4 自研反射深拷貝(原文範例改進版)

public static <T> T deepCopy(T source, Class<T> clazz) {
    if (source == null) return null;
    try {
        T target = clazz.getDeclaredConstructor().newInstance();
        for (Field f : clazz.getDeclaredFields()) {
            f.setAccessible(true);
            Object val = f.get(source);
            if (val == null) continue;
            // 基本型別、String、不可變類直接賦值
            if (isImmutable(f.getType())) {
                f.set(target, val);
            } else if (val instanceof Collection) {
                Collection<?> srcCol = (Collection<?>) val;
                Collection<Object> newCol = srcCol.getClass().getDeclaredConstructor().newInstance();
                for (Object item : srcCol) newCol.add(deepCopy(item, item.getClass()));
                f.set(target, newCol);
            } else if (val instanceof Map) {
                // Map 深拷貝邏輯類似
            } else {
                // 遞歸拷貝嵌套物件
                f.set(target, deepCopy(val, val.getClass()));
            }
        }
        return target;
    } catch (Exception e) {
        throw new RuntimeException("深拷貝失敗", e);
    }
}

缺點:效能低、循環引用棧溢出、泛型擦除導致集合元素類型丟失、單例/代理物件拷貝語義錯誤。

四、序列化深拷貝

public static <T> T serializationCopy(T obj) {
    try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
         ObjectOutputStream oos = new ObjectOutputStream(bos)) {
        oos.writeObject(obj);
        try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()))) {
            return (T) ois.readObject();
        }
    } catch (IOException | ClassNotFoundException e) {
        throw new RuntimeException("序列化拷貝失敗", e);
    }
}
  • 要求所有物件實作 Serializable,transient 字段會丟失。
  • 效能最差(字節流編解碼),但語義最純粹的深拷貝。
  • 適用於跨進程傳輸、持久化快照,不建議高頻業務調用。

五、編譯期代碼生成:MapStruct(首選)

@Mapper(componentModel = "spring")
public interface UserMapper {
    UserDTO toDTO(UserEntity entity);
    @Mapping(target = "addressList", source = "addresses")
    UserVO toVO(UserDTO dto);
    
    // 集合映射
    List toDTOList(List entities);
}
  • 編譯期生成 set/get 代碼,效能等同手寫,無反射開銷。
  • 型別安全,IDE 重構友好,支援複雜映射(嵌套、集合、枚舉、自定義轉換器)。
  • 僅淺拷貝,深拷貝需自定義 @Mapping + 轉換器手動 new 集合元素。

六、效能基準測試參考(JMH,100萬次調用)

方案耗時 (ms)相對基準深拷貝推薦指數
手寫 set/get121.0x可控⭐⭐⭐⭐⭐ (簡單物件)
MapStruct141.2x需配置⭐⭐⭐⭐⭐ (生產首選)
Spring BeanUtils18015x否⭐⭐⭐ (DTO互轉)
Hutool BeanUtil21017x否⭐⭐⭐ (功能豐富)
反射遞歸深拷貝3,500290x是⭐ (測試/臨時)
序列化12,0001000x是⭐ (持久化/跨進程)

七、選型決策樹

  1. 是否需要深拷貝?
    • 否 → DTO/VO 互轉首選 MapStruct,簡單臨時用 Spring BeanUtils。
    • 是 → 物件圖簡單、無循環引用 → 序列化 或 Jackson 深拷貝:objectMapper.convertValue(obj, clazz)。
  2. 高頻熱點路徑 → 手寫建構子/工廠方法 或 MapStruct + 自定義深拷貝轉換器。
  3. 不可變物件 → 直接共用引用,無需拷貝。

八、總結

  • clone() 設計缺陷多,現代開發不推薦使用。
  • 反射工具僅適合淺拷貝場景,MapStruct 為生產級首選(編譯期生成、型別安全、效能極致)。
  • 深拷貝慎用序列化/反射遞歸,優先考慮不可變設計或領域模型重構避免深拷貝需求。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved