@(EffecitiveJava)[类设计|JAVA基础]
应用场景:
当我们准备实现一个具有特殊约束条件类的时候,假设类的客户端会尽其所能的破坏这个类的约束条件,因此我们必须保护性设计程序。
扩展:
JAVA是一门安全的语言。这就意味着,它对缓冲区溢出、数组越界、非法指针以及其它的内存破坏错误都自动免疫,而这些错误却困扰着诸如C和C++这样的不安全的语言。在一门安全的语言中,可以确切的知道,无论系统的其他部分发生什么事,这些类的约束都可以保持为真。对于那些“把内存当做一个巨大数组看待”的语言来说,这是不可能的。
规范已经注意事项
- 对于构造器的每个可变参数进行保护性拷贝
如上图所示,检查参数的合法性(约束)是在保护性拷贝之后进行的。而且参数的合法性的检查是针对保护性拷贝之后的对象,而不是针对原始对象。
这样做是为了避免危险阶段来自另外一个线程的修改参数。危险阶段指的是检查参数开始,直至保护性拷贝参数之间的时间。在计算机安全社区中,这段时间被称为TOC、TOU(Time-Of-Use,Time-Of-Check)。
- 对于参数类型可以被不可信任类子类化的参数,请不要使用clone方法进行保护性拷贝,文摘如下:
原因:不能保证clone方法返回类一定是X类的对象,由于JAVA的向上转型特性,返回的可能是出于恶意的目的而设计的不可信子类的实例。
- 当X类提供了可变内部成员的访问能力时,该访问返回的应该是可变内部域的保护性拷贝。
总结:
保护性拷贝,不仅仅适用于不可变类。当编写构造器或方法时,如果它要允许客户提供的对象进入到内部数据中,则有必要考虑一下,客户端提供的对象是否可能是有变的。如果是,就要考虑到你的类是否能容忍对象进入数据结构之后发生变化。如果答案是否定的,那么就必须使用保护性拷贝,并且让保护性拷贝之后的对象而不是原始对象进入到类的内部。
保护性拷贝可能会带来性能损失,我觉得这是对的。类的拷贝明显会增加一个方法执行的时间成本。原文说,这种说法并不总是对的,但并没有指出这种情况,我想了想,也没想到。
保护性拷贝是可以被代替的。如果某些情况下,拷贝成本收到限制,并且类信任它的客户端不会不恰当的修改组件,可以在文档中显示声明不得修改收到影响的组件,以此代替保护性拷贝。