我们在复制java对象的时候,往往使用Object.clone();这个方法。但是,如果对象内引用了另一个对象——假如说,a1对象内引用另一个对象b1,如果a2克隆a1,那么a2引用的b对象,还是b1吗?
根据某些时候的业务需求,分成了两种情况:
浅克隆:复制对象,但引用不变(依旧是B1)。
深克隆:复制对象,连同对象的引用的对象,一起复制(生成B2)。
可以做个测试:修改a2的引用中b的成员变量,然后同时输出a1的引用b的成员变量的值,进行比较。
浅克隆----------------------------------
类A:
public class A implements Cloneable {
private String name;
private int age;
private B b;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public B getB() {
return b;
}
public void setB(B b) {
this.b = b;
}
@Override
public Object clone() throws CloneNotSupportedException
{
return super.clone();
}
}
public class B {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
测试类:
public static void main(String[] args) throws CloneNotSupportedException {
A a1 = new A();
B b1 = new B();
a1.setName("A1");
a1.setAge(1);
a1.setB(b1);
b1.setName("B1");
b1.setAge(11);
A a2 = (A) a1.clone();
a2.getB().setName("B2");
a2.getB().setAge(22);
System.out.println("a1的B对象属性name:"+a1.getB().getName());
System.out.println("a1的B对象属性age:"+a1.getB().getAge());
System.out.println("a2的B对象属性name:"+a2.getB().getName());
System.out.println("a2的B对象属性age:"+a2.getB().getAge());
}
输出结果:
a1的B对象属性name:B2
a1的B对象属性age:22
a2的B对象属性name:B2
a2的B对象属性age:22
深克隆----------------------------------
类A:
public class A implements Cloneable {
private String name;
private int age;
private B b;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public B getB() {
return b;
}
public void setB(B b) {
this.b = b;
}
@Override
public Object clone() throws CloneNotSupportedException
{
A a = (A) super.clone();
a.setB((B)a.getB().clone());
return a;
}
}
类B:
public class B implements Cloneable{
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public Object clone() throws CloneNotSupportedException
{
return super.clone();
}
}
测试类:
public static void main(String[] args) throws CloneNotSupportedException {
A a1 = new A ();
B b1 = new B ();
a1.setAge(1);
a1.setB(b1);
b1.setName("B1");
b1.setAge(11);
A a2 = (A) a1.clone();
a2.getB().setName("B2");
a2.getB().setAge(22);
System.out.println("a1的B对象属性name:"+a1.getB().getName());
System.out.println("a1的B对象属性age:"+a1.getB().getAge());
System.out.println("a2的B对象属性name:"+a2.getB().getName());
System.out.println("a2的B对象属性age:"+a2.getB().getAge());
}
输出结果:
a1的B对象属性name:B1
a1的B对象属性age:11
a2的B对象属性name:B2
a2的B对象属性age:22
分析:通过比较结果,我们可以看到:通过浅克隆进行克隆对象的操作,只是克隆了对象的成员变量和引用,对引用的对象并未进行深层次的复制;而通过深克隆进行克隆对象的操作,不仅仅克隆了对象的成员变量,连被克隆对象的引用对象都一起克隆了。
那么,如何实现两者呢?
需要事先说明的是,如果想要进行最基本的克隆对象操作,这个对象的类必须已经实现了Cloneable接口,并且重写了clone()这个方法(方法返回 super.clone()即可)。
浅克隆,只是需要我们对直接克隆的对象,实现这个接口即可。
深克隆就复杂一点,不过也好理解。
拿上面的例子说,首先,我们让B实现Cloneable这个接口,重写clone()这个方法。然后,让A也实现Cloneable接口,只是A的clone()方法,要变通一下:我们自己克隆a1,获得a,然后通过a获取b,再克隆b,再赋给a,最后返回a。简而言之,就是从a里面拿出来b,克隆出一个新的b,再放回去,原来的b被掉包了。
思考,如果B里面有个引用C,那么A,B,C那该怎样写呢?留给读者思考。