目錄
一、概念
二、實現方式
2.1 淺拷貝(不推薦)
2.2 深拷貝
2.2.1 方法一:重寫?clone()?方法并遞歸克隆(常用)
2.2.2 方法二:通過序列化實現(更強大,但更重)
2.2.3?使用拷貝構造方法或拷貝工廠(推薦)
一、概念
淺拷貝 (Shallow Copy):只復制對象本身以及對象中的基本數據類型字段的值,而對于對象中的引用類型字段,則只復制其內存地址(即引用),而不復制引用的對象本身。
影響:原始對象和拷貝對象中的引用字段指向的是同一個堆內存中的子對象。修改其中一個對象的引用字段的內容,另一個對象的對應字段也會“看到”這個變化。
深拷貝 (Deep Copy):僅復制對象本身和基本數據類型字段的值,還會遞歸地復制所有引用類型字段所指向的對象,直到所有可達的對象都被復制。
影響:原始對象和拷貝對象完全獨立,它們所有的引用字段都指向不同的對象。修改其中一個對象的任何內容,都不會影響另一個對象。
二、實現方式
2.1 淺拷貝(不推薦)
實現方式:依靠Object
?類的?clone()
?方法。
實現條件:
-
被拷貝的類實現?
Cloneable
?接口(這是一個標記接口,沒有方法)。 -
被拷貝的類重寫?
Object
?的?clone()
?方法,并在其中調用?super.clone()
。
public class Person implements Cloneable {private String name; // String (不可變對象,可視為基本類型)private int age; // 基本類型private Address address; // 引用類型// 構造方法、getters、setters 省略...@Overridepublic Object clone() throws CloneNotSupportedException {// 直接調用Object的clone()方法,完成基本數據類型和引用地址的復制return super.clone();}
}public class Address {private String city;private String street;// 構造方法、getters、setters...
}public class TestShallowCopy {public static void main(String[] args) throws CloneNotSupportedException {Address addr = new Address("北京", "長安街");Person original = new Person("張三", 25, addr);// 進行淺拷貝Person copied = (Person) original.clone();System.out.println(original == copied); // false,是兩個不同的對象System.out.println(original.getName() == copied.getName()); // true,String池或同一對象(可能)System.out.println(original.getAddress() == copied.getAddress()); // true!關鍵在這里:引用指向同一個Address對象// 修改拷貝對象的引用類型成員copied.getAddress().setCity("上海");// 原始對象的address也被修改了!System.out.println(original.getAddress().getCity()); // 輸出:上海}
}
2.2 深拷貝
2.2.1 方法一:重寫?clone()
?方法并遞歸克隆(常用)
// 1、讓 Address 類也變得可克隆(實現 Cloneable 并重寫 clone())
public class Address implements Cloneable {private String city;private String street;// ... 其他代碼 ...@Overridepublic Object clone() throws CloneNotSupportedException {return super.clone();}
}// 2、在 Person 的 clone() 方法中,不僅調用 super.clone(),還要手動克隆 address 字段
public class Person implements Cloneable {private String name;private int age;private Address address;// ... 其他代碼 ...@Overridepublic Object clone() throws CloneNotSupportedException {// 1. 先調用super.clone()完成淺拷貝Person copied = (Person) super.clone();// 2. 對引用類型字段,手動調用其clone()方法進行深拷貝copied.address = (Address) this.address.clone();// 3. 返回深拷貝后的對象return copied;}
}// 3、測試
public class TestDeepCopy {public static void main(String[] args) throws CloneNotSupportedException {Address addr = new Address("北京", "長安街");Person original = new Person("張三", 25, addr);Person copied = (Person) original.clone();System.out.println(original.getAddress() == copied.getAddress()); // false!現在是不同的Address對象// 修改拷貝對象的addresscopied.getAddress().setCity("上海");// 原始對象的address不受影響System.out.println(original.getAddress().getCity()); // 輸出:北京System.out.println(copied.getAddress().getCity()); // 輸出:上海}
}
2.2.2 方法二:通過序列化實現(更強大,但更重)
前提:所有涉及到的類都必須實現?java.io.Serializable
?接口。
import java.io.*;public class DeepCopyUtil {@SuppressWarnings("unchecked")public static <T extends Serializable> T deepCopy(T object) {try (ByteArrayOutputStream baos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(baos)) {// 將對象寫入字節流oos.writeObject(object);oos.flush();try (ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());ObjectInputStream ois = new ObjectInputStream(bais)) {// 從字節流中讀出新的對象return (T) ois.readObject();}} catch (IOException | ClassNotFoundException e) {throw new RuntimeException("Deep copy failed", e);}}
}// Person和Address類都需要實現Serializable接口
public class Person implements Serializable { ... }
public class Address implements Serializable { ... }// 使用工具類進行深拷貝
Person original = new Person(...);
Person copied = DeepCopyUtil.deepCopy(original); // 完美的深拷貝
優點:無需關心對象內部復雜的引用結構,序列化機制會自動完成所有遞歸復制。
缺點:性能開銷比?clone()
?方法大;所有相關類都必須實現?Serializable
?接口。
2.2.3?使用拷貝構造方法或拷貝工廠(推薦)
這是一種非常推薦的方式,它不依賴?Cloneable
?接口,代碼更清晰,也更靈活。
-
拷貝構造方法:接受一個同一類型的參數,并據此構造一個新對象。
-
拷貝工廠:一個靜態方法,用于完成拷貝。
public class Person {private String name;private int age;private Address address;// 拷貝構造方法public Person(Person other) {this.name = other.name;this.age = other.age;// 對引用類型,調用其拷貝構造方法進行深拷貝this.address = new Address(other.address); // 假設Address也有拷貝構造方法}// 拷貝工廠 (靜態方法)public static Person newInstance(Person other) {return new Person(other);}
}public class Address {private String city;private String street;// Address的拷貝構造方法public Address(Address other) {this.city = other.city;this.street = other.street;}
}// 使用
Person original = new Person(...);
Person copied = new Person(original); // 使用拷貝構造方法
// 或
Person copied2 = Person.newInstance(original); // 使用拷貝工廠
優點:代碼意圖明確,易于控制和擴展,是《Effective Java》推薦的方式。