1、浅克隆
1.1、什么是浅克隆?
被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然
指向原来的对象(克隆对象与原型对象共享引用数据类型变量)。
如下图所示:
1.2、浅克隆代码实现
类实现接口 Cloneable,表示该类的对象是可以克隆的
示例代码1:
public class ConcretePrototype implements Cloneable {
public ConcretePrototype() {
System.out.println("具体的对象创建完成!");
}
@Override
protected ConcretePrototype clone() throws CloneNotSupportedException {
System.out.println("具体的对象复制成功!");
return (ConcretePrototype)super.clone();
}
}
测试
@Test
public void test01() throws CloneNotSupportedException {
ConcretePrototype c1 = new ConcretePrototype();
ConcretePrototype c2 = c1.clone();
System.out.println("对象c1和c2是同一个对象?" + (c1 == c2));
}
打印结果:
对象c1和c2是同一个对象?true
示例代码2:
在 类 ConcretePrototype 中添加一个引用类型的属性Person,如下所示:
public class ConcretePrototype implements Cloneable {
private Person person;
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
void show(){
System.out.println("嫌疑人姓名: " +person.getName());
}
public ConcretePrototype() {
System.out.println("具体的对象创建完成!");
}
@Override
protected ConcretePrototype clone() throws CloneNotSupportedException {
System.out.println("具体的对象复制成功!");
return (ConcretePrototype)super.clone();
}
}
public class Person {
private String name;
public Person() {
}
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
测试:
@Test
public void test02() throws CloneNotSupportedException {
ConcretePrototype c1 = new ConcretePrototype();
Person p1 = new Person();
c1.setPerson(p1);
//复制c1
ConcretePrototype c2 = c1.clone();
//获取复制对象c2中的Person对象
Person p2 = c2.getPerson();
p2.setName("慕容复");
//判断p1与p2是否是同一对象
System.out.println("p1和p2是同一个对象?" + (p1 == p2));
c1.show();
c2.show();
}
打印结果:
p1和p2是同一个对象?true
嫌疑人姓名:慕容复
嫌疑人姓名:慕容复
p1与p2是同一对象,这是浅克隆的效果,也就是对具体原型类中的引用数据类型的属性进行
引用的复制
2、深克隆
2.1、什么是深克隆
除去那些引用其他对象的变量,被复制对象的所有变量都含有与原来的对象相同的值。那些
引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言
之,深复制把要复制的对象所引用的对象都复制了一遍。如下图所示:
2.2、深克隆代码实现
对于上边浅克隆示例2中的代码,如果有需求场景中不允许共享同一对象,那么就需要使用
“深拷贝”,如果想要进行深拷贝需要使用到对象序列化流 (对象序列化之后,再进行反序列化
获取到的是不同对象)。代码如下
@Test
public void test03() throws Exception {
ConcretePrototype c1 = new ConcretePrototype();
Person p1 = new Person("峰哥");
c1.setPerson(p1);
//创建对象序列化输出流
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("c.txt"));
//将c1对象写到文件中
oos.writeObject(c1);
oos.close();
//创建对象序列化输入流
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("c.txt"));
//读取对象
ConcretePrototype c2 = (ConcretePrototype) ois.readObject();
Person p2 = c2.getPerson();
p2.setName("凡哥");
//判断p1与p2是否是同一个对象
System.out.println("p1和p2是同一个对象?" + (p1 == p2));
c1.show();
c2.show();
}
3、总结
1)Java中的Object类中提供了 `clone()` 方法来实现“浅克隆”。需要注意的是要想实现克隆
的Java类必须实现一个标识接口 Cloneable ,来表示这个Java类支持被复制。
2)其实现在不推荐大家用Cloneable接口,实现比较麻烦,现在借助Apache Commons或
者springframework可以直接实现:
(1)浅克隆:BeanUtils.cloneBean(Object obj);
BeanUtils.copyProperties(S,T);
(2)深克隆:SerializationUtils.clone(T object);
BeanUtils是利用反射原理获得所有类可见的属性和方法,然后复制到target类。
SerializationUtils.clone()就是使用我们的前面讲的序列化实现深克隆,当然你要把要克隆
的类实现Serialization接口。