在创建防御性副本时创建新对象而不是克隆

时间:2016-11-12 03:55:31

标签: java

effective java项目中说:在需要时制作防御性副本,不使用克隆方法制作类型可由不信任方进行子类化的参数的防御性副本

我无法理解这实际意味着什么。

2 个答案:

答案 0 :(得分:2)

以下是本书在该部分重点介绍的问题。

 public MyOtherClass implements Cloneable {
     public Object clone() {
         super.clone();
     }
 }

 public MyOtherClass {
     private MyClass m;

     public MyOtherClass(MyClass m) {
         this.m = m.clone();  // Defensive copy.
     }
 }

 public SneakyClass extends MyClass {
     public clone() {
         return this;         // !!!!!!
     }
 }

通过将SneakyClass的实例传递给MyOtherClass构造函数,有人可以击败构造函数尝试执行的防御性副本。如您所见,clone() 的覆盖不会返回目标对象的副本。

(在这种情况下,解决方案是将MyClass声明为final或将MyClass.clone()声明为final。)

答案 1 :(得分:2)

您正在谈论的项目是描述如何正确实现不可变类。

完整段落说:

  

另请注意,我们没有使用Date的克隆方法来制作防御性副本。因为Date是非最终的,所以不能保证clone方法返回一个类为java.util.Date的对象;它可以返回一个专门为恶意恶作剧设计的不受信任的子类的实例。例如,这样的子类可以在创建私有静态列表时记录对每个实例的引用,并允许攻击者访问该列表。这将使攻击者在所有实例中自由统治。为防止此类攻击,不要使用克隆方法制作类型可由不信任方进行子类化的参数的防御性副本

要显示段落的其余部分所描述的内容,请设想一个执行此操作的clone方法:

public class Foo implements Cloneable {
    private int bar;
    private static List<Foo> secretList = new ArrayList<>();

    public Foo(int bar) { this.bar = bar; }

    @Override
    public Foo clone() {
        Foo copy = new Foo(this.bar);
        secretList.add(copy); // this is the line of concern
        return copy;
    }
}

现在Foo类仍然可以访问它创建的实例,这意味着它可能会恶意修改实例,即使它是不可变类的成员。