理解浅拷贝和深拷贝
代码演示浅拷贝
先看一段代码:
worker.java
/**
* <h1>员工</h1>
* */
@Data
public class Worker implements Cloneable {
private String name;
private Integer age;
private String gender;
private EducationInfo educationInfo;
public Worker(String name, Integer age, String gender, String school, String time) {
this.name = name;
this.age = age;
this.gender = gender;
this.educationInfo = new EducationInfo(school, time);
}
@Override
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
return null;
}
}
}
EducationInfo.java
/**
* <h1>教育信息</h1>
* */
@Data
@AllArgsConstructor
public class EducationInfo implements Cloneable*/{
private String school;
private String time;
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
Main.java测试克隆代码
/**
* <h1>理解深拷贝和浅拷贝</h1>
* */
@SuppressWarnings("all")
public class Main {
private static void copyTest() {
Worker worker1 = new Worker(
"lcry", 22, "m", "CSDN大学", "2021"
);
System.out.println("原始对象: " + worker1.getEducationInfo().getSchool());
Worker worker2 = (Worker) worker1.clone();
System.out.println("拷贝对象: " + worker2.getEducationInfo().getSchool());
worker2.getEducationInfo().setSchool("社会大学");
System.out.println("原始对象: " + worker1.getEducationInfo().getSchool());
System.out.println("拷贝对象: " + worker2.getEducationInfo().getSchool());
}
public static void main(String[] args) throws CloneNotSupportedException {
copyTest();
}
}
执行结果可想而知,当我们修改了work2的school属性值,发现work1的school属性值也变了,这就是浅拷贝,也同时说明Object类自带的clone方法是浅拷贝
图解浅拷贝深拷贝
看下图更加清晰理解。
实现深拷贝三种方式
可以有三种方式。我们按照前面的案例进行改造代码word类如下:
/**
* <h1>员工</h1>
* */
@Data
public class Worker implements Serializable {
private String name;
private Integer age;
private String gender;
private EducationInfo educationInfo;
public Worker(String name, Integer age, String gender, String school, String time) {
this.name = name;
this.age = age;
this.gender = gender;
this.educationInfo = new EducationInfo(school, time);
}
// @Override
// public Object clone() {
//
// // 第一种方式:直接通过new对象创建新的对象
Worker worker = new Worker(
name, age, gender, educationInfo.getSchool(), educationInfo.getTime()
);
return worker;
//
// // 第二种方式:通过父类clone再进行赋值
// try {
// Worker worker = (Worker) super.clone();
// worker.educationInfo = (EducationInfo) educationInfo.clone();
// return worker;
// } catch (CloneNotSupportedException ex) {
// return null;
// }
// }
// 推荐第三种方式:通过序列化和反序列化使用流进行深拷贝,注意类都要实现Serializable接口
public Worker clone() {
Worker worker = null;
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(this);
// 将流序列化成对象
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
worker = (Worker) ois.readObject();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
return worker;
}
}
总结
- Java 中 Object 中的 clone 方法默认是浅拷贝,针对引用类型执行的是同一个地址;
- 实现深拷贝的三种方式:
- 第一种方式:直接通过 new 对象创建新的对象,通过 new 出来的对象肯定是在内存中重新开辟一块空间存储所以可以实现深拷贝;
- 第二种方式:通过调用父类clone再进行重新复制,虽然调用父类 Native 修饰的 clone 方法比第一种方式速度快,此步骤如果继承类中有多个引用类型克隆相对麻烦;
- 第三种方式:通过序列化和反序列化使用流进行深拷贝(注意类都要实现 Serializable 接口),因为保存在流中的数据相当于新的,若要实现对象深拷贝,推荐使用此方式。