- 设计良好的模块会隐藏所有的实现细节,会把实现细节与它的API清晰的隔离开来,模块之间只通过它们的API进行通信,一个模块不需知道其他模块的内部工作情况。这个概念被称为信息隐藏(information hiding)或封装(encapsulation)。
1. 四种访问级别
关键字 | 描述 |
---|---|
private | 只有在声明该成员的顶层类内部才可以访问这个成员 |
package-private (default) | 声明该成员的包的内部的任何类都可以访问这个成员 |
protected | 声明该成员的类的子类可以访问这个成员,并且声明该成员的包内部的任何类也可以访问这个成员 |
public | 任何地方都可以访问该成员 |
2. 访问控制机制
- 访问控制机制决定了类、接口和成员的可访问性(accessibility);
- 尽可能地使每个类或者成员不被外界访问,对于顶层的(非嵌套的)类和接口,只有两种可能的访问级别:包级私有的(package-private)和公有的(public);
- 如果一个包级私有的顶层类(或者接口)只是在某一个类的内部被用到,就应该考虑使它成为唯一使用它的那个类的私有嵌套类;
- 只有当同一个包内的另一个类真正需要访问一个成员的时候,你才应该删除private修饰符,使该成员成为包级私有的。
- 如果方法覆盖了超类中的一个方法,子类中的访问级别就不允许低于超类中的访问级别;(特殊:如果一个类实现了一个接口,那么接口中所有的类方法在这个类中也都必须被生命为公有的,接口中所有方法都隐含着共有访问级别)
- 实例域中,包含共有可变域的类并不是线程安全的;
- 静态域中,假设常量构成类类提供的整个抽象中的一部分,可以通过共有的静态final域来暴露这些常量,由大写字母组成单词之间用下划线隔开。很重要一点,这些域要么包含基本类型的值,要么包含指向不可变对象的引用。
注:长度非零的数组总是可变的,类具有共有的静态final数组域,或者返回这种域的访问方法,几乎总是错误的。这是安全漏洞的一个常见根源:
// Potential security hole!
public static fianl Thing[] VALUES = { … };
修正方法:
- 公有数组变为私有的,并增加一个公有的不可变列别:
private static final Thing[] PRIVATE_VALUES = { … };
public static final List<Thing> VALUES =
Collections.unmodifiableList(Arrays.asLIst(PRIVATE_VALUES));
- 使数组变成私有的,并添加一个公有方法,它返回私有数组的一个备份
private static final Thing[] PRIVATE_VALUES = { … };
public static fianl Thing[] values() {
return PRIVATE_VALUES.clone();
}
总结:应该尽可能地降低可访问性。在仔细地设计一个最小的公有API之后,应该防止把任何散乱的类、接口和成员变成API的一部分,除公有静态final域的特殊情形之外,共有类都不应该包含公有域,并且要确保公有静态final域所引用的对象都不是可变的。