1、什么是"克隆"?
在开发过程中,我们有一个对象A,其中存储了一些信息,这时我们需要创建一个新对象B,要求B的初始化信息和
A中的信息保持一致,但对B对象随后的信息更改不会影响B的信息。说简单点,对象B的初始化信息依赖于B,但是
A和B是两个完全独立的对象。
2、影子克隆(shallow clone)
这里我们以实验的方式讲解影子克隆的效果.
首先我们创建两个类CloneA、CloneB
public class CloneA {
private int i;
public CloneA(int i) {
super();
this.i = i;
}
public void add() {
i++;
}
public int getI() {
return i;
}
public void setI(int i) {
this.i = i;
}
@Override
public String toString() {
return "CloneA [i=" + i + "]";
}
}
/////////////////////////////////////////////////////////////////////////////
public class CloneB implements Cloneable{
private int v;
private CloneA cloneA = new CloneA(88);
public int getV() {
return v;
}
public void setV(int v) {
this.v = v;
}
public CloneA getCloneA() {
return cloneA;
}
public void setCloneA(CloneA cloneA) {
this.cloneA = cloneA;
}
@Override
protected Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
@Override
public String toString() {
return "CloneB [v=" + v + ", cloneA=" + cloneA + "]";
}
备注:尽管java中实现克隆的方式很多,譬如直接new一个对象,然后
设置原始对象的属性信息就OK了,但是实现Cloneable接口,实现
Clone方法是最高效的。
然后,进行如下测试:
public class main {
public static void main(String[] args) {
CloneB b = new CloneB();
b.setV(123);
/*************克隆对象,改变Clone后的对象属性***************/
CloneB b1 = (CloneB) b.clone();
b1.setV(456);
b1.getCloneA().add();
System.out.println("b:"+b.toString());
System.out.println("==============After Clone==============");
System.out.println("b1:"+b1.toString());
}
测试结果:
b:CloneB [v=123, cloneA=CloneA [i=89]]
==============After Clone==============
b1:CloneB [v=456, cloneA=CloneA [i=89]]
查看结果表明,整型(基本数据类型)已经被克隆了(z),但是对象CloneA仍然指向同一对象(不同引用而已)。
由此我们可以得出对于基本数据类型,调用Object中的Clone方法产生的效果:在内存中开辟一块内存区域,
然后用原始对象的内容初始化该内存区,但是对象内容并未实现真正的克隆,仅是增加了对同一个对象的
引用。这种就是"影子克隆"。
3、深度克隆
所谓深度克隆,实际上是对一个对象的所有可序列化(非静态、没有使用transient修饰)的所有类型域进行
克隆。
在上边例子的基础上,我们稍作修改,进行对象的深度克隆演示。
1)将类CloneA实现Cloneable接口,实现clone方法。
2)在类CloneB中的clone方法中,增加对象类型的域的clone(红色部分为修改)
public class CloneA implements Cloneable{
private int i;
public CloneA(int i) {
super();
this.i = i;
}
public void add() {
i++;
}
public int getI() {
return i;
}
public void setI(int i) {
this.i = i;
}
@Override
protected Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
@Override
public String toString() {
return "CloneA [i=" + i + "]";
}
}
///////////////////////////////////////////////////////////////////////////////////////////
public class CloneB implements Cloneable{
private int v;
private CloneA cloneA = new CloneA(88);
public int getV() {
return v;
}
public void setV(int v) {
this.v = v;
}
public CloneA getCloneA() {
return cloneA;
}
public void setCloneA(CloneA cloneA) {
this.cloneA = cloneA;
}
@Override
protected Object clone() {
CloneB o = null;
try {
o = (CloneB) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
o.cloneA = (CloneA) cloneA.clone();
return o;
}
@Override
public String toString() {
return "CloneB [v=" + v + ", cloneA=" + cloneA + "]";
}
}
执行测试:
b:CloneB [v=123, cloneA=CloneA [i=88]]
==============After Clone==============
b1:CloneB [v=456, cloneA=CloneA [i=89]]
实现了深度复制。
========================================================
注意:并不是所有的类都能通过clone直接实现深度复制,比如
如果类的字段类型是StringBuffer类型,由于StringBuffer没有重载clone()方法,更为严重的是StringBuffer还是一个final类
这也是说我们也不能用继承的办法间接实现StringBuffer的clone。
不过我们可以在clone方法了改写:
new StringBuffer()代替 clone()方法。
另外,除了基本数据类型能自动实现深度clone以外,String对象是一个例外。