对象类具有一个clone方法,如果实例变量为基本类型,则可使用该方法复制对象的值而不会产生任何副作用。但是在实例变量作为对象的情况下,由于仅复制了引用,因此副作用起着作用。可以通过重写克隆方法来解决此问题。请参见以下示例。
public class Tester {
public static void main(String[] args) throws CloneNotSupportedException {
//方案1:浅拷贝
B b1 = new B();
A a1 = new A();
a1.a = 1;
a1.b = 2;
b1.aObject = a1;
b1.a = 1;
//打印一个a1对象
printObject(b1.aObject, "b1.a", b1.a);
//克隆运算符复制参考
B b2 = b1.clone();
//b1.aObject.a和b2.aObject.a现在指向同一对象
//修改b2.aObject.a,更改将反映在b1.aObject.a-
b2.aObject.a = 3;
printObject(b1.aObject, "b1.a", b1.a);
printObject(b2.aObject, "b2.a", b2.a);
//方案2:使用深度复制
C c1 = new C();
A a3 = new A();
a3.a = 1;
a3.b = 2;
c1.aObject = a3;
c1.a = 1;
//打印一个a1对象
printObject(c1.aObject, "c1.a", c1.a);
C c2 = c1.clone();
//c1.aObject.a和c2.aObject.a现在指向不同的对象
//修改c2.aObject.a,更改不会反映在c1.aObject.a-
c2.aObject.a = 3;
printObject(c1.aObject, "c1.a", c1.a);
printObject(c2.aObject, "c2.a", c2.a);
}
private static void printObject(A a, String name, int value) {
System.out.println(name + ": [" + a.a + ", " + a.b + "]" + ", " + value);
}
}
class A {
public int a;
public int b;
}
class B implements Cloneable {
public int a;
public A aObject;
public B clone() throws CloneNotSupportedException {
B b = (B)super.clone();
return b;
}
}
class C implements Cloneable {
public int a;
public A aObject;
public C clone() throws CloneNotSupportedException {
C c = (C)super.clone();
c.aObject = new A();
c.aObject.a = this.aObject.a;
c.aObject.b = this.aObject.b;
return c;
}
}输出结果
b1.a: [1, 2], 1 b1.a: [3, 2], 1 b2.a: [3, 2], 1 c1.a: [1, 2], 1 c1.a: [1, 2], 1 c2.a: [3, 2], 1
在原始实例变量的情况下,clone()方法没有副作用,因为在克隆过程中会创建一个新对象。
如果将对象作为实例变量使用,则clone()方法(如果未正确实施)也会产生副作用,因为克隆的对象具有引用的副本。这被称为浅表副本。
可以重写clone()方法,以防止可以单独创建实例变量对象的浅表复制,并且可以更新其属性。这被称为深层副本。