[Effective Java] (13)使类和成员的可访问性最小化

  • 设计良好的模块会隐藏所有的实现细节,会把实现细节与它的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 = { … };

修正方法:

  1. 公有数组变为私有的,并增加一个公有的不可变列别:
private static final Thing[] PRIVATE_VALUES = { … };
public static final  List VALUES = 
        Collections.unmodifiableList(Arrays.asLIst(PRIVATE_VALUES));
  1. 使数组变成私有的,并添加一个公有方法,它返回私有数组的一个备份
private static final Thing[] PRIVATE_VALUES = { … };
public static fianl Thing[] values() {
    return PRIVATE_VALUES.clone();
}

总结:应该尽可能地降低可访问性。在仔细地设计一个最小的公有API之后,应该防止把任何散乱的类、接口和成员变成API的一部分,除公有静态final域的特殊情形之外,共有类都不应该包含公有域,并且要确保公有静态final域所引用的对象都不是可变的

你可能感兴趣的:([Effective Java] (13)使类和成员的可访问性最小化)