Java中實現(xiàn)對象深克隆的四種方式
在Java中實現(xiàn)對象的深克隆(Deep Clone)需要確保對象及其所有引用類型的字段都被復(fù)制,而不是共享相同的內(nèi)存地址。以下是幾種常見的實現(xiàn)方式:
方法1:手動實現(xiàn)深克隆
通過重寫 clone() 方法并逐層復(fù)制引用對象:
@Data
@AllArgsConstructor
@NoArgsConstructor
class Address implements Cloneable {
private String city;
@Override
protected Address clone() throws CloneNotSupportedException {
return (Address) super.clone();
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
class Person implements Cloneable {
private String name;
private Address address;
private List<String> hobbies;
@Override
protected Person clone() throws CloneNotSupportedException {
Person cloned = (Person) super.clone();
// 深克隆引用對象
cloned.address = this.address.clone();
// 若hobbies是自定義對象列表,需逐個深克隆
return cloned;
}
}
public class DeepCloneExample {
public static void main(String[] args) throws CloneNotSupportedException {
Address addr = new Address("Beijing");
List<String> hobbies = List.of("reading", "swimming");
Person p1 = new Person("Alice", addr, hobbies);
// 深克隆
Person p2 = p1.clone();
// 修改克隆對象的屬性,不會影響原對象
p2.getAddress().setCity("Shanghai");
p2.getHobbies().add("running");
System.out.println(p1.getAddress().getCity()); // 輸出: Beijing
System.out.println(p2.getAddress().getCity()); // 輸出: Shanghai
System.out.println(p1.getHobbies()); // 輸出: [reading, swimming]
System.out.println(p2.getHobbies()); // 輸出: [reading, swimming, running]
}
}
關(guān)鍵點:
- 實現(xiàn)
Cloneable接口:標(biāo)記類支持克隆。 - 重寫
clone()方法:調(diào)用super.clone()后,手動復(fù)制引用類型字段。 - 逐層深克隆:對每個引用對象(如
Address)也需實現(xiàn)深克隆。
方法2:使用序列化 Serializable
通過序列化和反序列化實現(xiàn)深克?。?/p>
@Data
@AllArgsConstructor
@NoArgsConstructor
class Address implements Serializable {
private static final long serialVersionUID = 1L;
private String city;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
class Person implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private Address address;
private List<String> hobbies;
// 深克隆方法
public Person deepClone() {
// 序列化
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.txt"))) {
oos.writeObject(this);
} catch (Exception e) {
e.printStackTrace();
}
// 反序列化
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.txt"))) {
return (Person) ois.readObject();
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class DeepCloneBySerialization {
public static void main(String[] args) {
Address addr = new Address("Beijing");
List<String> hobbies = new ArrayList<>(List.of("reading", "swimming"));
Person p1 = new Person("Alice", addr, hobbies);
// 深克隆
Person p2 = p1.deepClone();
// 修改克隆對象的屬性,不會影響原對象
p2.getAddress().setCity("Shanghai");
p2.getHobbies().add("running");
System.out.println(p1.getAddress().getCity()); // 輸出: Beijing
System.out.println(p2.getAddress().getCity()); // 輸出: Shanghai
System.out.println(p1.getHobbies()); // 輸出: [reading, swimming]
System.out.println(p2.getHobbies()); // 輸出: [reading, swimming, running]
}
}
使用 ByteArrayOutputStream 將對象序列化為內(nèi)存中的字節(jié)數(shù)組。 再通過 ByteArrayInputStream 從內(nèi)存中讀取該字節(jié)數(shù)組并反序列化成新對象。 整個過程不涉及任何磁盤 I/O,完全在內(nèi)存中完成,效率更高。
import java.io.*;
public class DeepCloneUtil {
public static <T extends Serializable> T deepClone(T object) {
try (
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
) {
// 序列化對象到內(nèi)存中的字節(jié)數(shù)組
oos.writeObject(object);
try (
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
) {
// 反序列化并返回新對象
return (T) ois.readObject();
}
} catch (IOException | ClassNotFoundException e) {
throw new RuntimeException("深克隆失敗", e);
}
}
public static void main(String[] args) {
Address address = new Address("Beijing");
Person person1 = new Person("Alice", address, List.of("reading", "swimming"));
// 深克隆
Person person2 = DeepCloneUtil.deepClone(person1);
// 修改克隆對象的屬性,不會影響原對象
person2.getAddress().setCity("Shanghai");
System.out.println(person1.getAddress().getCity()); // 輸出: Beijing
System.out.println(person2.getAddress().getCity()); // 輸出: Shanghai
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
class Address implements Serializable {
private static final long serialVersionUID = 1L;
private String city;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
class Person implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private Address address;
private List<String> hobbies;
}
關(guān)鍵點:
- 實現(xiàn)
Serializable接口:所有類(包括嵌套類)必須可序列化。 - 通過流操作:將對象序列化為字節(jié)流,再反序列化為新對象,確保所有引用都被復(fù)制。
- 自動處理嵌套對象:無需手動逐層克隆
方法3:使用第三方庫 Apache Commons Lang(推薦使用,因為一般項目中都會引入這個依賴)
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
使用 SerializationUtils.clone():
import org.apache.commons.lang3.SerializationUtils;
// 確保所有類實現(xiàn)Serializable接口
class Address implements Serializable { /* ... */ }
class Person implements Serializable { /* ... */ }
public class DeepCloneWithLibrary {
public static void main(String[] args) {
// 創(chuàng)建對象
Person p1 = new Person("Alice", new Address("Beijing"), List.of("reading"));
// 深克隆
Person p2 = SerializationUtils.clone(p1);
// 修改克隆對象,不影響原對象
p2.getAddress().setCity("Shanghai");
}
}
方法4:使用第三方庫 Hutool
使用 CloneUtil.cloneByStream(obj):
需要對象及其嵌套對象實現(xiàn) java.io.Serializable 接口
@Data
@AllArgsConstructor
@NoArgsConstructor
class Address implements java.io.Serializable {
private String city;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
class Person implements java.io.Serializable {
private String name;
private Address address;
}
public class HutoolDeepCloneExample {
public static void main(String[] args) {
Address addr = new Address("Beijing");
Person p1 = new Person("Alice", addr);
Person p2 = CloneUtil.cloneByStream(p1);
p2.getAddress().setCity("Shanghai"); // 修改克隆對象的屬性,不會影響原對象
System.out.println(p1.getAddress().getCity()); // 輸出: Beijing
System.out.println(p2.getAddress().getCity()); // 輸出: Shanghai
}
}
到此這篇關(guān)于Java中實現(xiàn)對象深克隆的四種方式的文章就介紹到這了,更多相關(guān)Java實現(xiàn)對象深克隆內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解Spring Boot的GenericApplicationContext使用教程
這篇教程展示了如何在Spring應(yīng)用程序中使用GenericApplicationContext 。小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-11-11
Java 中的 getDeclaredMethods() 方法(使用與原理)
文章介紹了Java反射機(jī)制中的`getDeclaredMethods()`方法,詳細(xì)講解了其使用方法、原理、注意事項以及實際應(yīng)用場景,幫助讀者更好地理解和應(yīng)用這一強大的工具,感興趣的朋友一起看看吧2024-12-12
Springboot項目基于Devtools實現(xiàn)熱部署步驟詳解
這篇文章主要介紹了Springboot項目基于Devtools實現(xiàn)熱部署,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-06-06
mybatis整合ehcache做三級緩存的實現(xiàn)方法
ehcache是一個快速內(nèi)存緩存框架,java項目里用起來很方便,下面這篇文章主要給大家介紹了關(guān)于mybatis整合ehcache做三級緩存的實現(xiàn)方法,文中通過實例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-06-06

