这里记录一下对java中克隆的理解及使用,java提供了Cloneable这个接口,一个类只要实现了这个接口,那么就可以被克隆,克隆通常有两种情况,一种是浅层克隆(又叫浅层复制、浅层拷贝、浅表复制),另一种是深层克隆(又叫深层复制、深层拷贝)。
克隆与生产一个对象有何区别呢?生成一个对象是得到一个对象的初始状态,而克隆一个对象,则将得到一个被克隆对象当前的所有状态,比如当前属性的值等等。
以下做一个例子来说明。
新建一个类TestObj.java,如下:
public class TestObj implements Cloneable{
private String name = null;
private String[] nameArr = null;
private TestObj2 testObj2 = null;
/**
* @return the testObj2
*/
public TestObj2 getTestObj2() {
return testObj2;
}
/**
* @param testObj2 the testObj2 to set
*/
public void setTestObj2(int i) {
this.testObj2 = new TestObj2(i);
}
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* @return the nameArr
*/
public String[] getNameArr() {
return nameArr;
}
/**
* @param nameArr the nameArr to set
*/
public void setNameArr(String[] nameArr) {
this.nameArr = nameArr;
}
public TestObj clone() {
TestObj clone = null;
try {
clone = (TestObj)super.clone();
//对对象的拷贝,如果没有这一行则是浅层复制
//clone.testObj2 = testObj2.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
}
在这个类里测试的属性为一个字符串、一个数组、一个类变量。
以下是另一个类TestObj2.java,如下:
public class TestObj2 implements Cloneable {
private int i = 0;
public TestObj2(int i){
this.i = i;
}
public void doAdd(){
this.i++;
}
public int display(){
return this.i;
}
public TestObj2 clone() {
TestObj2 clone = null;
try {
clone = (TestObj2)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
}
测试目标:
1、 什么是浅层复制
2、 什么是深层复制
浅层复制
1、 创建一个TestObj对象,然后给此对象赋值。
2、 再生成一个克隆对象,比较此克隆对象与被克隆的对象各属性值。
新建测试入口类TestCloneable.java:
public class TestCloneable{
private static TestCloneable testCloneable = new TestCloneable();
private void testClone(){
//原始对象
TestObj testObj = new TestObj();
//赋值
testObj.setTestObj2(1);
TestObj2 testObj2 = testObj.getTestObj2();
///
testObj.setName("tom");
String[] nameArr = new String[2];
nameArr[0] = "a";
nameArr[1] = "b";
testObj.setNameArr(nameArr);
System.out.println(testObj.getName()+">>"+testObj.getNameArr()+">>"+testObj2.display());
for (int i=0;i<testObj.getNameArr().length;i++){
System.out.println(testObj.getNameArr()[i]);
}
//克隆出来的对象
TestObj cloneTestObj1 = testObj.clone();
///
String[] nameArr2 = new String[2];
nameArr2[0] = "c";
nameArr2[1] = "d";
cloneTestObj1.setNameArr(nameArr2);
cloneTestObj1.setName("jim");
cloneTestObj1.getTestObj2().doAdd();
System.out.println(testObj.getName()+">>"+testObj.getNameArr()+">>"+testObj2.display());
for (int i=0;i<testObj.getNameArr().length;i++){
System.out.println(testObj.getNameArr()[i]);
}
System.out.println(cloneTestObj1.getName()+">>"+cloneTestObj1.getNameArr()+">>"+cloneTestObj1.getTestObj2().display());
for (int i=0;i<cloneTestObj1.getNameArr().length;i++){
System.out.println(cloneTestObj1.getNameArr()[i]);
}
}
/**
* @param args
*/
public static void main(String[] args) {
testCloneable.testClone();
}
}
运行main函数,输出结果为:
tom>>[Ljava.lang.String;@1fb8ee3>>1
a
b
tom>>[Ljava.lang.String;@1fb8ee3>>2
a
b
jim>>[Ljava.lang.String;@61de33>>2
c
d
以上输出中,前三行是原始对象的输出,中间三行是克隆对象cloneTestObj1设置属性值后,原始对象的输出,后三行是克隆对象的输出。
从上面中间三行可以看出,原始对象中的字符串和数组没有受到克隆对象设置值的影响,而类变量则受到了克隆对象的影响,也就是
cloneTestObj1.getTestObj2().doAdd();
改变的不仅仅是克隆对象还有原始对象,也就是克隆对象中类变量和原始对象指向的是同一个句柄,这种情况下的克隆对象往往不是我们所需要的,因为我们很显然需要一个完全与原始对象无关的对象,要做到这一点,就得用到深层复制了,也就是以上的克隆其实就是所说的浅层复制。
深层复制
在上面的TestObj类中,更改如下方法中的一行,将
clone.testObj2 = testObj2.clone();
注释去掉
public TestObj clone() {
TestObj clone = null;
try {
clone = (TestObj)super.clone();
//对对象的拷贝,如果没有这一行则是浅层复制
clone.testObj2 = testObj2.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
再次运行TestCloneable的main方法,得到如下结果:
tom>>[Ljava.lang.String;@1fb8ee3>>1
a
b
tom>>[Ljava.lang.String;@1fb8ee3>>1
a
b
jim>>[Ljava.lang.String;@61de33>>2
c
d
从上面的输出结果中可以看到,原始对象testObj的属性值没有受到克隆对象cloneTestObj1的影响,这个就是所说的深层复制了。
说明
1、 在上面的类TestObj和TestObj2中,都实现了Cloneable接口,不然会抛出CloneNotSupportedException
异常。
2、 在TestObj中,注意到只有类变量才需要额外的
clone.testObj2 = testObj2.clone();
处理,而字符串变量等不需要,这说明了在浅层克隆时对类类型的克隆只是克隆了原始对象的引用传递(reference)地址,并非是克隆了一个全新的对象。
3、 对象克隆在lucene中很多地方都用到了,比如在IndexInput.java中,所以在阅读lucene源代码的时候,弄清楚克隆的用法也是很有必要的。
非转载说明,本博文章皆为原创,转载本博文章请务必注明文章出处:
转载自子猴博客