如果输入参数无效,如何在构造函数中创建防御性副本
在 Josh Bloch 的优秀著作 Effective Java 的第 39 条中,他说:
“[D]在检查参数有效性之前制作防御副本,并且有效性检查是在副本而不是原始副本上执行的。”
给出的示例如下:
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
。
如果我将有效性检查移到防御性副本之前,我很容易受到检查时间/使用时间攻击,布洛赫将其作为首先进行防御性副本的原因。
我的问题是有什么办法解决这个问题?我不敢相信我是第一个在一本读得很好的书中看到这个问题的人(尽管这本书的勘误表没有提到这个问题),所以也许我只是错过了一些东西。
In Josh Bloch's excellent book Effective Java under Item 39 he says:
"[D]efensive copies are made before checking the validity of the parameters, and the validity check is performed on the copies rather than on the originals."
The example given is as follows:
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("...");
}
}
The problem with doing the validity checks after the defensive copy is that an invalid parameter can cause the creation of the copy to fail. For example, the class above will throw a NullPointerException
if you pass it a null
for start
or end
.
If I move the validity check before the defensive copy, I am vulnerable to the time-of-check/time-of-use attack that Bloch cites as the reason for doing the defensive copy first.
My question is what is a way around this? I can't believe I'm the first person to see this problem in a well read book (though the errata for the book says nothing about it), so maybe I'm just missing something.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
正如其他人所说,您在复制参数之前检查
null
。不,黑客无法将对实际实例的引用更改为空引用,反之亦然。进行复制是为了避免另一个线程更改参数的内部状态。
As others said, you check for
null
before copying the parameters.No, a hacker won't be able to change a reference to an actual instance to a null reference, or vice-versa. The copying is done to avoid changes to the internal state of the arguments from another thread.
防御性副本是一个很好的策略,但它有它的先决条件......
一是拥有可以真正复制的东西...
恕我直言,这意味着必须在复制之前检查
null
,如果失败,则会抛出适当的异常...Defensive copy is a good strategy BUT it has its prerequisites...
One is having something that you can really copy...
IMHO this means the check for
null
has to be done BEFORE the copy and if it fails throw an appropriate exception...对于测试指针的有效性,您不需要“防御”。指针不能更改为 null 或其他对象,只能更改它所指向的内容。
当制作“防御副本”时,您需要在踏上地形之前使用某种“棍子”来测试地形 - 在使用之前检查每个指针的有效性,限制检查边界值等。这并不难,只是乏味,并且需要一点细节的头脑。
[此外,简单地允许 NullPointerExceptions“冒泡”也没有什么大害处。]
You don't need to be "defensive" about testing the validity of a pointer. The pointer can't change to a null or a different object, it's only the contents of what it points to that can change.
When making "defensive copies" you need to use a "stick" of sorts to test the terrain before you step on it -- check every pointer for validity before you use it, limit check bounds values, etc. It's not hard, just tedious, and it requires a bit of a mind for details.
[Also, there is no great harm in simply allowing the NullPointerExceptions to "bubble up".]
我不明白这个问题。异常框架已经考虑了无效参数。
I don't understand the problem. The exception framework already accounts for invalid arguments.