1.深拷贝和浅拷贝的理解
1.1 浅拷贝
顾名思义,浅,即拷贝得比较浅,拷贝一个对象时除了对象下的8大基本数据类型和String类型外,其他自定义对象构成的属性都是指向source对象的自定义对象构成的属性的地址。所以,浅拷贝的使用场景一般是拷贝后不会对该对象的自定义对象的属性做修改,只做查询的时候使用。如果拷贝后修改其值再使用source对象,你会发现,source对象的自定义对象的属性值也被修改了。
1.2 深拷贝
同上,深,即拷贝得比较深,指的是完全拷贝,对象及对象中的属性都使用堆中新开辟的地址。那么使用场景也明确了,弥补浅拷贝的缺点,你可以对新拷贝的对象为所欲为。虽然深拷贝的优点很诱人,但是能不用深拷贝的地方我们还是尽量别用,毕竟从内存的角度考虑新开辟空间我们的可用内存也减少了。
2.实战中的拷贝方式有哪些呢?
2.1 BeanUtils.copyProperties(Object source, Object target)方式(浅拷贝)
Spring早就为我们开发出了我们想要的东西。springframework包下的BeanUtils.copyProperties()方法。这个静态方法使用起来非常简单,传入source拷贝源对象和target目标对象即可。不过需要注意的是这个方法是浅拷贝。
使用案例:
可用看到People对象name属性是直接拷贝的值,而 List属性是指向的source对象的List属性的地址。对拷贝后的List的值做修改时source对象的List的值跟着被修改了
2.2 实现Serializable接口方式(深拷贝)
Serializable实现拷贝的原理,首先江source对象转换成二进制流,这个时候我们可以将流持久化到磁盘中也可以将流暂存在内存中,这里我们只是为了深拷贝对象没必要将流持久化到磁盘直接存内存中即可,最后将二进制流反序列化成对像便完成了拷贝,因为是对流做复制操作所以Serializable拷贝方式是深拷贝。
使用案例:
第一步:实体实现Serializable接口
Girl实体:
import java.io.Serializable;
public class Girl implements Serializable {
private String song;
public String getSong() {
return song;
}
public void setSong(String song) {
this.song = song;
}
@Override
public String toString() {
return "Girl{" +
"song='" + song + '\'' +
'}';
}
}
Boy实体:
import java.io.Serializable;
public class Boy implements Serializable {
private String work;
public String getWork() {
return work;
}
public void setWork(String work) {
this.work = work;
}
@Override
public String toString() {
return "Boy{" +
"work='" + work + '\'' +
'}';
}
}
第二步:深度复制方法
People实体:
import java.io.*;
import java.util.List;
public class People implements Serializable{
private String name;
private int age;
private List<Boy> boys;
private List<Girl> girls;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public List<Boy> getBoys() {
return boys;
}
public void setBoys(List<Boy> boys) {
this.boys = boys;
}
public List<Girl> getGirls() {
return girls;
}
public void setGirls(List<Girl> girls) {
this.girls = girls;
}
@Override
public String toString() {
return "People{" +
"name='" + name + '\'' +
", age=" + age +
", boys=" + boys.toString() +
", girls=" + girls.toString() +
'}';
}
// 深度复制
public People clone() {
People people = 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);
people = (People) ois.readObject();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
return people;
}
}
第三步:测试
可以看到,上面代码已经完成了深度拷贝!
2.3 实现Cloneable接口方式(深拷贝和浅拷贝)
Cloneable是JDK为我们提供的clone方法,是Object9大方法之一,即可实现浅拷贝,也可深拷贝。设计模式中的原型模式就是用的它。
2.3.1 浅拷贝
第一步:实体实现Cloneable接口
Girl实体
public class Girl implements Cloneable {
private String song;
public String getSong() {
return song;
}
public void setSong(String song) {
this.song = song;
}
@Override
public String toString() {
return "Girl{" +
"song='" + song + '\'' +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
Boy实体:
public class Boy implements Cloneable{
private String work;
public String getWork() {
return work;
}
public void setWork(String work) {
this.work = work;
}
@Override
public String toString() {
return "Boy{" +
"work='" + work + '\'' +
'}';
}
@Override
protected Boy clone() throws CloneNotSupportedException {
return (Boy)super.clone();
}
}
People实体:
import java.util.List;
public class People implements Cloneable{
private String name;
private int age;
private List<Boy> boys;
private List<Girl> girls;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public List<Boy> getBoys() {
return boys;
}
public void setBoys(List<Boy> boys) {
this.boys = boys;
}
public List<Girl> getGirls() {
return girls;
}
public void setGirls(List<Girl> girls) {
this.girls = girls;
}
@Override
public String toString() {
return "People{" +
"name='" + name + '\'' +
", age=" + age +
", boys=" + boys.toString() +
", girls=" + girls.toString() +
'}';
}
@Override
protected People clone() throws CloneNotSupportedException {
return (People)super.clone();
}
}
第二步:测试
这样就实现了浅拷贝,如果需要实现深拷贝我们需要将People对象下的包装类型做进一步的操作,如下
2.3.2 深拷贝
People实体:
import java.util.ArrayList;
import java.util.List;
public class People implements Cloneable{
private String name;
private int age;
private List<Boy> boys;
private List<Girl> girls;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public List<Boy> getBoys() {
return boys;
}
public void setBoys(List<Boy> boys) {
this.boys = boys;
}
public List<Girl> getGirls() {
return girls;
}
public void setGirls(List<Girl> girls) {
this.girls = girls;
}
@Override
public String toString() {
return "People{" +
"name='" + name + '\'' +
", age=" + age +
", boys=" + boys.toString() +
", girls=" + girls.toString() +
'}';
}
@Override
protected People clone() throws CloneNotSupportedException {
People people = (People)super.clone();
List<Boy> boysCopy = new ArrayList<>();
List<Girl> girlsCopy = new ArrayList<>();
boys.forEach(boy -> {
try {
boysCopy.add(boy.clone());
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
});
people.setBoys(boysCopy);
girls.forEach(girl -> {
try {
girlsCopy.add(girl.clone());
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
});
people.setGirls(girlsCopy);
return people;
}
}
测试
这里对People对象下的包装类型做进一步的拷贝 ,这样就完成了深拷贝操作!