用Java克隆

对象类具有一个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()方法,以防止可以单独创建实例变量对象的浅表复制,并且可以更新其属性。这被称为深层副本。