一、什么是深度拷贝
在Java中,当我们需要复制一个对象的属性到另一个对象时,常常会选择使用浅拷贝。浅拷贝是指将对象的引用进行复制,这意味着新对象与原对象使用的是同一个内存地址,所以改变新对象中的属性值也会影响到原对象的属性值。相反,深度拷贝是指将对象包含的所有子对象也进行拷贝,这样新对象与原对象是完全独立的,它们的属性值互不影响。
二、使用clone方法实现List深度拷贝
在Java中,Object类实现了一个叫做clone的方法,可以用来创建对象的副本。如果一个类想要使用clone方法,必须实现Cloneable接口,这个接口是一个标记接口,它并没有需要实现的方法。
首先,我们需要定义一个可拷贝的类,例如:
public class Person implements Cloneable { private String name; private int age; private Listhobbies; // 需要进行深度拷贝的属性 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 { ListoriginalList = 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 Listhobbies; // 需要进行深度拷贝的属性 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 { ListoriginalList = 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 { ListoriginalList = 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类。在使用这些方法时,需要注意对象中是否包含子对象,需要对子对象也进行深度拷贝。