要区别一个模块是否设计良好,最重要的因素是,对于其他模块而言该模块隐藏其内部数据和其他实现细节的程度。设计良好的模块应该隐藏所有实现细节,将API与其实现清晰地隔离开来。这样,模块之间通过他们的API进行通信,而不必知道其他模块的内部工作情况。这个概念被称为信息隐藏(information hiding)或封装(encapsulation),是软件涉及的基本原则之一。

信息隐藏之所以重要有许多原因,大多数原因都源于这样一个事实:它可以将组成系统的各个模块解耦,使得这些模块可以独立地开发、测试、优化、使用、理解和修改。1、信息隐藏可以加速系统开发,因为这些模块可以并行开发。2、信息隐藏可以减轻维护负担,因为可以更快地理解这些模块,在调试时不用担心影响其他模块。3、虽然信息隐藏不会提高性能,但是他可以有效地调节性能:一旦完成了一个系统,并且通过婆媳确定了哪些模块导致性能问题,那么就能优化这些模块而无需担心影响其他模块。4、信息隐藏可以增加软件重用性,因为模块间并不紧密关联,模块除了在开发过程中的环境中有用之外,在其他环境中通常也有用。5、最后,信息隐藏减少了构建大型系统的风险,因为即便系统不可用,但这些独立的模块却可能是有用的。

Java提供了许多机制来帮助实现信息隐藏。访问控制机制可以指定类、接口及其成员的可访问性。实体的可见性是由该实体声明所在的位置,以及声明的访问修饰符决定的。正确地使用这些访问修饰符对于信息隐藏是及其关键的。

第一规则很简单:使类及其成员尽可能地不被访问。也就是说,在保证功能的情况下,使用最低的访问级别。

顶级类

对于顶级(非嵌套)的类和接口,只有两种可能的访问级别:package-private、public。如果一个顶级类或接口可以设置成包级私有,那就应该设成包级私有。这样它就成了实现的一部分而不是API的一部分,你可以对它修改、替换,或者在以后的版本中删除它,而不用担心影响已有的客户端。而如果你把它设成public,你就有义务永远支持它,以保证兼容性。

类成员

如果包级私有的顶级类(接口)仅仅只被一个类使用,那就考虑将这个顶级类写成私有嵌套类。这样可以把可访问范围进一步缩小,从包中的所有类缩小到使用它的那个类。然而,降低不必要的public类的访问性,比降低包级私有类的访问性要重要得多,因为public类是API的一部分,而包级私有类只是实现的一部分。

对于类成员(字段、方法、嵌套类、嵌套接口),则有四种可能的访问级别,按照可访问性递增顺序罗列如下:

  • private——成员只能被声明它的顶级类访问。
  • package-private——成员能被声明该成员的包中的任何类访问。也被称为default访问级别。
  • protected——成员能被声明它的类的子类访问,也能被声明该成员的包中的任何类访问。
  • public——成员能被所有类访问。

【private】【package-private】

当仔细设计好类的公共API后,你应该把所有其他成员设为private。只有当同一个包中的其他类确实需要访问该成员时,才移掉private修饰符,使该成员变成package-private。如果你经常这么做的话,你就应当重新检查你的系统设计,看看是否有另一种分解方案得到的类,与其他类的耦合度会更小。私有成员和包级私有成员都是类的实现的一部分,一般不会影响类的导出API。然而,如果类实现了Serializable,则这些成员可能会泄漏到导出API中

【protected】

对于public类的成员,当把访问级别从package-private改为protected后,其可访问性会大大增加。protected成员是该类的导出API的一部分,并且应当永远被支持。导出类的protected成员也代表了对实现细节的公开承诺。protected成员应当少用

有一个规则限制了你降低方法访问级别的能力:如果子类重写父类的一个方法,则子类方法的访问级别不能被父类方法低。这可以确保子类在父类出现的任何地方都能适用。如果违反了这条规则,编译器会产生一个错误信息。这个规则的一个特殊情形是,如果一个类实现一个接口,则接口中的所有方法在该类中都必须为public。这是因为接口中的所有成员都隐含着public访问级别。

为了方便测试,你可能想扩大类、接口、成员的可访问性。这在一定程度上是可以的,我们可以接受将public类的private成员变成package-private,以便来测试这个成员,但是如果扩大到更高的可访问性就不可接受了。换言之,我们不能接受仅仅为了方便测试就将类、接口、成员变成导出API的一部分。幸运的是,也不必要这么做,因为测试用例可以作为待测包的一部分来运行,这样就可以访问其package-private的成员。——如果测试用例在其他包里,怎么办?

变量

【实例变量】永远不能为public。如果实例变量不是final的,或者虽然是final,但是指向一个可变对象;如果将该实例变量设为public,则你就放弃了限制该变量取值的能力。这意味着你同时放弃了对该变量进行约束的能力、放弃了当该变量被修改时采取必要措施的能力,所以拥有public可变字段的类不是线程安全的。

即使变量是final的,并指向一个不可变对象,如果将该变量设为public,你也就放弃了将该域切换为一个新的内部数据表示的灵活性。

同样的建议也适用于【静态变量】,除了一种例外情况。你可以使用public static final变量来暴露一个常量,可以假定这个常量是该类的抽象的一部分。按惯例,这种变量名称必须大写,单词间用下划线分开。关键的一点是,这种变量要么指向基本类型,要么指向不可变对象。如果一个final变量指向一个可变对象,那么它就有非final变量的所有缺点。虽然引用不可修改,但引用的对象是可以修改的,这会导致灾难性的后果。

要注意长度非零的数组总是可变的,所以类中不能有public static final的【数组变量】,也不能有返回这种变量的方法。如果一个类中包含这种变量或方法,则客户端就能够修改数组的内容。这是安全漏洞的一个常见根源。

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

要注意,许多IDE会自动生成访问方法,返回指向private数组变量的引用,这就会导致上述问题。修正这个问题有两种方法:

1)将public的数组设为private,并添加一个public的不可变list:

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

2)或者可以将数组设为private,并添加一个public方法来返回这个private数组的拷贝:

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

要在这两种方法间进行选择时,要考虑客户端可能怎么处理这个结果。那种返回类型更加方便?那种性能更好?

总之,

  1. 你应该尽可能减小可访问性。
  2. 在仔细设计一个最小的public API后,应该防止把不必要的类、接口、成员变成API的一部分。public类中,
  3. 除了public static final变量,不应该包含任何public变量。
  4. 确保public static final域引用的对象是不可变的。

Effective Java:Ch4_Class:Item13_最小化类及其成员的可访问性的更多相关文章

  1. Effective Java 第三版——15. 使类和成员的可访问性最小化

    Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...

  2. 《Effective Java》笔记 使类和成员的可访问性最小化

    类和接口 第13条 使类和成员的可访问性最小化 1.设计良好的模块会隐藏所有的实现细节,把它的API与实现清晰的隔离开来,模块之间只通过它们的API进行通信,一个模块不需要知道其他模块的内部工作情况: ...

  3. Effective Java —— 使类和成员的可访问性最小化

    本文参考 本篇文章参考自<Effective Java>第三版第十五条"Minimize the accessibility of classes and members&quo ...

  4. Effective Java --使类和成员的可访问性最小化

    尽可能地降低可访问性 接口和成员变量访问级别四种访问级别: 私有的(private) --- 只有在生命该成员的顶层类内部才可以访问 包级私有的(package-private) --- 缺省的&qu ...

  5. EffectiveJava(13)使类和成员的可访问性最小化

    1.为什么要使类和成员可访问性最小化 它可以有效地解除组成系统的各模块之间的耦合关系,使得这些模块可以独立的开发 测试 优化 使用 理解和修改.提高软件的可重用性 2.成员的访问级别 私有(priva ...

  6. Effective Java -- 使可变性最小化

    为了使类成为不可变的,应该遵循以下五条原则: 1. 不要提供任何会下盖对象状态的方法 2. 保证类不会被扩展 3. 使所有的域都是final的 4. 使所有的域都成为私有的 5. 确保对于任何可变组件 ...

  7. 《Effective Java》第4章 类和接口

    第13条:使类和成员的可访问性最小化 第一规则很简单:尽可能地使每个类或者成员不被外界访问.换句话说.应该使用与你正在编写的软件的对应功能相一致的.尽可能最小的访问级别. 对于顶层的(非嵌套的)类和接 ...

  8. 一.OC基础之:1,OC语言的前世今生 ,2,OC语言入门,3,OC语言与C的差异,4,面向对象,5,类和对象的抽象关系,6,类的代码创建,7,类的成员组成及访问

    1,OC语言的前世今生 , 一, 在20世纪80年代早期,布莱德.麦克(Brad Cox)设计了OC语言,它在C语言的基础上增加了一层,这意味着对C进行了扩展,从而创造出一门新的程序设计语言,支持对象 ...

  9. 大神为你分析 Go、Java、C 等主流编程语言(Go可以替代Java,而且最小化程序员的工作量,学习比较容易)

    本文主要分析 C.C++98.C++11.Java 与 Go,主要论述语言的关键能力.在论述的过程中会结合华为各语言编程专家和华为电信软件内部的骨干开发人员的交流,摒弃语言偏好或者语言教派之争,尽量以 ...

随机推荐

  1. VIM编辑命令的技巧

    vim 选择文本,删除,复制,粘贴   文本的选择,对于编辑器来说,是很基本的东西,也经常被用到,总结如下: v    从光标当前位置开始,光标所经过的地方会被选中,再按一下v结束. V    从光标 ...

  2. android 自定义按钮的外边框

    <?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http: ...

  3. [Gauss]POJ1681 Painter's Problem

    和POJ1222(分析)完全相同 题意也类似, 可以涂自己以及上下左右五个位置的颜色 问几次能全部涂色 不能输出inf 01方程组 用异或来求解就好了 ][]; // 增广矩阵 ]; // 解 ]; ...

  4. Windows 内核(WRK)编译

    引子 WRK 是微软于 2006 年针对教育和学术界开放的 Windows 内核的部分源码, WRK(Windows Research Kernel)也就是 Windows 研究内核, 在 WRK 中 ...

  5. 优化 Android 线程和后台任务开发

    在 Android 开发中,你不应该做任何阻碍主线程的事情.但这究竟意味着什么呢?在这次海湾 Android 开发者大会讲座中,Ari Lacenski 认为对于长时间运行或潜在的复杂任务要特别小心. ...

  6. 【原创】FPGA开发手记(二) VGA接口

    以下内容均以Xilinx的Nexys3作为开发板 1.VGA接口介绍 首先,先看电路图(3*5为例): 标准VGA一共15个接口,但是实际应用的接口信号只用五个:HSYNC,行同步信号:VSYNC,场 ...

  7. [转] 舞蹈链(Dancing Links)——求解精确覆盖问题

    转载自:http://www.cnblogs.com/grenet/p/3145800.html 精确覆盖问题的定义:给定一个由0-1组成的矩阵,是否能找到一个行的集合,使得集合中每一列都恰好包含一个 ...

  8. js 获取服务器控件

    大致是HtmlControl被服务器发送到页面ID不变,比如<div id="a" runat="sever"></div> WebCo ...

  9. c程序设计语言_习题8-6_利用malloc()函数,重新实现c语言的库函数calloc()

    The standard library function calloc(n,size) returns a pointer to n objects of size size , with the ...

  10. 转 Xcode磁盘空间大清理

    http://www.iwangke.me/2013/09/09/clean-xcode-to-free-up-disk-space/#jtss-tsina 我的设备是Macbook Air 13’ ...