概念
对象拷贝分为浅拷贝和深拷贝;
浅拷贝:只拷贝了源对象指向堆空间中内存地址,所以源对象的值发生变化时,拷贝对象的值也会发生变化;
深拷贝:源对象和新对象指向堆空间中内存地址不同,即使源对象的值发生变化时,拷贝对象的值也不会改变
浅拷贝
1)直接赋值
Object a=new Object();
Object b=a;
这样只是将对象a的引用地址赋值给了b,当a对象值改变的时候,b对应的值也会改变
2)使用Cloneable,重写clone方法实现浅拷贝
首先创建两个类
@Data
@AllArgsConstructor
public class Persion implements Cloneable{
private String name;
private Integer age;
private Course course;
@Override
public Persion clone() throws CloneNotSupportedException {
Persion persion = (Persion) super.clone();
return persion;
}
}
@Data
@AllArgsConstructor
public class Course {
private String courseName;
private Integer score;
}
测试
public static void main(String[] args) throws CloneNotSupportedException {
Course course=new Course("语文",60);
Persion red=new Persion("小红",10,course);
Persion black=red.clone();
black.setName("小明");
black.getCourse().setCourseName("数学");
System.out.println(black.getName()+" "+black.getCourse().getCourseName()+" "+black.hashCode()+" "+black.getCourse().hashCode());
System.out.println(red.getName()+" "+red.getCourse().getCourseName()+" "+red.hashCode()+" "+red.getCourse().hashCode());
}
结果:
小明 数学 -1611798689 48879495
小红 数学 -1589896237 48879495
结论:重写Cloneable的clone方法,只能拷贝当前对象,对于该对象引用的对象并没有改变;
如果要当前对象的引用也拷贝则需要当前对象的引用对象也实现Cloneable的接口,重写clone方法;
3)使用Spring提供的copyProperties方法实现浅拷贝
Course course=new Course("语文",60);
Persion red=new Persion("小红",10,course);
Persion black=new Persion();
BeanUtils.copyProperties(red,black);
black.setName("小明");
black.getCourse().setCourseName("数学");
System.out.println(black.getName()+" "+black.getCourse().getCourseName()+" "+black.hashCode()+" "+black.getCourse().hashCode());
System.out.println(red.getName()+" "+red.getCourse().getCourseName()+" "+red.hashCode()+" "+red.getCourse().hashCode());
结果:
小明 数学 -1611798689 48879495
小红 数学 -1589896237 48879495
深拷贝
深拷贝对引用数据类型的成员变量中的所有的对象都开辟了内存空间,创建内存空间和拷贝整个对象,深拷贝相比于浅拷贝速度较慢并且花销较大。
1)使用Cloneable,重写clone方法实现深拷贝
即是将当前对象应用的所有对象都实现Cloneable并重写clone方法,但是这样比较麻烦;不常用
序列化实现深拷贝
2)使用apache的SerializationUtils实现深拷贝
Persion black=SerializationUtils.clone(red);
结果:
小明 数学 -1611798689 48879495
小红 语文 -1571722113 67053619
可以看到,Persion的hashCode和Course的hashCode都发生了变化,说明当前类中的引用类也是在内存中重新开辟空间存储;
注意:SerializationUtils.clone()中的类要实现Serializable接口
3)使用fastjson进行序列化
Persion black= JSONObject.parseObject(JSONObject.toJSONString(red),Persion.class);
结果
小明 数学 -1611798689 48879495
小红 语文 -1571722113 67053619
其它json也都是深拷贝
使用序列化进行拷贝对象的注意事项
静态成员变量、被transient修饰的成员变量 在序列化时是不会被序列化的。