开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 10 天,点击查看活动详情
觉得对你有益的小伙伴记得点个赞+关注
后续完整内容持续更新中
希望一起交流的欢迎发邮件至javalyhn@163.com
1. 先上案例
在解释深拷贝与浅拷贝前,先分别来一个浅拷贝与深拷贝的案例
1.1 浅拷贝案例
public class Thing implements Cloneable{
//定义一个私有变量
private ArrayList<String> array = new ArrayList<>();
//浅拷贝
@Override
protected Object clone() throws CloneNotSupportedException {
Thing thing = null;
try {
thing = (Thing)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return thing;
}
//设置ArrayList的值
public void setValue(String value) {
this.array.add(value);
}
//获取ArrayList的值
public ArrayList<String> getValue() {
return this.array;
}
}
在Thing类中增加一个私有变量array,通过setValue和getValue进行取值和赋值,下面定义CLient来看看浅拷贝如何操作
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
//产生一个对象
Thing thing = new Thing();
//设置一个值
thing.setValue("lyhn");
//拷贝一个对象
Thing clone = (Thing) thing.clone();
clone.setValue("zhangsan");
System.out.println(thing.getValue());
}
}
为什么我一个用的thing一个用的thing的克隆对象,但是最终结果不是lyhn,而是lyhn和zhangsan呢?
这就是浅拷贝,原始对象的array和其克隆对象array共用一个内存地址,仅仅实现了thing的克隆,而属性array并没有
1.2 深拷贝案例
我们仅需要增加一行代码,就可以实现
//深拷贝
@Override
protected Object clone() throws CloneNotSupportedException {
Thing thing = null;
try {
thing = (Thing)super.clone();
//ArrayList实现了Cloneable
this.array = (ArrayList<String>)this.array.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return thing;
}
2. 浅拷贝与深拷贝
2.1 浅拷贝
- 对于数据类型是
基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。 - 对于数据类型是
引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值。 - 前面的浅拷贝案例即可证明。
- 浅拷贝是使用默认的clone()方法来实现。
String 类型比较特殊,他不会产生有浅拷贝带来的问题。他是没有clone方法的,JAVA希望我们把他当做基本类型使用。而且处理机制很特殊,通过字符串池(String Pool)在需要的时候才在内存中创建新的字符串。
2.2 深拷贝
- 复制对象的
所有基本数据类型的成员变量值 - 为所有引用数据类型的成员变量
申请存储空间,并复制每个引用数据类型成员变量所引用的对象,直到该对象可达所有对象。也就是说,对象进行深拷贝要对整个对象进行拷贝 - 深拷贝实现方式1:重写clone方法来实现深拷贝深拷贝
- 实现方式2:通过对象序列化实现深拷贝(
推荐)
实现方式1已经实现过了,就是上面深拷贝的案例,下面演示通过对象序列化实现深拷贝。
public class DeepProtoType implements Serializable,Cloneable {
public String name;
public DeepCloneableTarget deepCloneableTarget;
public DeepProtoType() {
}
//深拷贝
//方式二
//通过对象序列化实现深拷贝 推荐
public Object deepClone() {
//创建流对象
ByteArrayOutputStream bos = null;
ObjectOutputStream oos = null;
ByteArrayInputStream bis = null;
ObjectInputStream ois = null;
try{
//序列化操作
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
oos.writeObject(this);//当前这个对象以对象流的方式输出
//反序列化
bis = new ByteArrayInputStream(bos.toByteArray());
ois = new ObjectInputStream(bis);
DeepProtoType deepProtoType = (DeepProtoType) ois.readObject();
return deepProtoType;
}catch (Exception e) {
e.printStackTrace();
return null;
}finally {
//关闭流
try {
bos.close();
oos.close();
bis.close();
ois.close();
}catch (Exception e) {
e.printStackTrace();
}
}
}
}
2.3 建议
深拷贝和浅拷贝不建议混合使用,特别是在涉及类的继承时,父类有多个引用的情况就十分复杂,建议分开来实现浅拷贝与深拷贝。
3. clone与final
对象的clone与final这两个关键字是有冲突的
下面看例子
相信大家一看就知道,array被设置为了final,那还怎么重新赋值,要实现深拷贝的梦想在final关键字的威胁下破灭了,因此要想实现深拷贝,final关键字不能加。