一、什么是深度拷贝
在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/n/237424.html
微信扫一扫
支付宝扫一扫