【某大厂一面】java深拷贝和浅拷贝的区别
在 Java 中,深拷贝和浅拷贝的主要区别在于对象的复制方式和对象中引用类型的处理。下面是更详细的解释,其中也会有相应的代码说明:
1. 浅拷贝(Shallow Copy)
浅拷贝是指复制一个对象时,复制的是对象的“结构”,但对于对象内部的引用类型的成员变量,它们的引用地址并不会被复制,而是共享同一引用。换句话说,浅拷贝只是创建了一个新的对象,但如果该对象内部含有引用类型的成员变量,这些成员变量还是指向原始对象中的相同内存地址。
浅拷贝的特点
- 浅拷贝复制了对象本身,但并没有复制对象的引用类型成员。
- 对于引用类型字段(如数组、集合、对象),它们的引用会被共享。
浅拷贝的实现方式
- 通过
clone()
方法:Java 的Object
类提供了clone()
方法来实现浅拷贝。 - 通过
copy()
方法:如果是ArrayList
等集合类型,可以使用ArrayList
的clone()
方法进行浅拷贝。
示例:
import java.util.ArrayList;
class Person implements Cloneable {
String name;
ArrayList<String> hobbies;
public Person(String name, ArrayList<String> hobbies) {
this.name = name;
this.hobbies = hobbies;
}
// 重写 clone 方法
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone(); // 浅拷贝
}
@Override
public String toString() {
return "Person{name='" + name + "', hobbies=" + hobbies + "}";
}
}
public class ShallowCopyExample {
public static void main(String[] args) throws CloneNotSupportedException {
ArrayList<String> hobbies = new ArrayList<>();
hobbies.add("Reading");
hobbies.add("Swimming");
Person person1 = new Person("John", hobbies);
Person person2 = (Person) person1.clone(); // 浅拷贝
// 修改 person2 的 hobbies
person2.hobbies.add("Cooking");
System.out.println("person1: " + person1);
System.out.println("person2: " + person2);
}
}
输出:
person1: Person{name='John', hobbies=[Reading, Swimming, Cooking]}
person2: Person{name='John', hobbies=[Reading, Swimming, Cooking]}
在这个示例中,person1
和 person2
的 hobbies
共享同一个引用,因此修改 person2
中的 hobbies
也会影响到 person1
中的 hobbies
。
2. 深拷贝(Deep Copy)
深拷贝是指复制一个对象及其内部的所有对象,包括所有引用类型的字段,确保它们有独立的内存空间。换句话说,深拷贝不仅复制了对象本身,还递归地复制对象中包含的每个引用类型的成员变量。这样,原对象和新对象之间就完全独立了,它们不会共享任何引用。
深拷贝的特点
- 深拷贝会创建一个新的对象,并且递归地复制对象中引用类型的所有成员变量。
- 原始对象和新对象完全独立,修改新对象不会影响原始对象。
深拷贝的实现方式
- 手动实现深拷贝:通过逐个复制对象中的引用类型成员变量来实现。
- 通过序列化和反序列化:可以利用 Java 的序列化机制来实现深拷贝,这种方式适用于对象较复杂的情况。
- 通过
clone()
方法的深度实现:需要在clone()
方法中递归地克隆对象中的引用类型字段。
示例:
import java.util.ArrayList;
class Person implements Cloneable {
String name;
ArrayList<String> hobbies;
public Person(String name, ArrayList<String> hobbies) {
this.name = name;
this.hobbies = hobbies;
}
// 实现深拷贝
@Override
protected Object clone() throws CloneNotSupportedException {
Person cloned = (Person) super.clone(); // 浅拷贝
// 进行深拷贝,创建一个新的 hobbies 列表
cloned.hobbies = new ArrayList<>(this.hobbies); // 深拷贝
return cloned;
}
@Override
public String toString() {
return "Person{name='" + name + "', hobbies=" + hobbies + "}";
}
}
public class DeepCopyExample {
public static void main(String[] args) throws CloneNotSupportedException {
ArrayList<String> hobbies = new ArrayList<>();
hobbies.add("Reading");
hobbies.add("Swimming");
Person person1 = new Person("John", hobbies);
Person person2 = (Person) person1.clone(); // 深拷贝
// 修改 person2 的 hobbies
person2.hobbies.add("Cooking");
System.out.println("person1: " + person1);
System.out.println("person2: " + person2);
}
}
输出:
person1: Person{name='John', hobbies=[Reading, Swimming]}
person2: Person{name='John', hobbies=[Reading, Swimming, Cooking]}
在这个示例中,person1
和 person2
的 hobbies
列表是独立的,因此修改 person2
的 hobbies
不会影响 person1
。
3. 使用序列化实现深拷贝(通过反序列化)
使用序列化和反序列化可以方便地进行深拷贝,前提是对象及其成员变量都需要实现 Serializable
接口。
示例(通过序列化实现深拷贝):
import java.io.*;
class Person implements Serializable {
String name;
ArrayList<String> hobbies;
public Person(String name, ArrayList<String> hobbies) {
this.name = name;
this.hobbies = hobbies;
}
@Override
public String toString() {
return "Person{name='" + name + "', hobbies=" + hobbies + "}";
}
}
public class DeepCopyBySerialization {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ArrayList<String> hobbies = new ArrayList<>();
hobbies.add("Reading");
hobbies.add("Swimming");
Person person1 = new Person("John", hobbies);
// 深拷贝:序列化 -> 反序列化
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(bos);
out.writeObject(person1);
out.flush();
byte[] byteData = bos.toByteArray();
ByteArrayInputStream bis = new ByteArrayInputStream(byteData);
ObjectInputStream in = new ObjectInputStream(bis);
Person person2 = (Person) in.readObject();
// 修改 person2 的 hobbies
person2.hobbies.add("Cooking");
System.out.println("person1: " + person1);
System.out.println("person2: " + person2);
}
}
输出:
person1: Person{name='John', hobbies=[Reading, Swimming]}
person2: Person{name='John', hobbies=[Reading, Swimming, Cooking]}
4. 总结
- 浅拷贝:复制对象本身,但嵌套对象或引用类型的字段引用仍然指向原对象,即对象的引用被共享。
- 深拷贝:不仅复制对象本身,还递归地复制所有引用类型的字段,确保原对象和新对象完全独立。
对于简单的对象,使用 clone()
方法实现浅拷贝即可。对于复杂的对象,或者需要独立对象时,可以手动实现深拷贝,或者使用序列化方式。
小伙伴们在开发的过程中有更好的代码实现或者具体的使用场景吗,欢迎大家在评论区进行讨论