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

Java Clone 淺拷貝與深拷貝完整解析:Cloneable、拷貝建構子、序列化三種方案

     編輯:Java 程式設計入門

Clone 核心概念

  • 淺拷貝:基本類型複製值,引用類型複製引用 (指向同一物件)
  • 深拷貝:引用類型遞迴創建新物件,完全隔離
  • Java 預設 Object.clone() 為淺拷貝
  • 實作 Cloneable 介面 (標記介面) 才能呼叫 clone(),否則拋 CloneNotSupportedException

方案一:覆寫 clone() 實現深拷貝

基本類型與不可變物件 (String、包裝類) → 淺拷貝即可


public class SimpleClone implements Cloneable {
    private int id;
    private String name; // String 不可變,共享安全

    @Override
    protected SimpleClone clone() {
        try {
            return (SimpleClone) super.clone(); // 淺拷貝足夠
        } catch (CloneNotSupportedException e) {
            throw new AssertionError(); // 不可能發生
        }
    }
}

含可變引用類型 (陣列、集合、自訂物件) → 必須深拷貝


import java.util.*;

public class DeepClone implements Cloneable {
    private String[] tags;           // 陣列
    private List comments;   // 集合
    private Address address;         // 自訂物件

    public DeepClone(String[] tags, List comments, Address address) {
        this.tags = tags;
        this.comments = comments;
        this.address = address;
    }

    @Override
    protected DeepClone clone() {
        try {
            DeepClone cloned = (DeepClone) super.clone();
            // 1. 陣列深拷貝
            cloned.tags = this.tags == null ? null : this.tags.clone();
            // 2. 集合深拷貝 (元素為 String 不可變,淺拷貝集合即可)
            cloned.comments = this.comments == null ? null : new ArrayList<>(this.comments);
            // 3. 自訂物件遞迴 clone (需 Address 也實作 Cloneable)
            cloned.address = this.address == null ? null : this.address.clone();
            return cloned;
        } catch (CloneNotSupportedException e) {
            throw new AssertionError();
        }
    }
}

// Address 也需實作 Cloneable
class Address implements Cloneable {
    private String city;
    private String street;

    @Override
    protected Address clone() {
        try {
            return (Address) super.clone(); // 基本類型 + String,淺拷貝足夠
        } catch (CloneNotSupportedException e) {
            throw new AssertionError();
        }
    }
}

方案二:拷貝建構子 (Copy Constructor) - 推薦替代方案

優點:不拋受檢異常、不需實作介面、可指定最終類型、支援 final 欄位、可轉型父類建構子。


public class CopyConstructorDemo {
    private final int id;           // final 欄位 clone() 無法處理
    private final String name;
    private List tags;
    private Address address;

    // 主建構子
    public CopyConstructorDemo(int id, String name, List tags, Address address) {
        this.id = id;
        this.name = name;
        this.tags = tags;
        this.address = address;
    }

    // 拷貝建構子 (深拷貝)
    public CopyConstructorDemo(CopyConstructorDemo other) {
        this.id = other.id;
        this.name = other.name;
        this.tags = other.tags == null ? null : new ArrayList<>(other.tags);
        this.address = other.address == null ? null : new Address(other.address);
    }

    // Address 同樣提供拷貝建構子
    static class Address {
        private String city, street;
        public Address(String city, String street) { this.city = city; this.street = street; }
        public Address(Address other) { this.city = other.city; this.street = other.street; }
    }
}

方案三:序列化反序列化 (通用深拷貝)

適用於複雜物件圖、第三方類別無法修改、需通用工具方法。


import java.io.*;

public class SerializationCloneUtil {

    @SuppressWarnings("unchecked")
    public static  T deepClone(T obj) {
        if (obj == null) return null;
        try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
             ObjectOutputStream oos = new ObjectOutputStream(bos);
             ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
             ObjectInputStream ois = new ObjectInputStream(bis)) {
            oos.writeObject(obj);
            oos.flush();
            return (T) ois.readObject();
        } catch (IOException | ClassNotFoundException e) {
            throw new RuntimeException("深拷貝失敗: " + e.getMessage(), e);
        }
    }
}

// 使用
User cloned = SerializationCloneUtil.deepClone(originalUser);

三種方案完整對比

維度clone()拷貝建構子序列化
語法複雜度中 (需 try-catch、轉型)低 (純 Java 代碼)低 (工具類封裝)
final 欄位支援❌ 無法賦值✅ 自然支援✅ 透過反序列化
介面侵入性需實作 Cloneable無需實作 Serializable
效能最快 (原生)快 (直接 new)慢 (IO 序列化開銷)
循環引用處理手動遞迴易出錯手動遞迴✅ 自動處理
第三方類別❌ 無法修改❌ 無法修改✅ 僅需 Serializable
型別安全需轉型✅ 編譯期檢查需轉型
推薦場景簡單類、效能敏感✅ 現代 Java 首選複雜物件圖、通用工具

現代最佳實踐建議

  1. 優先使用拷貝建構子:清晰、型別安全、支援 final、無受檢異常
  2. 不可變物件:record (Java 14+) 或 final 欄位 + 無 setter,根本不需拷貝
  3. 集合拷貝:new ArrayList<>(list)、new HashSet<>(set)、Map.copyOf(map) (Java 10+)
  4. 陣列拷貝:arr.clone()、Arrays.copyOf(arr, len)、System.arraycopy()
  5. 第三方庫:SerializationUtils.clone() (Apache Commons Lang)、BeanUtils.copyProperties() (Spring、淺拷貝)

常見錯誤避坑

  • 忘記實作 Cloneable:直接呼叫 super.clone() 拋異常
  • 淺拷貝當深拷貝用:修改 clone 物件的集合/陣列影響原物件
  • 遞迴 clone 循環引用:A.clone() → B.clone() → A.clone() → StackOverflowError
  • final 欄位無法在 clone() 中賦值:編譯錯誤,需用拷貝建構子
  • 序列化深拷貝未實作 Serializable:拋 NotSerializableException
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved