Java 浅拷贝和深拷贝
浅拷贝和深拷贝
浅拷贝:直接复制了对象A地址给另一个对象B。由于对象A,B指向的是同一个地址因此修改B,A也会跟着改变。
深拷贝:复制了对象A的值给另一个对象B,这两个对象没有任何关系指向不同的地址。修改其中一个并不会对另一个造成影响。
浅拷贝
先把要用到的两个类放上来,一个狗类,一个毛类。get()、set()
什么的就不放了。
class Dog{
private String name;
private int age;
private String color;
private Mao mao;
}
class Mao {
private String changduan;
private String color;
}
直接赋值
public void assign() {
Dog d1 = new Dog("d1", 1, "green");
Dog d2 = d1;
d2.setName("d2");
// 可以看到修改了d2后,d1也改变了
System.out.println("d1 name:" + d1.getName());
System.out.println("d2 name:" + d2.getName());
}
输出:
d1 name:d2
d2 name:d2
可以看到修改了d2的name后,d1的name也发生了改变
重写clone()
clone()
其实可以说是半深半浅。下面细🔒
可以通过重写Object
类的clone()
方法来进行拷贝。
clone的深拷贝方面
首先要重写clone()
方法需要实现Cloneable
接口
class Dog implements Cloneable{
private String name;
private int age;
private String color;
private Mao mao;
/**
* 重写clone方法
*/
@Override
public Dog clone() {
try {
return (Dog)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
}
然后创建一个一岁长毛狗d1,并使用clone()
方法将d1拷贝给d3
public void clone_() {
Dog d1 = new Dog("d1", 1, "green");
Mao mao =new Mao("long","black");
d1.setMao(mao);
Dog d3 = d1.clone();
d3.setAge(2);
System.out.println("d1:的年龄" + d1.getAge());
System.out.println("d3:的年龄" + d3.getAge());
}
输出:
d1:的年龄1
d3:的年龄2
我们发现修改了d3的age后并没有影响到d1。这就是深拷贝。
clone的浅拷贝方面
还是之前那两个对象,只不过这次我们修改其中的Mao(毛) 属性。
public void clone_() {
Dog d1 = new Dog("d1", 1, "green");
Mao mao =new Mao("long","black");
d1.setMao(mao);
Dog d3 = d1.clone();
d3.setAge(2);
mao.setChangduan("short");
mao.setColor("red");
System.out.println("d1的Mao属性" + d1.getMao()+"d1:的年龄" + d1.getAge());
System.out.println("d3的Mao属性" + d3.getMao()+"d3:的年龄" + d3.getAge());
}
输出:
d1的Mao属性Mao [changduan=short, color=red]d1:的年龄1
d3的Mao属性Mao [changduan=short, color=red]d3:的年龄2
我们发现修改了修改d3的age
年龄属性不会对d1造成影响,但是修改d3的Mao属性就会把d1的Mao属性也改变了。这就是clone()
的浅拷贝!
为啥又有深又有浅
如下图所示,虽然重写了clone()
方法,拷贝出来的对象指向了不同的地址。但是d1和d3的Mao属性并没被拷贝出来。也就是说d1和d3的属性Mao指向的是同一个地址,所以修改Mao的值,就会同时影响到d1和d3。
总结
可以使用clone()
直接深拷贝的:
- 本数据类型如int、char等
- 基本数据类型的包装类Integer等
- String
不可以简单的使用clone()
直接深拷贝的:
引用类型的数据如我上面自己定义的Mao类,数组等。
但是重写clone()的时候处理下还是可以深拷贝的
深拷贝
重写clone()
没错又是重写clone()
,只要我们对对象中的引用类型进行处理,就可以使用clone
进行深拷贝。就是让这个引用类型也重写clone
(套娃!)
首先让Mao类也重写clone()
,并修改Dog类的clone()
逻辑,把拷贝后的Mao赋值给拷贝后的Dog
class Mao implements Cloneable{
private String changduan;
private String color;
}
@Override
public Mao clone() {
try {
return (Mao)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
class Dog implements Cloneable{
private String name;
private int age;
private String color;
private Mao mao;
/**
* 重写clone方法
*/
@Override
public Dog clone() {
try {
Dog res=(Dog)super.clone();
res.mao=mao.clone();
return res;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
然后使用clone()
进行拷贝
public void deepClone() {
Mao mao = new Mao("long", "black");
d1.setMao(mao);
Dog d3 = d1.clone();
d3.setName("d3");
mao.setChangduan("short");
mao.setColor("red");
System.out.println("d1:" + d1);
System.out.println("d3:" + d3);
}
d1:Dog [age=1, color=green, mao=Mao [changduan=short, color=red], name=d1]
d3:Dog [age=1, color=green, mao=Mao [changduan=long, color=black], name=d3]
但是如果Mao类里面又有一个引用类型的属性,那就又要继续重写clone()
无限套娃很麻烦。
完美实现深拷贝
new一个新对象
我们可以通过new一个新的对象,然后把老对象的值复制给新对象来做到深拷贝(对象较复杂的时候很麻烦)
public void newObj() {
Dog d2 = new Dog(d1.getName(), d1.getAge(), d1.getColor());
d2.setAge(88);
System.out.println("d1:" + d1);
System.out.println("d2:" + d2);
}
老简单了,不多说了
使用序列化和反序列化进行深拷贝
序列化可以把java类的所有数据及其数据类型都输出到IO中,然后反序列化就是从IO中读取这些数据重新生成对象。由于是直接读取值然后新生成一个对象,所以就是深拷贝!
public void serDeepCopy() throws IOException, ClassNotFoundException {
Mao mao=new Mao("short", "red");
d1.setMao(mao);
//先把序列化数据放到内存操作流buf中
ByteArrayOutputStream buf = new ByteArrayOutputStream();
ObjectOutputStream obs = new ObjectOutputStream(buf);
obs.writeObject(d1);
obs.close();
//从buf流中读取数据 并反序列化
ByteArrayInputStream ios = new ByteArrayInputStream(buf.toByteArray());
ObjectInputStream ois=new ObjectInputStream(ios);
Dog d4=(Dog)ois.readObject();
mao.setColor("black");
d4.setName("d4");
System.out.println("d1:" + d1);
System.out.println("d4:" + d4);
}
d1:Dog [age=1, color=green, mao=Mao [changduan=short, color=black], name=d1]
d4:Dog [age=1, color=green, mao=Mao [changduan=short, color=red], name=d4]