什么是复制构造函数?
有人能分享一个小例子来帮助理解防御性复制原则吗?
有人能分享一个小例子来帮助理解防御性复制原则吗?
这里有一个很好的例子:
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
并复制它 - 这就是一个拷贝构造函数
。防御性
复制,因为通过复制它来保护原始的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.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
实际上并不是不可变的。请参见 https://dev59.com/hXVC5IYBdhLWcg3w7Vtq#12600683。 - Gili在Java中,拷贝构造函数可以用于克隆对象。
class Copy {
int a;
int b;
public Copy(Copy c1) {
a=c1.a;
b=c1.b;
}
}
c2=c1
时,它只是创建了一个对原始对象的引用,而不是副本,因此你需要手动复制对象值。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;
}
.
.
.
}
与防御性拷贝相关的这篇文章是一篇不错的阅读材料