在 Java 開發中,當我們需要一個現有對象的獨立副本,且不希望修改副本影響原對象時,克隆是常見需求。典型場景包括:
Cloneable 是一個標記接口,不包含任何方法。實現它僅表示「允許調用 Object.clone()」,否則會拋出 CloneNotSupportedException。
protected Object clone() throws CloneNotSupportedException
約定俗成的規範(非強制):
x.clone() != x(新對象)x.clone().getClass() == x.getClass()(同類型)x.clone().equals(x) 通常為 true,但非必須@Override
public Object clone() throws CloneNotSupportedException {
return super.clone(); // 逐字段複製:基本類型複製值,引用類型複製引用
}
特點:速度快,但引用類型字段指向同一對象,修改會互相影響。
需手動克隆引用類型字段:
static class CloneableImp implements Cloneable {
public int count;
public Child child; // 引用類型
@Override
public Object clone() throws CloneNotSupportedException {
CloneableImp obj = (CloneableImp) super.clone();
obj.child = (Child) child.clone(); // 遞歸克隆
return obj;
}
}
前提:被克隆的引用類型也必須實現 Cloneable 並覆寫 clone()。
public class Car {
private Wheel wheel;
private String manufacturer;
public Car(Car other) { // 複製構造函數
this.wheel = new Wheel(other.wheel.brand); // 深拷貝
this.manufacturer = other.manufacturer;
}
// 靜態工廠方法更優雅
public static Car newInstance(Car car) { return new Car(car); }
}
優點:簡單、類型安全、無拋出異常、支援 final 欄位。
public class DeepCopyExample implements Serializable {
private Child child;
public DeepCopyExample copy() {
try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos)) {
oos.writeObject(this);
try (ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais)) {
return (DeepCopyExample) ois.readObject();
}
} catch (IOException | ClassNotFoundException e) {
throw new RuntimeException("深拷貝失敗", e);
}
}
}
要求:所有涉及類實現 Serializable。優點:自動處理複雜對象圖;缺點:性能較差,不適合高頻調用。
| 場景 | 推薦方案 |
|---|---|
| 僅包含基本類型/不可變對象 | 淺拷貝(clone 或複製構造函數) |
| 包含可變引用類型,結構簡單 | 複製構造函數(顯式深拷貝) |
| 複雜對象圖、需通用深拷貝 | 序列化 / 第三方庫(如 Apache Commons Lang SerializationUtils) |
| 高性能、高併發場景 | 避免克隆,採用不可變對象設計 |
現代 Java 開發中,優先選擇複製構造函數或靜態工廠方法,代碼更清晰、類型安全,且避開了 clone 所有已知坑點。