核心差異一覽
| 複製類型 | 機制 | 參考型別欄位行為 | 適用場景 |
淺層複製clone() | Object.clone()逐位複製 | 複製參考值,新舊物件共享同一參考物件 | 全為基本型別/不可變物件(String等) |
| 深層複製 | 遞迴複製或序列化 | 遞迴建立新物件,新舊物件完全獨立 | 含可變參考型別欄位(集合、自訂物件等) |
實作範例:Square類別含Point參考
public class Square implements Cloneable, Serializable {
private Point location = new Point(0, 0); // 可變參考型別
private float sideLength = 1F;
// 淺層複製:預設clone()行為
@Override
public Object clone() {
try { return super.clone(); }
catch (CloneNotSupportedException e) { throw new InternalError(e); }
}
// 深層複製:序列化往返
public Object deepClone() throws IOException, ClassNotFoundException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this); // 寫入位元組流
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return ois.readObject(); // 讀出新物件圖
}
// getter/setter...
}
驗證結果解讀
Square s = new Square();
s.setSideLength(2);
s.setLocation(new Point(2, 5));
Square shallow = (Square) s.clone();
Square deep = (Square) s.deepClone();
// 修改原物件參考欄位
s.getLocation().x = 99;
// 淺層複製受影響
System.out.println(shallow.getLocation().x); // 99 ⚠️ 共享同一Point
// 深層複製獨立
System.out.println(deep.getLocation().x); // 2 ✅ 獨立副本
深層複製三大實作策略比較
- 序列化法(本例):通用、簡潔,要求所有物件
implements Serializable,效能較低(I/O開銷),不支援transient欄位邏輯。
- 手動遞迴複製:在
clone()中new Point(this.location.x, this.location.y),效能最佳、可控性最高,但維護成本隨物件圖複雜度上升。
- 第三方庫:Apache Commons Lang
SerializationUtils.clone()、Dozer、MapStruct等,生產環境建議採用成熟庫。
最佳實務建議
- 優先使用不可變物件(如
String、LocalDate、GuavaImmutableList),從根源消除深複製需求
- 若必須可變,建構子/工廠方法回傳防禦性副本
- 實作
Cloneable時務必覆寫clone()並呼叫super.clone(),處理參考欄位深複製
- 序列化法適用於物件圖結構固定、效能非瓶頸、跨JVM傳輸場景