克隆方法的由来
问题一:什么是克隆(clone)方法
答: 创建并返回此对象的一个副本——按照原对象,创建一个新的对象(复制原对象的内容)。
问题二:已经存在new关键字和反射技术都可以创建对象,为什么还需要一个Object的clone方法呢?
答:必然是new关键字和反射技术,存在一些弊端。
new关键字和反射创建对象的弊端:
通过new和反射可以创建内容一模一样的对象。但是,创建对象之后,通过setter方法,完成设置(不一样的内容),如果需要创建更多的内容一致的对象,那么setter方法调用就不断在重复。
使用clone方法创建对象
Object的clone方法使用步骤:
- 在需要调用clone方法的对象上添加实现Cloneable接口;
- 复写clone方法,在自己的clone方法中调用父类的clone方法,将返回值类型强转成本类类型,将当前clone方法修饰符改成public;
- 调用对象的clone方法;
代码演示:
public class Person implements Cloneable{
private String name;
private int age;
//....省略get/set/toString方法
@Override
public Person clone() throws CloneNotSupportedException {
return (Person) super.clone();
}
}
public class Test {
public static void main(String[] args) throws Exception {
Person p1 = new Person();
p1.setName("张三");
p1.setAge(18);
Person p2 = p1.clone();
System.out.println(p1+":"+p1.hashCode());
System.out.println(p2+":"+p2.hashCode());
}
}
//控制台打印结果:
Person{name='张三', age=18}:1908153060
Person{name='张三', age=18}:116211441
通过使用clone方法,我们发现大大的减少了创建重复对象代码。这也就是clone方法存在的意义。
克隆出来的对象和原来的对象有什么关系:
通过上面的测试,我们已经知道了,克隆出来的对象内容一致,但是对象哈希值不同,所以是不同对象。
那么两个对象的内容之间有什么关联呢——两个对象的内容是彼此独立,还是,两个对象底层使用的同一个内容呢?
代码演示:
public class Person implements Cloneable{
private String name;
private Integer age;
private Children child;
//....省略get/set/toString方法
@Override
public Person clone() throws CloneNotSupportedException {
return (Person) super.clone();
}
}
public class Children {
private String name;
private Integer age;
//....省略get/set/toString方法
}
public class Test {
public static void main(String[] args) throws Exception {
Person p1 = new Person();
p1.setName("张三");
p1.setAge(28);
Children children1 = new Children();
children1.setName("张伟");
children1.setAge(5);
p1.setChild(children1);
Person p2 = p1.clone();
System.out.println(p1+":对象的哈希值:"+p1.hashCode()+":child成员变量的哈希值:"+p1.getChild().hashCode());
System.out.println(p2+":对象的哈希值:"+p2.hashCode()+":child成员变量的哈希值:"+p2.getChild().hashCode());
}
}
//控制台打印结果
Person{name='张三', age=28, child=Children{name='张伟', age=5}}:对象的哈希值:116211441:child成员变量的哈希值:607635164
Person{name='张三', age=28, child=Children{name='张伟', age=5}}:对象的哈希值:529116035:child成员变量的哈希值:607635164
结论:通过测试发现克隆出来的对象虽然不一致,但是底层的成员变量的哈希值是一致的。
这种复制我们称之为:浅表复制。
浅表复制的内存结构:
浅表复制的弊端:
由于浅表复制导致克隆的对象中成员变量的底层哈希值一致,如果我们操作其中一个对象的成员变量内容,就会导致,所有的克隆对象的成员内容发送改变。
结论:clone方法默认的复制操作是浅表复制,浅表复制存在弊端——仅仅创建新的对象,对象的成员内容底层哈希值是一致的,因此,不管是原对象还是克隆对象,只有其中一个修改了成员的数据,就会影响所有的原对象和克隆对象。
要解决浅表复制的问题:进行深层的复制。
深层复制
目的:不仅在执行克隆的时候,克隆对象是一个新对象,而且,克隆对象中的成员变量,也要求是一个新的对象
开发步骤:
- 修改children类实现Cloneable接口;
- 修改children类重写clone方法;
- 修改Person类重写clone方法,在clone方法中调用children的clone方法;
代码演示:
public class Children implements Cloneable {
private String name;
private Integer age;
//....省略get/set/toString方法
@Override
public Children clone() throws CloneNotSupportedException {
return (Children) super.clone();
}
}
public class Person implements Cloneable{
private String name;
private Integer age;
private Children child;
//....省略get/set/toString方法
@Override
public Person clone() throws CloneNotSupportedException {
Person clone = (Person) super.clone();
clone.setChild(child.clone());
return clone;
}
}
public class Test {
public static void main(String[] args) throws Exception {
Person p1 = new Person();
p1.setName("张三");
p1.setAge(28);
Children children1 = new Children();
children1.setName("张伟");
children1.setAge(5);
p1.setChild(children1);
Person p2 = p1.clone();
System.out.println(p1.getChild());
System.out.println(p2.getChild());
children1.setName("张三丰");
System.out.println(p1.getChild());
System.out.println(p2.getChild());
Children children2 = p2.getChild();
children2.setName("张无忌");
System.out.println(p1.getChild());
System.out.println(p2.getChild());
System.out.println(p1.getChild().hashCode());
System.out.println(p2.getChild().hashCode());
}
}
//控制台打印结果
Children{name='张伟', age=5}
Children{name='张伟', age=5}
Children{name='张三丰', age=5}
Children{name='张伟', age=5}
Children{name='张三丰', age=5}
Children{name='张无忌', age=5}
116211441
607635164
可以看到,深层复制成员变量如果是对象,那么也会创建新的对象。
使用clone接口实现深层复制的弊端:
虽然深度复制成员变量对象也是创建新的对象,但是修改类中成员变量对应的源码,如果成员变量特别多,那么就需要修改多个类的源码;
比如:前面的Person类中现在又新增了Son、Sister、Mother啥全家福的,那对应需要修改的类就特别多了;
深层复制的几种方法
以上为学习所做笔记,来源为黑马程序员Object课程!