Object.clone() 為淺拷貝Cloneable 介面 (標記介面) 才能呼叫 clone(),否則拋 CloneNotSupportedException
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();
}
}
}
優點:不拋受檢異常、不需實作介面、可指定最終類型、支援 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 首選 | 複雜物件圖、通用工具 |
record (Java 14+) 或 final 欄位 + 無 setter,根本不需拷貝new ArrayList<>(list)、new HashSet<>(set)、Map.copyOf(map) (Java 10+)arr.clone()、Arrays.copyOf(arr, len)、System.arraycopy()SerializationUtils.clone() (Apache Commons Lang)、BeanUtils.copyProperties() (Spring、淺拷貝)super.clone() 拋異常NotSerializableException