Java编程思想
对象克隆是指创建已有对象的一个拷贝,如果想要修改一个对象,但同时不想改变调用者的对象,那么克隆会是很好的解决方式。
在Java中,实现对象的克隆只需要覆盖Object提供的clone()方法,并将方法访问级别改为public
,同时要注意对象所属类必须实现Cloneable
接口,否则在调用clone()方法时会抛出CloneNotSupportedException
异常。
1 clone()方法的使用
class Test implements Cloneable{
public int ii;
public String ss;
public Test2 test2;
public Test() {
super();
}
public String format() {
return String.format("ii = %s, ss = %s, test2 = %s", ii, ss, test2);
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class Test2 implements Cloneable{
public int ii2;
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class CloneTest {
public static void main(String[] args) throws Exception {
Test t = new Test();
t.ii = 1;
t.ss = "1";
t.test2 = new Test2();
Test t2 = (Test) t.clone();
System.out.println(t);
System.out.println(t2);
System.out.println(t.format());
System.out.println(t2.format());
t2.ii = 2;
t2.ss = "2";
System.out.println(t.format());
System.out.println(t2.format());
}
}
输出:
gdou.laiminghai.Test@2a139a55
gdou.laiminghai.Test@15db9742
ii = 1, ss = 1, test2 = gdou.laiminghai.Test2@7d4991ad
ii = 1, ss = 1, test2 = gdou.laiminghai.Test2@7d4991ad
ii = 1, ss = 1, test2 = gdou.laiminghai.Test2@7d4991ad
ii = 2, ss = 2, test2 = gdou.laiminghai.Test2@7d4991ad
可以看到,通过覆盖clone()方法,在clone()方法中通过super.clone()
的调用,我们可以获得对象的拷贝,两个对象引用指向不同的内存区域,同时拥有了相同的属性值。当我们对拷贝对象作出修改时,并不会影响原有的对象。
2 浅拷贝和深拷贝
从上面的例子我们可以看到,clone()方法的使用的确返回对象的一个拷贝,但如果对象中的属性是另一个引用类型的话,那么clone()方法的简单调用并不会把该引用指向的对象再作一次拷贝,同时把新对象属性里的引用指向这个拷贝,而只是把引用(内存地址)作一次拷贝,结果是两个属性引用指向的仍然是同一个对象。这种只拷贝了原始数据类型的拷贝,我们就称作浅拷贝
。(这里我们可能发现了,String类型是引用类型,为什么拷贝对象中String类型变量改变了,原有对象却没改变,其实主要是因为String的不可变性,拷贝对象改变的只是引用而引用指向的内容)
有浅拷贝
就有深拷贝
,深拷贝会拷贝所有的原始数据属性,同时也拷贝引用属性指向的动态分配的内存。那么要如何实现深拷贝呢?
@Override
public Object clone() throws CloneNotSupportedException {
Test t = (Test)super.clone();
t.test2 = (Test2) test2.clone();
return t;
}
方法就是在覆盖clone()方法时,对对象中引用变量也明确调用一次clone()方法进行一次克隆。
修改后输出:
gdou.laiminghai.Test@2a139a55
gdou.laiminghai.Test@15db9742
ii = 1, ss = 1, test2 = gdou.laiminghai.Test2@7d4991ad
ii = 1, ss = 1, test2 = gdou.laiminghai.Test2@28d93b30
ii = 1, ss = 1, test2 = gdou.laiminghai.Test2@7d4991ad
ii = 2, ss = 2, test2 = gdou.laiminghai.Test2@28d93b30
3 Object的clone()方法做了什么
我们发现,在覆盖clone()方法的时候,我们有一句super.clone()
是必有的,通过super.clone()方法传递调用,我们知道,最后一定会调用到Object的clone()方法,那么,Object.clone()方法会做什么呢?
Object.clone()会检查原先的对象有多大(通过运行时类型信息RTTI判断),再为新对象腾出足够多的内存,并通过“按位复制”将所有二进制位从原来的对象复制到新对象的存储空间。