Java雖宣稱「無指針」,實則通過引用間接操作對象。Object o = new Object();做三件事:聲明引用變量、堆上分配對象、引用指向對象。引用像遙控器,對象像飛機。理解此點,克隆機制自然清晰:克隆是創建新對象並復制原對象狀態。
Cloneable是標記接口(無方法),聲明「我支持克隆」Object.clone()為protected native方法,按位復制字段(淺克隆)clone()需:CloneableCloneNotSupportedExceptionsuper.clone()獲取淺克隆副本public class Sheep implements Cloneable {
private String name; // String不可變,淺克隆安全
// getter/setter省略
@Override
public Sheep clone() throws CloneNotSupportedException {
return (Sheep) super.clone();
}
}
運行結果:兩隻羊name均為「我是真的」。但若字段為可變對象(數組、集合、自定義對象),淺克隆僅復制引用,雙方共享同一底層對象,修改一方影響另一方 → 數據污染。
public class DeepCloneExample implements Cloneable {
private int[] scores; // 可變數組
private Address address; // 可變對象
private List tags; // 可變集合
@Override
public DeepCloneExample clone() {
try {
DeepCloneExample cloned = (DeepCloneExample) super.clone();
// 基本類型/不可變對象無需處理
// 可變字段深度復制
cloned.scores = scores.clone(); // 數組clone
cloned.address = address.clone(); // 假設Address實現Cloneable
cloned.tags = new ArrayList<>(tags); // 集合構造器復制
return cloned;
} catch (CloneNotSupportedException e) {
throw new AssertionError(); // 不可能發生
}
}
}
public 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,transient字段不復制。適用於複雜對象圖、第三方類無法修改時。
生產環境推薦用成熟序列化庫替代原生Java序列化,性能提升10-100倍,體積更小。
public User(User other) { this.name = other.name; ... } — 類型安全、無拋檢查異常、可選擇性復制、支持不可變對象User copy = user.toBuilder().build() — 鏈式調用、可修改部分字段record、Lombok @Value、Immutable Collections — 根除克隆需求,狀態不可變天然線程安全| 場景 | 推薦方案 |
|---|---|
| 簡單不可變對象 | 無需克隆,直接共享引用 |
| 可變對象、字段較少、性能敏感 | 手動深克隆 / 複製構造器 |
| 複雜對象圖、含循環引用、第三方類 | 序列化(Kryo/FST/Jackson) |
| 新項目/核心領域模型 | 優先設計為不可變,用record/Builder |
| 集合類 | new ArrayList<>(old)、new HashSet<>(old)等構造器 |
Java克隆機制雖歷史悠久,但Cloneable+clone()設計缺陷明顯(無接口強制、拋檢查異常、淺克隆陷阱、與final字段衝突)。現代開發應優先選擇複製構造器、Builder、序列化庫、不可變數據類等更安全、清晰、高效的替代方案。理解克隆本質,是為了在維護舊代碼、排查淺克隆Bug時游刃有餘,而非在新代碼中推廣使用。