Effective Java —— 使类和成员的可访问性最小化
本文参考
本篇文章参考自《Effective Java》第三版第十五条"Minimize the accessibility of classes and members"
Access modifiers (private, package-private, protected and public)
访问修饰符 | 同类 | 同包 | 不同包子类 | 不同包非子类 |
private | √ | |||
package-private(默认) | √ | √ | ||
protected | √ | √ | √ | |
public | √ | √ | √ | √ |
Make each class or member as inaccessible as possible
顶层类和接口的访问权限往往会设计为public和package-private,如果package-private访问权限已经能够满足需求,则应该设计为package-private权限。
If a top-level class or interface can be made package-private, it should be
被public权限修饰符(以及protected权限修饰符)修饰的成员方法或成员变量往往是需要交付给客户的公共API,并且这些API不应该发生频繁的修改。而package-private权限修饰符修饰的类和接口则代表包内的某种业务逻辑实现,客户不需要关心也无法知道它们的API有哪些,因此对这些类和接口的方法签名的修改不会影响客户对系统的使用,也能够更好的区分内部实现的方法和外部暴露的方法
If a package-private top-level class or interface is used by only one class, consider making the top-level class a private static nested class of the sole class that uses it
正如上一点所说,我们需要尽可能地降低类、接口、成员方法和成员变量的可访问性,因此当一个package-private的类或接口A仅仅被另外一个类B使用时,可以考虑将A作为内部类内聚到B中
这样做的好处是,确保了包内的其它类和接口不会越权访问
Only if another class in the same package really needs to access a member should you remove the private modifier
当我们无法确定成员方法和成员变量合适的访问权限时,应当将它设计为private,当同一个包内的另一个类需要用到它时,再将它设计为package-private权限。也就是说,我们在设计类时,应当从private关键字开始逐级地开放成员方法和成员变量的权限
Tests can be made to run as part of the package being tested, thus gaining access to its package-private elements
当我们把类、接口、成员变量或成员方法的权限修饰符设计为package-private时,专门放置于"test"包内的测试代码就无法访问它们,我们不能够为了测试的便利而去提高它们的权限,而是将测试代码和包级私有的实现代码放到一起
Instance fields of public classes should rarely be public
譬如在"单例模式"的实现代码中,我们不会将instance成员变量的权限修饰符定为public,而是增加一个getInstance()的方法来或取单例。因为public权限修饰会使得程序的任何一处都可以修改该引用所指向的对象的值,所以在多线程的情况下更有可能导致严重的线程安全问题
注意,即使为引用类型的字段添加final关键字也无法确保对象的值不被更改,只是确保了对象的引用不变,这对于数组类型的字段也是如此
it is wrong for a class to have a public static final array field, or an accessor that returns such a field. If a class has such a field or accessor, clients will be able to modify the contents of the array.
针对数组的情况,有两种解决方法,一种是通过Collections.unmodifiableList()方法返回一个不可更改的List,另一种是调用数组的clone()方法,返回一个数组元素的深拷贝