前言
在我们项目开发过程中,对象复制是我们必不可少的关键操作,浅拷贝和深拷贝就是我们选择使用进行对象复制的。之前的自己,对这2个有所了解,也能说出个七七八八,但是还是决定写这篇文章来聊一聊它俩,其一是加深印象,其二是在温故知新。
浅拷贝(Shallow Copy)
只复制对象的最外层数据,对于对象内部的引用类型(String除外),仅复制引用地址,而不是复制对象内部的引用类型数据。结果就是源对象和复制对象共享相同的一个内部对象实例,若内部对象被修改,也会影响另一个对象。 下面是浅拷贝的简单实现例子:
public class User implements Cloneable {
private int id;
private String name;
private List<String> hobby;
public User() {
}
public User(int id, String name, List<String> hobby) {
this.id = id;
this.name = name;
this.hobby = hobby;
}
/**
* 默认浅拷贝
*
* @return 对象
* @throws CloneNotSupportedException
*/
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<String> getHobby() {
return hobby;
}
public void setHobby(List<String> hobby) {
this.hobby = hobby;
}
public static void main(String[] args) throws CloneNotSupportedException {
List<String> hobby = new ArrayList<>();
hobby.add("看电影");
String name = "linyi";
User user_1 = new User(1, name, hobby);
//浅拷贝一个User对象
User user_2 = (User) user_1.clone();
//修改user_2的name
name = "ly";
user_2.setName(name);
//打印user_1和user_2的name
System.out.println("user_1:" + user_1.getName());
System.out.println("user_2:" + user_2.getName());
//修改user_2的hobby
hobby.add("看动漫");
System.out.println("user_1:" + user_1.getHobby());
System.out.println("user_2:" + user_2.getHobby());
}
}
为什么String也是引用类型,却没有修改呢?因为String它是不可变的对象呀!每次对String的修改就会重新创建一个新的String对象,而原始的String对象没有发生改变。
浅拷贝草图:
深拷贝(Deep Copy)
对对象进行递归复制,就是不仅复制最外层数据,还将所有的引用类型的对象进行复制,形成一份完全对立的对象副本。拷贝对象和源对象之间互不影响,修改任何一个对象都不会影响另一个对象。
public class User implements Serializable {
private int id;
private String name;
private List<String> hobby;
public User() {
}
public User(int id, String name, List<String> hobby) {
this.id = id;
this.name = name;
this.hobby = hobby;
}
/**
* 深拷贝方法
*
* @return 深拷贝后的 User 对象
*/
protected User deepCopy() throws Exception {
// 使用 try-with-resources 确保资源正确关闭
try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream)) {
objectOutputStream.writeObject(this); // 将当前对象写入字节流
// 使用 try-with-resources 确保资源正确关闭
try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream)) {
return (User) objectInputStream.readObject(); // 从字节流读取并返回深拷贝对象
}
}
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<String> getHobby() {
return hobby;
}
public void setHobby(List<String> hobby) {
this.hobby = hobby;
}
public static void main(String[] args) throws Exception {
//创建一个User对象
List<String> hobby = new ArrayList<>();
hobby.add("看电影");
String name = "linyi";
User user_1 = new User(1, name, hobby);
//浅拷贝一个User对象
User user_2 = user_1.deepCopy();
System.out.println("user_1:"+ "hobby的哈希值"+System.identityHashCode(user_1.getHobby())+"--hobby:"+ user_1.getHobby());
System.out.println("user_2:"+"hobby的哈希值"+ System.identityHashCode(user_2.getHobby())+"--hobby:"+ user_2.getHobby());
}
}
运行结果:
哈希值不通,不是一个对象。 深拷贝草图:
流程图
graph TD
A[原对象] --> B[基本类型字段]
A --> C[引用类型字段]
C --> D[堆内存对象X]
subgraph 浅拷贝
E[拷贝对象] --> F[基本类型拷贝值]
E --> G[引用类型复制地址]
G --> D
end
subgraph 深拷贝
H[拷贝对象] --> I[基本类型拷贝值]
H --> J[引用类型创建新对象]
J --> K[堆内存新对象Y]
end
浅拷贝VS深拷贝
| 浅拷贝 | 深拷贝 | |
|---|---|---|
| 基本数据类型 | 值拷贝 | 值拷贝 |
| 引用数据类型(String除外) | 复制内存地址(与源对象同一地址) | 递归创建新对象(一个新地址) |
| 内存消耗 | 小 | 大 |
| 修改影响 | 修改引用对象,源对象会改变 | 修改引用对象,源对象不会改变 |
| 复杂程度 | 小 | 大 |
| 典型实现方式 | Object.clone()默认实现 | 递归实现、序列化和反序列化 |
| 适用场景 | 高性能拷贝操作、需共享引用对象 | 需要完全隔离拷贝前后对象、涉及线程安全的数据隔离 |