Java深拷贝和浅拷贝的区别
在Java中,深拷贝(Deep Copy)和浅拷贝(Shallow Copy)是对象复制时的两种策略,它们的主要区别在于复制的深度和对象的引用关系处理上。
(1)浅拷贝(Shallow Copy)
浅拷贝只复制对象的基本数据类型字段值和引用类型字段的内存地址(即引用),而不复制引用类型字段所指向的对象本身。因此,原对象和拷贝对象共享对同一个引用类型对象的引用。对拷贝对象修改非基本数据类型字段会影响到原对象,因为它们引用的是同一个对象。
在Java中,使用赋值运算符=
、拷贝构造函数
或实现了Cloneable接口并使用Object.clone()方法
进行的拷贝通常是浅拷贝。
示例
假设我们有一个简单的Person类,它包含一个基本数据类型字段和一个引用类型字段(例如Address):
class Address {
String street;
String city;
// 构造器、getter和setter省略
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Address address = (Address) o;
return Objects.equals(street, address.street) &&
Objects.equals(city, address.city);
}
@Override
public int hashCode() {
return Objects.hash(street, city);
}
}
class Person implements Cloneable {
String name;
Address address;
// 构造器、getter和setter省略
@Override
protected Person clone() throws CloneNotSupportedException {
return (Person) super.clone(); // 浅拷贝
}
// 为了演示,重写toString方法
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", address=" + address +
'}';
}
}
public class ShallowCopyExample {
public static void main(String[] args) throws CloneNotSupportedException {
Address address = new Address();
address.street = "123 Main St";
address.city = "Anytown";
Person originalPerson = new Person();
originalPerson.name = "John Doe";
originalPerson.address = address;
Person clonedPerson = originalPerson.clone();
clonedPerson.address.city = "New City"; // 修改拷贝对象的地址
System.out.println(originalPerson); // 输出将显示地址已被修改
System.out.println(clonedPerson);
}
}
在这个例子中,当我们修改clonedPerson的地址时,originalPerson的地址也会被修改,因为它们引用的是同一个Address对象。
(2)深拷贝(Deep Copy)
深拷贝会复制对象的所有字段值,包括基本数据类型和引用类型字段。对于引用类型字段,深拷贝会创建一个新的对象,并复制原对象中的引用类型字段所指向的对象到新对象中。因此,原对象和拷贝对象是完全独立的,对拷贝对象的修改不会影响到原对象。
在Java中,实现深拷贝通常需要手动编写代码或使用序列化(Serialization)和反序列化(Deserialization)
的方式来实现。通过序列化将对象转换为字节序列,然后反序列化这些字节序列来创建一个新的对象,从而实现深拷贝。
示例 为了进行深拷贝,我们需要确保所有引用类型字段都被复制,并创建新的对象实例:
class Person implements Cloneable {
String name;
Address address;
// 构造器、getter和setter省略
@Override
protected Object clone() throws CloneNotSupportedException {
Person cloned = (Person) super.clone(); // 浅拷贝Person对象
cloned.address = (Address) address.clone(); // 假设Address也实现了Cloneable并覆盖了clone方法
return cloned;
}
// 为了演示,假设Address类也实现了Cloneable并覆盖了clone方法
// ...
// toString方法省略
}
// Address类也需要实现Cloneable并覆盖clone方法
class Address implements Cloneable {
String street;
String city;
// 构造器、getter、setter和clone方法省略
// toString方法省略
}
public class DeepCopyExample {
public static void main(String[] args) throws CloneNotSupportedException {
// ... 初始化originalPerson的代码与ShallowCopyExample相同 ...
Person deepClonedPerson = (Person) originalPerson.clone(); // 进行深拷贝
deepClonedPerson.address.city = "New City"; // 修改拷贝对象的地址
System.out.println(originalPerson); // 输出将不会显示地址已被修改
System.out.println(deepClonedPerson);
}
}
在这个深拷贝的例子中,即使我们修改了deepClonedPerson的地址,originalPerson的地址也不会被修改,因为它们引用的是不同的Address对象实例。注意,在实际应用中,如果对象图很复杂,可能需要使用更复杂的深拷贝策略,如序列化/反序列化或使用专门的库(如Apache Commons Lang的SerializationUtils)来实现深拷贝。
总结
在面试中,可以这样总结深拷贝和浅拷贝的区别:
深拷贝和浅拷贝的主要区别在于对引用类型字段的处理上。浅拷贝只复制引用类型字段的内存地址,而深拷贝会创建一个新的对象并复制原对象中的引用类型字段所指向的对象到新对象中。因此,浅拷贝的拷贝对象和原对象共享对同一个引用类型对象的引用,而深拷贝的拷贝对象和原对象是完全独立的。
在实现深拷贝时,需要注意递归地复制对象的所有字段,包括嵌套的对象和集合等。如果对象的字段中包含循环引用,则需要特别处理以避免无限递归。此外,深拷贝通常比浅拷贝更消耗资源,因为它需要创建新的对象并复制对象的所有字段值。
评论( 0 )