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

Java 深拷貝 vs 淺拷貝徹底解析:clone()、序列化、第三方庫全方案對比

     編輯:Java 相關知識

對象拷貝是 Java 開發高頻操作,但 `clone()` 默認僅**淺拷貝**,導致引用類型字段共享底層對象,修改副本影響原對象。本文徹底解析淺/深拷貝區別、五種深拷貝實現方案、性能對比、適用場景選型指南。

一、 核心概念區別

維度淺拷貝深拷貝
基本類型/不可變對象值複製值複製
引用類型字段**複製引用** (指向同一對象)**遞歸複製對象圖** (新建獨立對象)
修改副本影響原對象是 (引用字段)否 (完全隔離)
典型實現`Object.clone()`、構造器複製引用序列化、手動遞歸、第三方庫

二、 淺拷貝陷阱演示

class Address implements Serializable {
    String city;
    // getter/setter/toString
}

class User implements Cloneable, Serializable {
    String name;
    Address address; // 引用類型

    @Override
    protected User clone() throws CloneNotSupportedException {
        return (User) super.clone(); // 默認淺拷貝
    }
}

public class ShallowCopyTrap {
    public static void main(String[] args) throws Exception {
        User u1 = new User();
        u1.name = "Alice";
        u1.address = new Address();
        u1.address.city = "Beijing";

        User u2 = u1.clone(); // 淺拷貝
        u2.name = "Bob";
        u2.address.city = "Shanghai"; // 修改副本地址

        System.out.println(u1.address.city); // Shanghai !!! 原對象被污染
    }
}

三、 五種深拷貝方案全解析

方案 1:序列化反序列化 (通用、簡單、性能一般)

public static <T extends Serializable> T deepClone(T obj) {
    try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
         ObjectOutputStream oos = new ObjectOutputStream(bos)) {
        oos.writeObject(obj);
        try (ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
             ObjectInputStream ois = new ObjectInputStream(bis)) {
            return (T) ois.readObject();
        }
    } catch (IOException | ClassNotFoundException e) {
        throw new RuntimeException("深拷貝失敗", e);
    }
}
  • ✅ 任意可序列化對象圖、處理循環引用
  • ❌ 要求實現 `Serializable`、較慢 (IO+反射)、不處理 `transient` 字段

方案 2:手動實現 `clone()` 遞歸深拷貝 (高性能、侵入性強)

class DeepUser implements Cloneable {
    String name;
    Address address;

    @Override
    protected DeepUser clone() {
        try {
            DeepUser copy = (DeepUser) super.clone();
            copy.address = this.address.clone(); // 手動深拷貝引用字段
            return copy;
        } catch (CloneNotSupportedException e) {
            throw new AssertionError(e);
        }
    }
}
// Address 也需實現 Cloneable 並重寫 clone()
  • ✅ 極快、無反射、完全可控
  • ❌ 代碼量大、對象圖複雜時維護困難、需處理循環引用

方案 3:JSON 序列化 (Jackson/Gson) (現代、易讀、中等性能)

// Jackson
ObjectMapper mapper = new ObjectMapper();
User copy = mapper.readValue(mapper.writeValueAsString(user), User.class);

// Gson
Gson gson = new Gson();
User copy = gson.fromJson(gson.toJson(user), User.class);
  • ✅ 不需實現接口、支持不可變類、人類可讀中間格式
  • ❌ 需無參構造器、不支持循環引用 (需配置)、性能次於二進制序列化

方案 4:第三方庫 - Apache Commons Lang `SerializationUtils`

User copy = SerializationUtils.clone(user); // 內部用序列化
// 或
User copy = SerializationUtils.roundtrip(user); // 同義
  • ✅ 一行代碼、處理異常、空值安全
  • ❌ 同方案 1 性能特征

方案 5:MapStruct / ModelMapper (對象映射場景)

適用於 **DTO ↔ Entity** 互轉,非通用深拷貝,但生產環境最常用:

@Mapper
interface UserMapper {
    UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
    UserDto toDto(User user); // 自動生成深拷貝代碼
}

四、 性能基準測試參考 (對象圖深度 3、節點 50)

方案耗時 (μs/op)相對性能備註
手動 clone()0.151x (基準)最快,零開銷
Kryo (序列化庫)0.453x高性能二進制序列化
JSON (Jackson)1.28x易讀,適合配置/網絡
Java 序列化3.523x通用但慢

五、 選型決策樹

需要深拷貝?
├─ 是否高頻/熱路徑/極致性能敏感?
│   ├─ 是 → 手動 clone() / Kryo / Protobuf
│   └─ 否 → 對象是否可序列化?
│       ├─ 是 → 簡單場景:SerializationUtils.clone()
│       │           複雜對象圖/循環引用:Java 序列化 / Kryo
│       └─ 否 (含第三方不可變類/無無參構造器)
│           ├─ DTO/Entity 轉換 → MapStruct (編譯期生成)
│           ├─ 通用拷貝 → JSON (Jackson/Gson) / BeanUtils.copyProperties
│           └─ 測試/臨時 → JSON

六、 總結

  1. 淺拷貝是默認陷阱:`clone()`、構造器複製引用、BeanUtils.copyProperties 均為淺拷貝
  2. 深拷貝無銀彈:根據**性能要求、對象圖複雜度、可序列化性、依賴庫限制**選型
  3. 生產首選**:
    • 高性能:手動 `clone()` / Kryo
    • 通用簡單:`SerializationUtils.clone()`
    • DTO 映射:MapStruct (編譯期生成,類型安全、極快)
    • 非序列化/第三方類:Jackson JSON
  4. 測試驗證**:編寫「修改副本引用字段,斷言原對象不變」的單元測試,納入 CI
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved