根据Josh Bloch的有效java: -
不要使用克隆方法制作参数的防御副本 其类型可由不信任方进行子类化。
现在只从他的书中找专家: -
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
}
如果我修改一个accesor方法以使用clone函数返回日期对象的副本而不是使用这样的构造函数进行复制,我不会得到什么错误: -
public Date start() {
return start.clone();
}
而不是
public Date start() {
return new Date(start.getTime());
}
如何才能返回恶意子类的实例?
答案 0 :(得分:3)
在java.util.Date
方法中使用提供防御性副本的clone()
类,但这也不是最终版本。
假设我将Date
子类化,并覆盖clone()
方法
现在,在运行时,如果我收到此子类的实例,则不再使用Date的clone()
。所以,我不确定子类的实现是否仍然是原始的防御性副本。
这:
public Date start() {
return start.clone();
}
如果子类重写clone()
,那么将不会是防御性副本:
@Override
public Object clone() {
return this;
}
答案 1 :(得分:0)
将为访问器方法返回恶意子类的实例。引用同一章-
在访问器中,与构造函数不同的是, 使用克隆方法制作防御副本。这是因为 我们知道
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;
}
}
}