在Java編程中,理解深拷貝(Deep Copy)、淺拷貝(Shallow Copy)和引用拷貝(Reference Copy)是非常重要的。這三種拷貝方式涉及對象復制和內存管理。以下是對它們的詳細解釋:
1. 引用拷貝(Reference Copy)
引用拷貝是最簡單的一種拷貝方式。它只復制對象的引用,而不復制對象本身。換句話說,原始對象和新對象共享同一個內存地址。
Person person1 = new Person("Alice");
Person person2 = person1; // 引用拷貝person2.setName("Bob");System.out.println(person1.getName()); // 輸出 "Bob"
System.out.println(person2.getName()); // 輸出 "Bob"
在上述代碼中,person1
和person2
指向同一個對象,所以改變其中一個對象的屬性,另一個對象的屬性也會被改變。
2. 淺拷貝(Shallow Copy)
淺拷貝會創建一個新對象,但新對象中的成員變量(如果是對象)仍然是原對象的引用。淺拷貝僅復制對象的第一層屬性。
可以通過實現Cloneable
接口并重寫clone
方法來實現淺拷貝:
class Address {String city;public Address(String city) {this.city = city;}
}class Person implements Cloneable {String name;Address address;public Person(String name, Address address) {this.name = name;this.address = address;}@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone(); // 淺拷貝}
}public class Main {public static void main(String[] args) throws CloneNotSupportedException {Address address = new Address("New York");Person person1 = new Person("Alice", address);Person person2 = (Person) person1.clone();person2.name = "Bob";person2.address.city = "Los Angeles";System.out.println(person1.name); // 輸出 "Alice"System.out.println(person1.address.city); // 輸出 "Los Angeles"System.out.println(person2.name); // 輸出 "Bob"System.out.println(person2.address.city); // 輸出 "Los Angeles"}
}
在上述代碼中,person1
和person2
擁有不同的name
屬性,但是共享同一個Address
對象。
3. 深拷貝(Deep Copy)
深拷貝不僅創建一個新對象,還會遞歸地復制所有成員對象。這樣,原對象和新對象完全獨立,不共享任何引用。
深拷貝可以通過手動實現clone
方法來完成,或者使用序列化。
手動實現深拷貝的示例:
class Address implements Cloneable {String city;public Address(String city) {this.city = city;}@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}class Person implements Cloneable {String name;Address address;public Person(String name, Address address) {this.name = name;this.address = address;}@Overrideprotected Object clone() throws CloneNotSupportedException {Person cloned = (Person) super.clone();cloned.address = (Address) address.clone(); // 深拷貝return cloned;}
}public class Main {public static void main(String[] args) throws CloneNotSupportedException {Address address = new Address("New York");Person person1 = new Person("Alice", address);Person person2 = (Person) person1.clone();person2.name = "Bob";person2.address.city = "Los Angeles";System.out.println(person1.name); // 輸出 "Alice"System.out.println(person1.address.city); // 輸出 "New York"System.out.println(person2.name); // 輸出 "Bob"System.out.println(person2.address.city); // 輸出 "Los Angeles"}
}
在上述代碼中,person1
和person2
的所有屬性都是獨立的,修改一個對象的屬性不會影響另一個對象。
通過序列化和反序列化來實現Java的深拷貝是一種通用且方便的方法。它可以確保對象的完整復制,包括所有嵌套的成員對象。以下是具體的實現步驟:
- 讓類實現
Serializable
接口:確保需要深拷貝的類和它包含的所有成員類都實現Serializable
接口。 - 使用序列化和反序列化進行深拷貝:將對象寫入字節流,然后從字節流中讀出對象,從而實現對象的完全復制。
下面是一個具體的例子:
示例代碼
import java.io.*;class Address implements Serializable {private static final long serialVersionUID = 1L;String city;public Address(String city) {this.city = city;}@Overridepublic String toString() {return "Address{city='" + city + "'}";}
}class Person implements Serializable {private static final long serialVersionUID = 1L;String name;Address address;public Person(String name, Address address) {this.name = name;this.address = address;}@Overridepublic String toString() {return "Person{name='" + name + "', address=" + address + "}";}// 深拷貝方法public Person deepCopy() {try {// 序列化ByteArrayOutputStream bos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(bos);oos.writeObject(this);oos.flush();oos.close();// 反序列化ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());ObjectInputStream ois = new ObjectInputStream(bis);return (Person) ois.readObject();} catch (IOException | ClassNotFoundException e) {e.printStackTrace();return null;}}
}public class Main {public static void main(String[] args) {Address address = new Address("New York");Person person1 = new Person("Alice", address);Person person2 = person1.deepCopy();person2.name = "Bob";person2.address.city = "Los Angeles";System.out.println("Original: " + person1); // 原對象System.out.println("Copy: " + person2); // 深拷貝后的對象}
}
代碼解釋
- 實現
Serializable
接口:Address
和Person
類都實現了Serializable
接口,使它們可以被序列化。 - 深拷貝方法
deepCopy
:- 序列化:使用
ObjectOutputStream
將對象寫入ByteArrayOutputStream
。 - 反序列化:使用
ObjectInputStream
從ByteArrayInputStream
中讀出對象,生成一個新的對象副本。
- 序列化:使用
- 測試深拷貝:
- 創建原始對象
person1
,并通過deepCopy
方法生成person2
。 - 修改
person2
的屬性,驗證原始對象person1
不受影響,證明了對象的深拷貝。
- 創建原始對象
這種方法的優點是實現簡單,且適用于所有需要深拷貝的情況。然而,它也有一些限制,比如性能較慢(因為涉及IO操作)和必須實現Serializable
接口。
總結
- 引用拷貝:只復制引用,原對象和新對象指向同一個對象。
- 淺拷貝:創建新對象,但不遞歸復制成員對象,成員對象仍然是共享的引用。
- 深拷貝:創建新對象,并遞歸復制所有成員對象,完全獨立。
這些拷貝方式在實際應用中有不同的使用場景和適用性,根據需要選擇合適的拷貝方式可以有效管理內存和對象關系。