一、什麼是深度拷貝
在Java中,當我們需要複製一個對象的屬性到另一個對象時,常常會選擇使用淺拷貝。淺拷貝是指將對象的引用進行複製,這意味著新對象與原對象使用的是同一個內存地址,所以改變新對象中的屬性值也會影響到原對象的屬性值。相反,深度拷貝是指將對象包含的所有子對象也進行拷貝,這樣新對象與原對象是完全獨立的,它們的屬性值互不影響。
二、使用clone方法實現List深度拷貝
在Java中,Object類實現了一個叫做clone的方法,可以用來創建對象的副本。如果一個類想要使用clone方法,必須實現Cloneable介面,這個介面是一個標記介面,它並沒有需要實現的方法。
首先,我們需要定義一個可拷貝的類,例如:
public class Person implements Cloneable { private String name; private int age; private List hobbies; // 需要進行深度拷貝的屬性 public Person(String name, int age, List hobbies) { this.name = name; this.age = age; this.hobbies = hobbies; } // 省略getter和setter方法 @Override protected Object clone() throws CloneNotSupportedException { // 先調用父類的clone方法獲得一個淺拷貝的Person對象 Person clone = (Person) super.clone(); // 再將hobbies屬性進行深度拷貝 clone.hobbies = new ArrayList(hobbies); return clone; } }
在上面的代碼中,我們先調用父類的clone方法獲得一個淺拷貝的Person對象,然後將hobbies屬性進行深度拷貝,最後返回新的Person對象。
接下來,我們可以測試一下這個深度拷貝方法是否可用:
public static void main(String[] args) throws CloneNotSupportedException { List originalList = new ArrayList(); originalList.add(new Person("Tom", 20, Arrays.asList("swimming", "reading"))); originalList.add(new Person("Jerry", 18, Arrays.asList("music", "gaming"))); List clonedList = new ArrayList(); for (Person person : originalList) { clonedList.add((Person) person.clone()); } // 改變hobbies屬性值 clonedList.get(0).getHobbies().add("hiking"); System.out.println(originalList.get(0).getHobbies()); // [swimming, reading] System.out.println(clonedList.get(0).getHobbies()); // [swimming, reading, hiking] }
可以看到,原列表和克隆列表中的hobbies屬性值值發生了變化,證明深度拷貝方法生效。
三、使用序列化實現List深度拷貝
除了使用clone方法實現深度拷貝外,我們還可以使用Java的序列化機制。序列化是將對象轉換成位元組序列的過程,而反序列化則是將位元組序列轉換回對象。如果我們將一個對象通過序列化後再反序列化回來,得到的對象就是原對象的一個深度拷貝。
同樣地,我們需要定義一個可序列化的類:
public class Person implements Serializable { private String name; private int age; private List hobbies; // 需要進行深度拷貝的屬性 public Person(String name, int age, List hobbies) { this.name = name; this.age = age; this.hobbies = hobbies; } // 省略getter和setter方法 }
下面是序列化和反序列化的代碼:
public static void main(String[] args) throws IOException, ClassNotFoundException { List originalList = new ArrayList(); originalList.add(new Person("Tom", 20, Arrays.asList("swimming", "reading"))); originalList.add(new Person("Jerry", 18, Arrays.asList("music", "gaming"))); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream); objectOutputStream.writeObject(originalList); ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray()); ObjectInputStream objectInputStream = new ObjectInputStream(inputStream); List clonedList = (List) objectInputStream.readObject(); // 改變hobbies屬性值 clonedList.get(0).getHobbies().add("hiking"); System.out.println(originalList.get(0).getHobbies()); // [swimming, reading] System.out.println(clonedList.get(0).getHobbies()); // [swimming, reading, hiking] }
可以看到,經過序列化和反序列化後,原列表和克隆列表中的hobbies屬性值也發生了變化,證明深度拷貝方法生效。
四、使用Apache Commons Lang實現List深度拷貝
如果我們不想自己實現深度拷貝的方法,可以使用Apache Commons Lang庫中的SerializationUtils類來實現。這個類提供了一組靜態方法,可以對任何實現了Serializable介面的對象進行深度拷貝。
接下來看看如何使用這個類:
public static void main(String[] args) throws IOException, ClassNotFoundException { List originalList = new ArrayList(); originalList.add(new Person("Tom", 20, Arrays.asList("swimming", "reading"))); originalList.add(new Person("Jerry", 18, Arrays.asList("music", "gaming"))); List clonedList = (List) SerializationUtils.clone((Serializable) originalList); // 改變hobbies屬性值 clonedList.get(0).getHobbies().add("hiking"); System.out.println(originalList.get(0).getHobbies()); // [swimming, reading] System.out.println(clonedList.get(0).getHobbies()); // [swimming, reading, hiking] }
可以看到,使用SerializationUtils類的clone方法同樣可以實現深度拷貝,且更加方便。
五、總結
本文介紹了Java中三種實現List深度拷貝的方法:使用clone方法、使用序列化、使用Apache Commons Lang庫的SerializationUtils類。在使用這些方法時,需要注意對象中是否包含子對象,需要對子對象也進行深度拷貝。
原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/237424.html