鄙人写文章喜欢简洁点,希望用尽可能短的语句描述一个知识点
1、概述
拷贝的一个经典的使用场景:当前对象要传给其他多个方法使用,如果该对象在某一个方法中被修改,那么这个修改会影响到其他方法。 如果要避免这种影响,就需要给每一个方法都传入一个当前对象的拷贝。
深与浅拷贝的区别就在于对复杂对象的处理:对于基本类型,浅拷贝、深拷贝都是拷贝的值;对于引用类型浅拷贝的是对象的引用。而深拷贝则是直接新建一个对象实例。
注意浅拷贝中的复杂引用以及简单引用:对于简单引用,拷贝后的对象指向新的地址不会影响到原对象。复杂引用,拷贝后的对象将内部的对象指向新的地址,会影响到原对象对应的内部对象。这里一定理解下,抓住“地址”指向去理解就好了。
2、代码实现
(1)浅拷贝实现方式:类实现Cloneable接口,并且重写clone()方法。详细见下面的代码
(2)深拷贝:主要是针对类内部的复杂引用变量。两种方式:1)复杂引用也实现Cloneable,并重写clone()方法,然后在父对象重写的clone方法中将该复杂引用指向克隆后的引用 2)直接将需要克隆的对象进行序列化,然后反序列化就可以得到一个深拷贝的对象。
当然这些工作都有现成的轮子了,借助于Apache Commons工具类可以直接实现:
- 浅克隆:BeanUtils.cloneBean(Object obj);
- 深克隆:SerializationUtils.clone(T object);
当然可以直接使用
2.1浅拷贝
父子类
1 @Data
2 @Builder
3 class Father implements Cloneable {
4 Long age;
5 StringBuilder name;
6 Son son;
7
8 @Override
9 public Object clone() {
10 //浅拷贝
11 try {
12 return super.clone();
13 } catch (CloneNotSupportedException e) {
14 e.printStackTrace();
15 return null;
16 }
17 }
18 }
19
20 @Data
21 @Builder
22 class Son {
23 Long age;
24 String name;
25 int grade;
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 void shallowClone() {
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
@Builder
class Father implements Cloneable {
Long age;
StringBuilder name;
Son son;
@Override
public Object clone() {
Father father = null;
//浅拷贝
try {
father = (Father) super.clone();
father.son = (Son) son.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
return father;
}
}
@Data
@Builder
class Son implements Cloneable {
Long age;
String name;
int grade;
@Override
public Object 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 void deepClone() {
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
@Builder
class Father implements Serializable {
Long age;
StringBuilder name;
Son son;
public Object deepClone() throws IOException, ClassNotFoundException {
//序列化
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
//反序列化
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return ois.readObject();
}
}
@Data
@Builder
class Son implements Serializable {
Long age;
String name;
int grade;
}
View Code
实现方法
//深拷贝
private static void deepCloneV2() throws IOException, 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
@Builder
class Father implements Serializable{
Long age;
StringBuilder name;
Son son;
}
@Data
@Builder
class Son implements Serializable{
Long age;
String name;
int grade;
}
View Code
实现方法
//深拷贝
private static void deepCloneCommons() throws IOException, 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分