Java对象的复制——小明和他的克隆人的故事
2021-05-30 本文已影响0人
肥兔子爱豆畜子
Java对象的复制分为以下3种:
-
直接赋值
这个最简单了,Object a=b, 相当于变量a和b都只是指向同一个对象的引用,
所以不管操作a还是b对这俩对象中的任一个做了更改、比如修改了成员变量的值,那另一个也会变的,因为本质上一直是堆上的同一个对象。 -
浅拷贝
跟直接赋值不同,浅拷贝这个确实是创建了另一个不同的对象,对非静态的成员变量进行复制,但是只对基本类型的成员变量进行真正的复制,对引用类型的成员变量只是指向了同一个引用对象。 -
深拷贝
在浅拷贝的基础上,对引用类型的成员变量也做了真正的值的拷贝。深拷贝不仅复制对象的本身,也复制了包含的引用所指向的所有的对象。这可能是个递归的过程。
接下来用一个程序代码讲述一个故事,来加深一下深拷贝和浅拷贝的区别。而直接赋值太简单了,就不讲了。
故事是这样的,小明有个女朋友叫小美,小明有一天有了自己的克隆人,这个克隆人也叫小明,但是真正的小明显然是不想跟自己的克隆人共享女朋友的 -_- ,所以他试图将小丽介绍给克隆人当做女朋友,故事就是这样。。。
接下来看代码:
public class Boy implements Cloneable{
private String name;
private GirlFriend gf;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public GirlFriend getGf() {
return gf;
}
public void setGf(GirlFriend gf) {
this.gf = gf;
}
@Override
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
}
public class GirlFriend {
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class TestClone {
public static void main(String[] args) {
Boy xiaoming = new Boy();
xiaoming.setName("小明");
GirlFriend xiaomei = new GirlFriend();
xiaomei.setName("小美");
xiaoming.setGf(xiaomei);
Boy clone = (Boy) xiaoming.clone(); //小明的克隆人
clone.getGf().setName("小丽"); //克隆人的女朋友
System.out.println(xiaoming.getName() + "的女朋友是" + xiaoming.getGf().getName());
System.out.println(clone.getName() + "的克隆人的女朋友是" + clone.getGf().getName());
}
}
运行结果:
小明的女朋友是小丽
小明的克隆人的女朋友是小丽
乱了,全乱了。。。
以上,就是浅拷贝。clone对象赋值了小明的String name,因为名字是个基本类型String,但是女朋友gf是个引用类型GirlFriend,经过拷贝之后clone对象只是简单将自己的gf变量指向了同一个GirlFriend对象。
所以当试图给克隆人重新介绍女朋友的时候,clone.getGf().setName("小丽")也修改了原对象小明的女朋友。酿成了杯具。所以,我们需要深拷贝。
修改上面的代码:
public class GirlFriend implements Cloneable{
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public Object clone(){
try {
return super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
}
public class Boy implements Cloneable{
private String name;
private GirlFriend gf;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public GirlFriend getGf() {
return gf;
}
public void setGf(GirlFriend gf) {
this.gf = gf;
}
@Override
public Object clone() {
try {
GirlFriend gf_clone = (GirlFriend) gf.clone();
Boy clone = (Boy) super.clone();
clone.setGf(gf_clone); //女朋友也克隆一个
return clone;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
}
运行结果
小明的女朋友是小美
小明的克隆人的女朋友是小丽
正确了,大家都美滋滋。
总结
深拷贝和浅拷贝都是要实现Cloneable接口,然后Override重新Object clone()方法。
但是一个对象如果都只是调用父类也就是Object类的clone方法的话只是实现了浅拷贝,如果要实现深拷贝,方法是在clone方法内部除了调用super.clone()之外,对本对象的引用类型的成员再进行clone一次。就像上面的程序里对gf.clone()那样。
深拷贝的实现还有一种方法,就是对对象进行序列化,然后再反序列化回来,就可以得到一个深拷贝之后的全新的对象了。