Effective Java 【类和接口】第15条 使类的成员的可访问行最小化

前言

区分一个组件设计得好不好,唯一重要的因素在于,它对于外部的其他组件而言,是否隐藏了其内部数据和其他实现细节。设计良好的组件会隐藏所有的实现细节,把API与实现清晰地隔离开来。这种概念被称为信息隐藏或者封装,是软件设计地基本原则之一。

信息隐藏之所以非常重要有许多原因,其中大多是因为:**它可以有效地解除组成系统地各组件之间地耦合关系,即解耦,使得这些组件可以独立地开发、测试、优化、使用、理解和修改。**因为这些组件可以并行开发,所以加快了系统开发地速度。同时减轻了维护地负担,程序员可以更快地理解这些组件,并且在调式它们地时候不影响其他地组件。虽然信息隐藏本身无论是对内还是对外都不会带来更好地性能,但是可以有效地调节性能:一旦完成一个系统,并通过剖析确定了那些组件影响了系统地性能(详见第67条),那些组件就可以被进一步优化,而不会影响到其他组件地正确性。信息隐藏提高了软件地可重用性,因为组件之间并不紧密相连,除了开发这写模块所使用地环境之外,它们在其他地环境中往往也很有用。最后,信息隐藏也降低了构建大型系统地风险,因为即使整个系统不可用,这些独立地组件仍然有可能是可用的。

java中如何控制

Java提供了许多机制来协助信息隐藏。访问控制机制决定了类、接口和成员的可访问性。实体的可访问性是由该实体声明所在的位置,以及该实体声明中所有出现的访问修饰符(private、protected、public)共同决定的。正确地使用这些修饰符对于实现信息隐藏式非常关键地。

规则很简单:尽可能地使每个类或者成员不被外界访问。换句话说,应该使用与你正在编写地软件地对应功能相一致的、尽可能最小化地访问级别。

对于顶层的(非嵌套的)类和接口

对于顶层的(非嵌套的)类和接口,只有两种可能的访问级别:

  1. 包级私有的(package-private)
  2. 公有的(public)

如果你用public修饰符声明了顶层或者接口,那它就是公有的;否则,它就是包级私有的。如果类或者接口能够被做成包级私有,它就应该被做成包级私有。通过把类或者接口做成包级私有,它实际上成了这个包的一部分,而不是该包导出的API的一部分,在以后的发行版本中,可以对它进行修改、替换或者删除,而无须担心会影响到现有的客户端程序。如果把它做成公有的,你就有责任永远支持它,以保持它们的兼容性。

如果一个包级私有的顶层类或者接口只在某一个类的内部被使用,就应该考虑使用他成为唯一使用它的那个私有嵌套类(详见第24条)。这样可以将它的可访问范围从包中的所有类缩小到使用它的那个类。然而,降低不必要公有类的可访问性,比降低包级私有的顶层类的可访问性重要得多:因为公有类是包的API的一部分,而包级私有的顶层则已经是这个包的实现的一部分。

对于成员(域、方法、嵌套类和嵌套接口)

对于成员(域、方法、嵌套类和嵌套接口),有四种可能的访问级别

  1. 私有的(private):只有在声明该成员的顶层类内部才可以访问这个成员。
  2. 包级私有(package-private):声明该成员的包内部的任何都可以访问这个成员。从技术上将,他被称为“缺省”(default)访问级别,如果没有为成员指定访问修饰符,就采用这个访问级别(当然,接口成员除外,它们默认的访问级别是公有的)。
  3. 受保护的(protected):声明该成员的类的子类可以访问这个成员(但是有一些限制,并且声明该成员的包内部的任何类也可以访问这个成员)。
  4. 共有的(public):在任何地方都可以访问该成员。

如何确定成员应该用什么修饰符?

这里给出几条建议:

  1. 一般来说成员都应该是私有的。
  2. 当同一个包内部的另一个类真正需要访问一个成员,使该成员变量变成包级私有。
    私有成员的包级私有成员都是一个类的是实现的一部分,一般不会影响导出的API。然而,如果这个类实现了Serializable接口(详见第86条和第87条),这些域就有可能会被“泄露”到导出的API中。
  3. 对于公有类的成员,当访问级别从包级私有变成保护级别时,会大大增加可访问性。
    受保护的成员是类的导出的API的一部分,必须永远得到支持。导出的类的受保护成员也代表了该类对于某个实现细节的公开承诺(详见第19条)。应该尽量少用受保护的成员。
  4. 如果方法覆盖了超类中的一个方法,子类中的访问级别就不允许低于超类中的访问级别。这样可以确保任何可使用超类的实例的地方也都可以使用子类的实例(详见第10条)。
    接口是特例,因为它默认是共有的。
  5. 公有类的实例域决不能是公有的,包含公有可变域的类通常并不是线程安全的。
    如果实例域是非final的,或者是一个指向可变对象的final引用,那么一旦是这个域成为公有的,就等于放弃了对存储在这个域中的值进行限制的能力;这意味着,你也放弃了强制这个域不可变的能力。同时,当这个域被修改的时候,你也失去了对它采取任何行动的能力。
  6. 假设常量构成了类提供的整个抽象的一部分,可以通过公有的静态final域来暴露这些常量。
    按惯例,这种域的名称由大写字母组成,单词之间用下划线隔开(详见第68条)。很重要的一点是,这些域要么包含基本类型的值,要么包含指向不可变对象的引用(详见第17条)。

从Java9开始,有新增了两种隐式访问级别,作为模块系统的一部分。一个模块就是一组包,就像一个包就是一组类一样。模块可以通过其模块声明中的导出声明显式的导出它的一部分包(按照惯例,这个包含在名为module-info.java的源文件中)。

这里不建议用模块化的思想去做访问限制,它更多的作用是将应用程序给分离模块化。

总结

总而言之,应该始终尽可能(合理)的降低程序元素的可访问性。再仔细设计了一个最小的公有API之后,应该防止把任何散乱的类、接口或者成员变量变成API的一部分。除了公有静态final域的特殊情形之外(此时它们充当常量),公有类都不应该包含公有域,并且要确保公有静态final域所引用的对象都是不可变的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值