谁能告诉我在JAVA中类的构造函数的引用参数方面什么是防御性复制
非常感谢
鉴于我可以从问题中提取的内容,我想问题可以总结如下......
让我们从这个开始:
class NotSafeAtAll
{
private final List<X> list;
public NotSafeAtAll(final List<X> list)
{
this.list = list;
}
}
这个类有一个大问题:它将列表引用复制为其成员字段。这意味着如果构造函数的调用者修改了这个列表,变化也会反映在NotSafeAtAll
实例中。
这就是“防御性复制”发挥作用的地方。考虑:
class ALittleMoreSafe
{
private final List<X> list;
public ALittleMoreSafe(final List<X> list)
{
this.list = new ArrayList<X>(list);
}
}
现在,该类拥有自己的列表副本;如果调用者修改了它作为参数传递给构造函数的列表,则ALittleMoreSafe
实例不会受到影响。
但当然,故事并没有到此结束。现在考虑后一个类中有一个方法可以返回它作为参数接收到的列表:
class ALittleMoreSafe
{
private final List<X> list;
public ALittleMoreSafe(final List<X> list)
{
list = new ArrayList<X>(list);
}
public List<X> unsafeGetList()
{
return list;
}
}
你输了!即使您的构造函数是安全的,事实是您将引用返回到您的内部列表;调用者可以通过这个引用修改列表。
再一次,防御性副本可以拯救你,但有一个更好的解决方案:确保你返回的列表是不可变的:
public List<X> safeGetList()
{
return Collections.unmodifiableList(list);
}
调用者修改返回列表的任何尝试都将失败。
那么,故事结束了吗?
不。
X
只有所有实例都是不可变的,它才能结束。不幸的是,非常经典且过度使用的 bean 模式无法保证这一点。但这是另一个故事。