鄙人写文章喜欢简洁点,希望用尽可能短的语句描述一个知识点
1、概述
拷贝的一个经典的使用场景:当前对象要传给其他多个方法使用,如果该对象在某一个方法中被修改,那么这个修改会影响到其他方法。 如果要避免这种影响,就需要给每一个方法都传入一个当前对象的拷贝。
深与浅拷贝的区别就在于对复杂对象的处理:对于基本类型,浅拷贝、深拷贝都是拷贝的值;对于引用类型浅拷贝的是对象的引用。而深拷贝则是直接新建一个对象实例。
注意浅拷贝中的复杂引用以及简单引用:对于简单引用,拷贝后的对象指向新的地址不会影响到原对象。复杂引用,拷贝后的对象将内部的对象指向新的地址,会影响到原对象对应的内部对象。这里一定理解下,抓住“地址”指向去理解就好了。
2、代码实现
(1)浅拷贝实现方式:类实现Cloneable接口,并且重写clone()方法。详细见下面的代码
(2)深拷贝:主要是针对类内部的复杂引用变量。两种方式:1)复杂引用也实现Cloneable,并重写clone()方法,然后在父对象重写的clone方法中将该复杂引用指向克隆后的引用 2)直接将需要克隆的对象进行序列化,然后反序列化就可以得到一个深拷贝的对象。
当然这些工作都有现成的轮子了,借助于Apache Commons工具类可以直接实现:
浅克隆:BeanUtils.cloneBean(Object obj);
深克隆:SerializationUtils.clone(T object);
当然可以直接使用
2.1浅拷贝
父子类
1 @Data2 @Builder3 class Father implementsCloneable {4 Long age;5 StringBuilder name;6 Son son;7
8 @Override9 publicObject clone() {10 //浅拷贝
11 try{12 return super.clone();13 } catch(CloneNotSupportedException e) {14 e.printStackTrace();15 return null;16 }17 }18 }19
20 @Data21 @Builder22 classSon {23 Long age;24 String name;25 intgrade;26 }
初始赋值
1 public static Son son = Son.builder().age(new Long(30L)).name("大儿子").grade(100).build();2 public static Father father = Father.builder().age(new Long(50L)).name("爸爸").son(son).build();
浅拷贝示例:
private static voidshallowClone() {
System.out.println("父亲年龄:" +father.getAge());
System.out.println("父亲姓名:" +father.getName());
System.out.println("儿子年龄:" +father.getSon().getAge());
System.out.println("儿子姓名:" +father.getSon().getName());
System.out.println("儿子分数:" +father.getSon().getGrade());//开始克隆
Father fatherInLaw=(Father) father.clone();
fatherInLaw.getSon().setAge(10L);
fatherInLaw.getSon().setGrade(0);
fatherInLaw.getSon().setName("继子");
fatherInLaw.setAge(new Long(80L));
fatherInLaw.setName("继父");//修改后结果
System.out.println("==========浅拷贝后===========");
System.out.println("父亲年龄:" +father.getAge());
System.out.println("父亲姓名:" +father.getName());
System.out.println("儿子年龄:" +father.getSon().getAge());
System.out.println("儿子姓名:" +father.getSon().getName());
System.out.println("儿子分数:" +father.getSon().getGrade());
}
结果输出
父亲年龄:50父亲姓名:爸爸
儿子年龄:30儿子姓名:大儿子
儿子分数:100
==========浅拷贝后===========父亲年龄:50父亲姓名:爸爸
儿子年龄:10儿子姓名:继子
儿子分数:0
2.2.深拷贝
(1)子引用重写clone方法,并且在父类中改变子引用的指向
父子类
@Data
@Builderclass Father implementsCloneable {
Long age;
StringBuilder name;
Son son;
@OverridepublicObject clone() {
Father father= null;//浅拷贝
try{
father= (Father) super.clone();
father.son=(Son) son.clone();
}catch(CloneNotSupportedException e) {
e.printStackTrace();return null;
}returnfather;
}
}
@Data
@Builderclass Son implementsCloneable {
Long age;
String name;intgrade;
@OverridepublicObject clone() {//拷贝
try{return super.clone();
}catch(CloneNotSupportedException e) {
e.printStackTrace();return null;
}
}
}
View Code
赋初值
1 public static Son son = Son.builder().age(new Long(30L)).name("大儿子").grade(100).build();2 public static Father father = Father.builder().age(new Long(50)).name(new StringBuilder("爸爸")).son(son).build();
深拷贝示例:
//深拷贝
private static voiddeepClone() {
System.out.println("父亲年龄:" +father.getAge());
System.out.println("父亲姓名:" +father.getName());
System.out.println("儿子年龄:" +father.getSon().getAge());
System.out.println("儿子姓名:" +father.getSon().getName());
System.out.println("儿子分数:" +father.getSon().getGrade());//开始克隆
Father fatherInLaw=(Father) father.clone();
fatherInLaw.getSon().setAge(10L);
fatherInLaw.getSon().setGrade(0);
fatherInLaw.getSon().setName("继子");
fatherInLaw.setAge(new Long(80L));
fatherInLaw.setName(new StringBuilder("继父"));//修改前
System.out.println("==========浅拷贝后===========");
System.out.println("父亲年龄:" +father.getAge());
System.out.println("父亲姓名:" +father.getName());
System.out.println("儿子年龄:" +father.getSon().getAge());
System.out.println("儿子姓名:" +father.getSon().getName());
System.out.println("儿子分数:" +father.getSon().getGrade());
}
结果
父亲年龄:50父亲姓名:爸爸
儿子年龄:30儿子姓名:大儿子
儿子分数:100
==========浅拷贝后===========父亲年龄:50父亲姓名:爸爸
儿子年龄:30儿子姓名:大儿子
儿子分数:100
(2)序列化方式
父子类
@Data
@Builderclass Father implementsSerializable {
Long age;
StringBuilder name;
Son son;public Object deepClone() throwsIOException, ClassNotFoundException {//序列化
ByteArrayOutputStream bos = newByteArrayOutputStream();
ObjectOutputStream oos= newObjectOutputStream(bos);
oos.writeObject(this);//反序列化
ByteArrayInputStream bis = newByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois= newObjectInputStream(bis);returnois.readObject();
}
}
@Data
@Builderclass Son implementsSerializable {
Long age;
String name;intgrade;
}
View Code
实现方法
//深拷贝
private static void deepCloneV2() throwsIOException, ClassNotFoundException {
System.out.println("父亲年龄:" +father.getAge());
System.out.println("父亲姓名:" +father.getName());
System.out.println("儿子年龄:" +father.getSon().getAge());
System.out.println("儿子姓名:" +father.getSon().getName());
System.out.println("儿子分数:" +father.getSon().getGrade());//开始克隆
Father fatherInLaw=(Father) father.deepClone();
fatherInLaw.getSon().setAge(10L);
fatherInLaw.getSon().setGrade(0);
fatherInLaw.getSon().setName("继子");
fatherInLaw.setAge(new Long(80L));
fatherInLaw.setName(new StringBuilder("继父"));//修改前
System.out.println("==========浅拷贝后===========");
System.out.println("父亲年龄:" +father.getAge());
System.out.println("父亲姓名:" +father.getName());
System.out.println("儿子年龄:" +father.getSon().getAge());
System.out.println("儿子姓名:" +father.getSon().getName());
System.out.println("儿子分数:" +father.getSon().getGrade());
}
结果:
父亲年龄:50父亲姓名:爸爸
儿子年龄:30儿子姓名:大儿子
儿子分数:100
==========浅拷贝后===========父亲年龄:50父亲姓名:爸爸
儿子年龄:30儿子姓名:大儿子
儿子分数:100
3、使用Apach Commons
3.1实现深拷贝,需要实现序列化接口,SerializationUtils.clone(T object);
父子类
@Data
@Builderclass Father implementsSerializable{
Long age;
StringBuilder name;
Son son;
}
@Data
@Builderclass Son implementsSerializable{
Long age;
String name;intgrade;
}
View Code
实现方法
//深拷贝
private static void deepCloneCommons() throwsIOException, ClassNotFoundException {
System.out.println("父亲年龄:" +father.getAge());
System.out.println("父亲姓名:" +father.getName());
System.out.println("儿子年龄:" +father.getSon().getAge());
System.out.println("儿子姓名:" +father.getSon().getName());
System.out.println("儿子分数:" +father.getSon().getGrade());//开始克隆
Father fatherInLaw=(Father) SerializationUtils.clone(father);
fatherInLaw.getSon().setAge(10L);
fatherInLaw.getSon().setGrade(0);
fatherInLaw.getSon().setName("继子");
fatherInLaw.setAge(new Long(80L));
fatherInLaw.setName(new StringBuilder("继父"));//修改前
System.out.println("==========浅拷贝后===========");
System.out.println("父亲年龄:" +father.getAge());
System.out.println("父亲姓名:" +father.getName());
System.out.println("儿子年龄:" +father.getSon().getAge());
System.out.println("儿子姓名:" +father.getSon().getName());
System.out.println("儿子分数:" +father.getSon().getGrade());
}
View Code
结果
父亲年龄:50父亲姓名:爸爸
儿子年龄:30儿子姓名:大儿子
儿子分数:100
==========深拷贝后===========父亲年龄:50父亲姓名:爸爸
儿子年龄:30儿子姓名:大儿子
儿子分数:100
3.2使用工具类实现浅拷贝
BeanUtils.copyProperties(Object dest, Object orig)直接调用即可实现浅拷贝,BeanUtils.cloneBean(Object obj)最终也是调用的copyProperties方法。
今日学习笔记到此结束。
书于:2019/08/01/ 22:40分