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)深克隆:
@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 Security登錄接口兼容JSON格式登錄實現(xiàn)示例
前后端分離中,前端和后端的數(shù)據(jù)交互通常是JSON格式,本文主要介紹了Spring Security登錄接口兼容JSON格式登錄實現(xiàn)示例,具有一定的參考價值,感興趣的可以了解一下2024-01-01
Java Springboot websocket使用案例詳解
這篇文章主要介紹了Java Springboot websocket使用案例詳解,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-09-09
Spring接口版本控制方案及RequestMappingHandlerMapping接口介紹(最新推薦)
RequestMappingHandlerMapping接口是Spring MVC中的一個核心組件,負(fù)責(zé)處理請求映射和處理器的匹配這篇文章主要介紹了Spring接口版本控制方案及RequestMappingHandlerMapping接口介紹,需要的朋友可以參考下2024-07-07
java用重定向方法從文件中讀入或?qū)懭霐?shù)據(jù)
這篇文章主要為大家詳細(xì)介紹了用重定向方法從文件中讀入或?qū)懭霐?shù)據(jù),具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-03-03
java實現(xiàn)多線程的兩種方式繼承Thread類和實現(xiàn)Runnable接口的方法
下面小編就為大家?guī)硪黄猨ava實現(xiàn)多線程的兩種方式繼承Thread類和實現(xiàn)Runnable接口的方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-09-09
使用httpclient實現(xiàn)免費的google翻譯api
這篇文章主要介紹了使用httpclient實現(xiàn)免費的google翻譯api的方法,大家參考使用吧2014-01-01
SpringBoot日程管理Quartz與定時任務(wù)Task實現(xiàn)詳解
定時任務(wù)是企業(yè)級開發(fā)中必不可少的組成部分,諸如長周期業(yè)務(wù)數(shù)據(jù)的計算,例如年度報表,諸如系統(tǒng)臟數(shù)據(jù)的處理,再比如系統(tǒng)性能監(jiān)控報告,還有搶購類活動的商品上架,這些都離不開定時任務(wù)。本節(jié)將介紹兩種不同的定時任務(wù)技術(shù)2022-09-09

