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

Java 物件深拷貝最佳實踐:序列化實現通用深克隆

     編輯:Java 相關知識

Java 物件拷貝分為淺拷貝與深拷貝。實作 Cloneable 介面並覆寫 clone() 雖能實現淺拷貝,但參考型別欄位僅複製參考位址,導致多個物件共用同一個子物件,修改時會互相影響。本文透過實例說明淺拷貝缺陷,並展示序列化實現通用深拷貝的優雅寫法。

淺拷貝問題示範

public class Person implements Cloneable {
    private String name;
    private Email email;  // 參考型別

    // getter/setter/建構子略

    @Override
    protected Person clone() {
        try {
            return (Person) super.clone();  // 預設淺拷貝
        } catch (CloneNotSupportedException e) {
            throw new AssertionError();
        }
    }
}

public class Email {
    private String subject, content;
    // 建構子、getter/setter 略
}

測試程式:

Email email = new Email("會議通知", "12:30 開會");
Person p1 = new Person("張三", email);
Person p2 = p1.clone();
p2.setName("李四");

p1.getEmail().setContent("12:00 開會");  // 修改張三的郵件內容
System.out.println(p2.getEmail().getContent());  // 輸出「12:00 開會」——李四的郵件也被改了!

為什麼會這樣?

  • 基本型別:複製值。
  • String:複製參考,但因不可變特性,修改時會建立新字串,看似安全。
  • 參考型別:複製參考位址,雙方指向同一個 Email 實例。

手動深拷貝缺點

@Override
protected Person clone() {
    Person p = (Person) super.clone();
    p.setEmail(new Email(this.email.getSubject(), this.email.getContent())); // 手動 new
    return p;
}

缺點:每個類別都要寫 clone()、屬性一多極其繁瑣、維護困難、違反 DRY 原則。

序列化實現通用深拷貝(推薦)

import java.io.*;

public class DeepCloneUtil {
    @SuppressWarnings("unchecked")
    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);
        }
    }
}

使用範例

Person p1 = new Person("張三", new Email("會議", "12:30"));
Person p2 = DeepCloneUtil.deepClone(p1);
p2.setName("李四");
p1.getEmail().setContent("12:00");  // 只影響 p1
System.out.println(p2.getEmail().getContent());  // 仍為 12:30

注意事項

  1. 所有參與深拷貝的類別必須實作 Serializable 介面。
  2. transient 修飾的欄位不會被序列化,深拷貝後為預設值(null/0)。
  3. 效能較手動拷貝稍低,但開發效率極大提升,適合非極高頻呼叫場景。

總結

淺拷貝僅適用於純基本型別或不可變物件組成的類別。若物件圖包含可變參考型別,請採用序列化深拷貝或第三方庫(如 Apache Commons Lang SerializationUtils.clone()),一次解決所有層級拷貝問題。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved