引入
Java的Object类中有个clone()方法,我们都知道想要克隆一个对象,只需要实现Cloneable这个标记接口并重写clone()方法即可,但是令人烦恼的是,这个Object类中的clone()方法只能实现浅克隆,即只能将基本类型的值进行复制和引用类型的地址进行复制,没办法将引用类型指向的真正对象进行复制。,这样就导致原来对象和克隆出来的对象共享引用对象,假如一个对象里修改了引用对象,另外一个对象也将会受到影响。
而我们希望的是引用对象也被复制一份,就不用共享了
分析
既然知道了浅克隆的原理,那么实现深克隆就迎刃而解了,我们只需要将引用对象也克隆下来就行了,那么Java中有哪些引用类型呢?处了8大基本类型外,String,类,数组都是引用类型。
因此在需要实现深克隆的地方要注意要克隆的对象的属性里面是否存在这些引用类型。
实现方式1
前面说了可以重写Object类中的clone()方法能实现浅克隆,但其实也能实现深克隆,只是要在clone()方法的重写中加入自己的实现逻辑。
浅克隆
Room类:
public class Room implements Cloneable{
Desk desk;
@Override
protected Room clone() throws CloneNotSupportedException {
return (Room) super.clone();//直接调用Object中的clone()
}
//隐藏了构造方法和getter setter
}
Desk类:
public class Desk {
String shape;
public Desk(String shape) {
this.shape = shape;
}
}
调用clone()方法:
public static void main(String[] args) throws CloneNotSupportedException {
Room room = new Room(new Desk("圆桌"));
Room cloneRoom = room.clone();
System.out.println(room == cloneRoom);
System.out.println(room.desk == cloneRoom.desk);
}
结果:说明desk是共享的
深克隆
既然直接调用Object类中的clone没法实现深克隆,那么就需要加入自己的实现逻辑了,有两种方法:
方式1
修改Room类的clone方法
@Override
protected Room clone() throws CloneNotSupportedException {
Room cloneRoom = (Room) super.clone();
Desk desk = new Desk();
desk.setShape(cloneRoom.desk.getShape());
cloneRoom.setDesk(desk);
return cloneRoom;
}
结果:
表面上看这种实现没有问题,因为确实实现了深克隆,但是细想一下,克隆的时候Desk对象是我们new出来的,而且desk对象的属性也是我们从原来的desk对象中获取然后通过set方法进行注入的,加入desk对象中有十个、一百个属性那我们是不是要用一大堆代码去获取注入呢?这显然是不符合实际情况的。因此来看第二种方式。
方式2
透过方式1我们可以发现,其实我们实现深克隆的目的就是将对象的引用类型的属性复制一份出来然后再替换原来的属性。既然是复制,那不就又回到了克隆问题上了吗?不就就一层一层的套娃吗?
Desk类:
public class Desk implements Cloneable {
String shape;
public Desk(String shape) {
this.shape = shape;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
修改Room中的clone方法:
@Override
protected Room clone() throws CloneNotSupportedException {
Room cloneRoom = (Room) super.clone();
Desk desk = (Desk) this.desk.clone();//克隆一个desk
cloneRoom.setDesk(desk);
return cloneRoom;
}
结果:
问题:以上案例都是类类型的引用对象,那数组类型的引用对象该怎么克隆呢?
目前在Java中数据拷贝提供了如下方式:
- clone()
- System.arraycopy()
- Arrays.copyOf()
- Arrays.copyOfRange()
实现方式2
第一种方式是通过重写Object类中的clone()方法实现的,接下来使用对象序列化来实现克隆。
**思路:**将对象序列化,然后马上反序列化回来
Room类:
public class Room implements Serializable {
Desk desk;
public Room deepClone() throws Exception{
//序列化
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
objectOutputStream.writeObject(this);
//反序列化
ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
Room roomClone = (Room)objectInputStream.readObject();
return roomClone;
}
//忽略构造方法
}
Desk类:
public class Desk implements Serializable {
String shape;
}
结果:
注意:使用序列化与反序列化时必须实现标记接口Serializable