【发布时间】:2013-02-22 09:31:15
【问题描述】:
什么是复制构造函数?
有人可以分享一个有助于理解防御性复制原则的小例子吗?
【问题讨论】:
-
你可以在这里找到防御性复制的详细解释:Defensive copying。
标签: java defensive-programming
什么是复制构造函数?
有人可以分享一个有助于理解防御性复制原则的小例子吗?
【问题讨论】:
标签: java defensive-programming
这是一个很好的例子:
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) 如何获取 Point 并复制它 - 那是 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);
请注意,这不一定是创建对象的正确方式。然而,这是一种好创建对象的方法,可以确保您不会意外地对同一个对象进行两次引用。显然,如果这是您想要实现的目标,这只是一件好事。
【讨论】:
在 C++ 中经常看到的复制构造函数在部分隐藏、自动调用的操作中需要它们。
java java.awt.Point 和 Rectangle 浮现在脑海中;也是非常古老的可变对象。
通过使用不可变对象,如String 或BigDecimal,只需分配对象引用即可。事实上,由于Java在C++之后的早期阶段,仍然存在一个
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.
【讨论】:
BigDecimal 实际上并不是一成不变的。见stackoverflow.com/a/12600683/14731
这是您通过传递旧对象并复制其值来创建新对象的地方。
Color copiedColor = new Color(oldColor);
而不是:
Color copiedColor = new Color(oldColor.getRed(),
oldColor.getGreen(), oldColor.getBlue());
【讨论】:
复制构造函数用于使用现有对象的值创建新对象。
一种可能的用例是保护原始对象不被修改,而复制的对象可用于处理。
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;
}
.
.
.
}
与防御副本有关的here很好读
【讨论】:
需要克隆对象时可以使用java中的复制构造函数
class Copy {
int a;
int b;
public Copy(Copy c1) {
a=c1.a;
b=c1.b;
}
}
在 java 中,当你给 Copy c2=c1;只需创建对原始对象而不是副本的引用,因此您需要手动复制对象值。
看这个:
【讨论】: