在Josh Bloch的优秀著作Effective Java中,第39条建议如下:
“在检查参数的有效性之前,制作防御性副本,并且对副本而不是原件执行有效性检查。”
给出的示例如下:
在进行防御性拷贝之后再进行有效性检查的问题在于,无效的参数会导致拷贝的创建失败。例如,如果你为`start`或`end`传入`null`,则上述类将抛出`NullPointerException`。如果我在防御性拷贝之前进行有效性检查,则会面临布洛赫提到的时间检查/使用攻击的风险,这也是进行防御性拷贝的原因。我的问题是如何解决这个问题?我不相信我是第一个看到这个问题的人(尽管该书的勘误表没有提到),所以可能我只是忽略了什么。
“在检查参数的有效性之前,制作防御性副本,并且对副本而不是原件执行有效性检查。”
给出的示例如下:
public Period(Date start, Date end) {
this.start = new Date(start.getTime());
this.end = new Date(end.getTime());
if(this.start.compareTo(this.end) > 0)
throw new IllegalArgumentException("...");
}
}
在进行防御性拷贝之后再进行有效性检查的问题在于,无效的参数会导致拷贝的创建失败。例如,如果你为`start`或`end`传入`null`,则上述类将抛出`NullPointerException`。如果我在防御性拷贝之前进行有效性检查,则会面临布洛赫提到的时间检查/使用攻击的风险,这也是进行防御性拷贝的原因。我的问题是如何解决这个问题?我不相信我是第一个看到这个问题的人(尽管该书的勘误表没有提到),所以可能我只是忽略了什么。
Period p = new Period(someStart, someEnd)
,然后在构造函数中的空检查之后和防御性复制之前将someStart=null
,那么当它到达start.getTime()
调用时,它不会抛出NullPointerException
吗? - standsomeStart
只是一个对象的引用(假设该对象在0x123
处)。当您使用参数someStart
调用构造函数时,您正在将其start
参数指向实际对象0x123
。无论其他线程中是否设置了someStart = null
,构造函数都只知道对象0x123
。危险的是,如果从另一个线程调用someStart.changeState(5)
,这确实会更改对象0x123
的状态。 - toto2