18

什么是复制构造函数

有人可以分享一个有助于理解防御性复制原则的小例子吗?

4

5 回答 5

21

这是一个很好的例子:

class Point {
  final int x;
  final int y;

  Point(int x, int y) {
    this.x = x;
    this.y = y;
  }

  Point(Point p) {
    this(p.x, p.y);
  }

}

请注意构造函数如何Point(Point p)获取 aPoint并对其进行复制 - 那是 a copy constructor

这是一个defensive副本,因为通过复制原件Point可以防止更改。

所以现在:

// A simple point.
Point p1 = new Point(3,42);
// A new point at the same place as p1 but a completely different object.
Point p2 = new Point(p1);

请注意,这不一定是创建对象的正确方法。然而,这是一种创建对象的方法,可确保您不会意外地对同一个对象进行两次引用。显然,如果这是您想要实现的目标,这只是一件好事。

于 2013-02-22T09:45:02.653 回答
9

人们经常在 C++ 中看到复制构造函数,它们用于部分隐藏、自动调用的操作。

java java.awt.PointRectangle浮现在脑海中;也是非常古老的可变对象。

通过使用不可变对象,例如StringBigDecimal,只需分配对象引用即可。事实上,由于 C++ 之后 Java 的早期阶段,String中仍然存在一个愚蠢的复制构造函数:

public class Recipe {
    List<Ingredient> ingredients;

    public Recipe() {
        ingredients = new ArrayList<Ingredient>();
    }

    /** Copy constructor */
    public Recipe(Recipe other) {
        // Not sharing: ingredients = other.ingredients;
        ingredients = new ArrayList<>(other.ingredients);
    }

    public List<Ingredient> getIngredients() {
        // Defensive copy, so others cannot change this instance.
        return new ArrayList<Ingredient>(ingredients);
        // Often could do:
        // return Collections.immutableList(ingredients);
    }
}

根据要求

带有复制构造函数的泄漏类:

public class Wrong {
    private final List<String> list;

    public Wrong(List<String> list) {
        this.list = list; // Error: now shares list object with caller.
    }

    /** Copy constructor */
    public Wrong(Wrong wrong) {
        this.list = wrong.list; // Error: now shares list object with caller.
    }

    public List<String> getList() {
        return list; // Error: now shares list object with caller.
    }

    public void clear() {
        list.clear();
    }
}

具有复制构造函数的正确类:

public class Right {
    private final List<String> list;

    public Right(List<String> list) {
        this.list = new ArrayList<>(list);
    }

    public Right(Right right) {
        this.list = new ArrayList<>(right.list);
    }

    public List<String> getList() {
        return new ArrayList<>(list);
    }

    public List<String> getListForReading() {
        return Collections.unmodifiableList(list);
    }

    public void clear() {
        list.clear();
    }
}

带测试代码:

public static void main(String[] args) {
    List<String> list1 = new ArrayList<>();
    Collections.addAll(list1, "a", "b", "c", "d", "e");
    Wrong w1 = new Wrong(list1);
    list1.remove(0);
    System.out.printf("The first element of w1 is %s.%n", w1.getList().get(0)); // "b"
    Wrong w2 = new Wrong(w1);
    w2.clear();
    System.out.printf("Size of list1 %d, w1 %d, w2 %d.%n",
        list1.size(), w1.getList().size(), w2.getList().size());

    List<String> list2 = new ArrayList<>();
    Collections.addAll(list2, "a", "b", "c", "d", "e");
    Right r1 = new Right(list2);
    list2.remove(0);
    System.out.printf("The first element of r1 is %s.%n", r1.getList().get(0)); // "a"
    Right r2 = new Right(r1);
    r2.clear();
    System.out.printf("Size of list2 %d, r1 %d, r2 %d.%n",
        list2.size(), r1.getList().size(), r2.getList().size());
}

这使:

The first element of w1 is b.
Size of list1 0, w1 0, w2 0.
The first element of r1 is a.
Size of list2 4, r1 5, r2 0.
于 2013-02-22T09:45:23.963 回答
1

这是您通过传递旧对象并复制其值来创建新对象的地方。

Color copiedColor = new Color(oldColor);

代替 :

Color copiedColor = new Color(oldColor.getRed(),
                              oldColor.getGreen(), oldColor.getBlue());
于 2013-02-22T09:36:25.680 回答
1

复制构造函数用于使用现有对象的值创建新对象。
一种可能的用例是保护原始对象不被修改,而复制的对象可用于处理。

public class Person  
{  
   private String name;  
   private int age;  
   private int height;  


/** 
 * Copy constructor which creates a Person object identical to p.  
 */  
   public person(Person p)  
   {  
      person = p.person;  
      age = p.age;  
      height = p.height;  
   }  
.
.
. 
}

与防御副本相关的这里是一个很好的阅读

于 2013-02-22T09:50:14.663 回答
1

当您需要克隆对象时,可以使用 java 中的复制构造函数

class Copy {
   int a;
   int b;
  public Copy(Copy c1) {
    a=c1.a;
    b=c1.b;
  }
}

在 java 中,当你给 Copy 时c2=c1;只需创建对原始对象而不是副本的引用,因此您需要手动复制对象值。

看到这个:

于 2013-02-22T09:47:33.790 回答