Effective Java 第三版读书笔记——条款15:最小化类和成员的可访问性

设计良好的组件与设计不好的组件最重要的区别就是它们隐藏内部数据和其他实现细节的程度。一个设计良好的组件隐藏了它的所有实现细节,干净地将它的 API 与实现分离开来。然后组件只通过它们的 API 进行交流,并且对彼此的内部工作一无所知。这一概念,被称为信息隐藏或封装,是软件设计的基本原则。

信息隐藏如此重要的原因有很多,其中大部分在于它将组成系统的组件进行了解耦,允许它们被独立地开发、测试、优化、使用、理解和修改。这加速了系统开发,因为组件可以并行开发。它减轻了维护的负担,因为可以更快速地理解、调试或更换组件,而不用担心损害其他组件。虽然信息隐藏本身并不会导致良好的性能,但它可以有效地进行性能调整:一旦系统完成并且分析确定了哪些组件导致了性能问题(条款 67),则可以优化这些组件,而不会影响别人的正确的组件。信息隐藏增加了软件重用,因为松耦合的组件通常在除开发它们之外的其他环境中也是有用的。最后,隐藏信息降低了构建大型系统的风险,因为即使系统不能运行,各个独立的组件也可能是可用的。

经验法则很简单:让每个类或成员尽可能地无法被访问。换句话说,使用尽可能低的访问级别,与你正在编写的软件的对应功能保持一致。

对于顶层(非嵌套)的类和接口,只有两个可能的访问级别:包级私有(package-private)和公共的(public)。如果你使用 public 修饰符声明顶级类或接口,那么它是公开的;否则,它是包级私有的。如果一个顶层类或接口可以被做为包级私有,那么它应该是。通过将其设置为包级私有,可以将其作为实现的一部分,而不是导出的 API,你可以修改它、替换它,或者在后续版本中删除它,而不必担心损害现有的客户端。如果你把它公开,你就有义务永远地支持它,以保持兼容性。

如果一个包级私有的顶层类或接口只被一个类使用,那么可以考虑让这个顶层类成为使用它的唯一类的私有静态嵌套类(条款 24)。这将它的可访问性从包级的所有类减少到使用它的一个类。

对于成员(属性、方法、嵌套类和嵌套接口),有四种可能的访问级别,在这里,按照可访问性从小到大列出:

  • private —— 该成员只能在声明它的顶层类内访问。
  • package-private —— 成员可以从被声明的包中的任何类中访问。从技术上讲,如果没有指定访问修饰符(接口成员除外,它默认是公共的),这是默认访问级别。
  • protected —— 成员可以从被声明的类的子类中访问(受一些限制),以及它声明的包中的任何类。
  • public —— 该成员可以从任何地方被访问。

公共类的实例属性很少公开(条款 16)。如果一个实例属性是非 final 的,或者是对可变对象的引用,那么如果将其公开,你就放弃了限制属性中的值的能力。你放弃了强制该属性为不变量的能力。另外,当属性被修改时,你放弃了采取任何操作的能力,因此具有公共可变属性的类通常不是线程安全的。即使属性是 final 的,并且引用了一个不可变的对象,如果将它公开,你也放弃了切换到新的内部数据表示的灵活性。

请注意,非零长度的数组总是可变的,所以类具有公共静态 final 数组属性,或返回这样一个属性的访问器是错误的。如果一个类有这样的属性或访问方法,客户端将能够修改数组的内容。这是安全漏洞的常见来源:

// Potential security hole!
public static final Thing[] VALUES = { ... };

有两种方法可以解决这个问题。你可以使公共数组私有并添加一个公共的不可变列表:

private static final Thing[] PRIVATE_VALUES = { ... };
public static final List<Thing> VALUES =
    Collections.unmodifiableList(Arrays.asList(PRIVATE_VALUES));

或者,可以将数组设置为 private,并添加一个返回私有数组拷贝的公共方法:

private static final Thing[] PRIVATE_VALUES = { ... };
public static final Thing[] values() {
    return PRIVATE_VALUES.clone();
}

猜你喜欢

转载自www.cnblogs.com/LeeFire/p/10072592.html