Java clone() 複製
clone() 方法用於建立物件的副本。物件必須實作 Cloneable 介面才能被複製。
基本用法
public class Person implements Cloneable {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
protected Person clone() throws CloneNotSupportedException {
return (Person) super.clone();
}
}
// 使用
Person original = new Person("Alice", 25);
Person copy = original.clone();
copy.setName("Bob");
System.out.println(original.getName()); // Alice(不受影響)
System.out.println(copy.getName()); // Bob
淺複製 vs 深複製
淺複製(Shallow Copy)
public class Person implements Cloneable {
private String name;
private Address address; // 參考類型
@Override
protected Person clone() throws CloneNotSupportedException {
return (Person) super.clone(); // 淺複製
}
}
Person original = new Person("Alice", new Address("台北"));
Person copy = original.clone();
copy.getAddress().setCity("高雄");
System.out.println(original.getAddress().getCity()); // 高雄!(被影響了)
淺複製只複製基本類型和參考,參考類型仍指向同一物件。
深複製(Deep Copy)
public class Address implements Cloneable {
private String city;
@Override
protected Address clone() throws CloneNotSupportedException {
return (Address) super.clone();
}
}
public class Person implements Cloneable {
private String name;
private Address address;
@Override
protected Person clone() throws CloneNotSupportedException {
Person cloned = (Person) super.clone();
cloned.address = this.address.clone(); // 深複製參考物件
return cloned;
}
}
Person copy = original.clone();
copy.getAddress().setCity("高雄");
System.out.println(original.getAddress().getCity()); // 台北(不受影響)
使用複製建構子
public class Person {
private String name;
private Address address;
// 複製建構子
public Person(Person other) {
this.name = other.name;
this.address = new Address(other.address);
}
}
public class Address {
private String city;
public Address(Address other) {
this.city = other.city;
}
}
// 使用
Person original = new Person("Alice", new Address("台北"));
Person copy = new Person(original); // 深複製
使用序列化深複製
public static <T extends Serializable> T deepCopy(T obj) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(obj);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
@SuppressWarnings("unchecked")
T copy = (T) ois.readObject();
return copy;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
// 使用(所有相關類別需實作 Serializable)
Person copy = deepCopy(original);
使用 Record
Record 是不可變的,通常不需要複製,但可以建立新實例:
public record Person(String name, Address address) {
public Person withName(String newName) {
return new Person(newName, this.address);
}
public Person withAddress(Address newAddress) {
return new Person(this.name, newAddress);
}
}
陣列複製
// 一維陣列
int[] original = {1, 2, 3, 4, 5};
int[] copy1 = original.clone();
int[] copy2 = Arrays.copyOf(original, original.length);
int[] copy3 = new int[original.length];
System.arraycopy(original, 0, copy3, 0, original.length);
// 二維陣列(淺複製)
int[][] arr2d = {{1, 2}, {3, 4}};
int[][] shallowCopy = arr2d.clone();
// 二維陣列(深複製)
int[][] deepCopy = new int[arr2d.length][];
for (int i = 0; i < arr2d.length; i++) {
deepCopy[i] = arr2d[i].clone();
}
集合複製
// List 淺複製
List<String> original = new ArrayList<>(Arrays.asList("A", "B", "C"));
List<String> copy1 = new ArrayList<>(original);
List<String> copy2 = List.copyOf(original); // 不可變
// 深複製(需要手動複製元素)
List<Person> people = new ArrayList<>();
List<Person> deepCopy = people.stream()
.map(p -> new Person(p)) // 使用複製建構子
.collect(Collectors.toList());
clone() 的問題
- 需要實作 Cloneable:否則拋出
CloneNotSupportedException - 淺複製問題:預設只做淺複製
- 建構子不被呼叫:
clone()不呼叫建構子 - final 欄位問題:無法在 clone 中設定 final 欄位
替代方案比較
| 方法 | 優點 | 缺點 |
|---|---|---|
| clone() | 內建機制 | 複雜、容易出錯 |
| 複製建構子 | 清晰明確 | 需手動實作 |
| 序列化 | 自動深複製 | 效能較差 |
| Builder | 靈活 | 較多程式碼 |
重點整理
- 實作
Cloneable介面才能使用clone() - 預設是淺複製,參考類型需要手動深複製
- 建議使用複製建構子取代 clone()
- 序列化可以簡單實現深複製(但效能較差)
- 不可變物件不需要複製
- 陣列可用
clone()或Arrays.copyOf()