【问题标题】:clone() non final classesclone() 非最终类
【发布时间】:2016-11-29 09:58:46
【问题描述】:

根据 Josh Bloch 的 Effective java :-

不要使用 clone 方法制作参数的防御性副本 其类型可被不受信任的方子类化。

现在只从他的书中挑选一位专家:-

public final class Period {
private final Date start;
private final Date end;

/**
 * @param  start the beginning of the period
 * @param  end the end of the period; must not precede start
 * @throws IllegalArgumentException if start is after end
 * @throws NullPointerException if start or end is null
 */
public Period(Date start, Date end) {
    if (start.compareTo(end) > 0)
        throw new IllegalArgumentException(
            start + " after " + end);
    this.start = start;
    this.end   = end;
}

public Date start() {
    return start;
}
public Date end() {
    return end;
}

...  // Remainder omitted

}

如果我修改访问器方法以使用克隆函数返回日期对象的副本,而不是使用像这样的构造函数进行复制,我不明白会发生什么错误:-

public Date start() {
    return start.clone();
}

而不是

public Date start() {
       return new Date(start.getTime());
}

恶意子类的实例怎么可能被返回?

【问题讨论】:

标签: java clone


【解决方案1】:

采用java.util.Date 类,它在其clone() 方法中提供了一个防御性副本,但也不是最终的。
假设我继承了Date 并覆盖了clone() 方法。
现在,在运行时,如果我收到此子类的实例,则不再使用 Date 的 clone()。所以,我不确定子类的实现是否仍然像原来一样防御性的副本。

这个:

public Date start() {
    return start.clone();
}

如果子类像这样覆盖clone(),则不会是防御性副本:

@Override 
public Object clone() {
    return this; 
}

【讨论】:

  • 你的意思是:- Period p = new Period(new MaliciousDate(), new MaliciousDate());可能会造成伤害? MaliciousDate 扩展 Date 并覆盖 clone()。
  • 是的,如果您对这些参数调用 clone() 方法并且 clone() 的 MaliciousDate 实现不是防御性副本。在这种情况下,您将向您的客户提供相同的实例。而且,即使 Date 的 clone() 方法制作了一个防御性副本。因为在运行时,调用的是有效实例的方法。
【解决方案2】:

为访问器方法返回恶意子类的实例。引用同一章节 -

在访问器中,与构造器不同,它允许 使用克隆方法制作防御副本。这是因为 我们知道Period 的内部Date 对象类是 java.util.Date,而不是一些可能不受信任的子类。

扩展一点

不要使用 clone 方法制作参数的防御性副本 其类型可由不受信任的方子类化。

由于Date 不是最终的,它可以被子类化以改变其行为,因此Date 的克隆方法很可能返回一个恶意子类的实例。

例子

public class TestNonFinalCloneIssue {
    public static void main(String[] args) {

        NonFinalDate start = new NonFinalDate();
        NonFinalDate end = new NonFinalDate();
        Period p = new Period(start, end);

        // We are completely obvious to the fact that copies of our data
        // exists in another malicious class
        System.out.println("Secretly copied data - " + NonFinalDate.MaliciousDate.getListOfInstances());
    }
}

final class Period {
    private final NonFinalDate start;
    private final NonFinalDate end;

    public Period(NonFinalDate start, NonFinalDate end){
        NonFinalDate s = start.clone();
        NonFinalDate e = end.clone();
        if (s.compareTo(e) > 0){
            throw new IllegalStateException(start + " after " + end);
        }
        this.start = s;
        this.end = e;
    }
}

class NonFinalDate extends Date{
    @Override
    public NonFinalDate clone(){
        // NonFinalDate returning a malicious subclass (subclassing possible only because NonFinalDate is not final)
        return new MaliciousDate(this);
    }
    static class MaliciousDate extends NonFinalDate {
        private static List<NonFinalDate> listOfInstances = new ArrayList<>();

        public MaliciousDate(NonFinalDate date){
            // Secretly making copies of the data
            listOfInstances.add(date);
        }

        public static List<NonFinalDate> getListOfInstances() {
            return listOfInstances;
        }
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多