Effective Java:Ch4_Class:Item13_最小化类及其成员的可访问性
要区别一个模块是否设计良好,最重要的因素是,对于其他模块而言该模块隐藏其内部数据和其他实现细节的程度。设计良好的模块应该隐藏所有实现细节,将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();
}
要在这两种方法间进行选择时,要考虑客户端可能怎么处理这个结果。那种返回类型更加方便?那种性能更好?
总之,
- 你应该尽可能减小可访问性。
- 在仔细设计一个最小的public API后,应该防止把不必要的类、接口、成员变成API的一部分。public类中,
- 除了public static final变量,不应该包含任何public变量。
- 确保public static final域引用的对象是不可变的。
Effective Java:Ch4_Class:Item13_最小化类及其成员的可访问性的更多相关文章
- Effective Java 第三版——15. 使类和成员的可访问性最小化
Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...
- 《Effective Java》笔记 使类和成员的可访问性最小化
类和接口 第13条 使类和成员的可访问性最小化 1.设计良好的模块会隐藏所有的实现细节,把它的API与实现清晰的隔离开来,模块之间只通过它们的API进行通信,一个模块不需要知道其他模块的内部工作情况: ...
- Effective Java —— 使类和成员的可访问性最小化
本文参考 本篇文章参考自<Effective Java>第三版第十五条"Minimize the accessibility of classes and members&quo ...
- Effective Java --使类和成员的可访问性最小化
尽可能地降低可访问性 接口和成员变量访问级别四种访问级别: 私有的(private) --- 只有在生命该成员的顶层类内部才可以访问 包级私有的(package-private) --- 缺省的&qu ...
- EffectiveJava(13)使类和成员的可访问性最小化
1.为什么要使类和成员可访问性最小化 它可以有效地解除组成系统的各模块之间的耦合关系,使得这些模块可以独立的开发 测试 优化 使用 理解和修改.提高软件的可重用性 2.成员的访问级别 私有(priva ...
- Effective Java -- 使可变性最小化
为了使类成为不可变的,应该遵循以下五条原则: 1. 不要提供任何会下盖对象状态的方法 2. 保证类不会被扩展 3. 使所有的域都是final的 4. 使所有的域都成为私有的 5. 确保对于任何可变组件 ...
- 《Effective Java》第4章 类和接口
第13条:使类和成员的可访问性最小化 第一规则很简单:尽可能地使每个类或者成员不被外界访问.换句话说.应该使用与你正在编写的软件的对应功能相一致的.尽可能最小的访问级别. 对于顶层的(非嵌套的)类和接 ...
- 一.OC基础之:1,OC语言的前世今生 ,2,OC语言入门,3,OC语言与C的差异,4,面向对象,5,类和对象的抽象关系,6,类的代码创建,7,类的成员组成及访问
1,OC语言的前世今生 , 一, 在20世纪80年代早期,布莱德.麦克(Brad Cox)设计了OC语言,它在C语言的基础上增加了一层,这意味着对C进行了扩展,从而创造出一门新的程序设计语言,支持对象 ...
- 大神为你分析 Go、Java、C 等主流编程语言(Go可以替代Java,而且最小化程序员的工作量,学习比较容易)
本文主要分析 C.C++98.C++11.Java 与 Go,主要论述语言的关键能力.在论述的过程中会结合华为各语言编程专家和华为电信软件内部的骨干开发人员的交流,摒弃语言偏好或者语言教派之争,尽量以 ...
随机推荐
- zoj 3765
一道区间更新.查询的题: 但是线段树不能做插入: 后来才知道用splay: splay用来做区间查询的话,先将l-1旋转到根节点,然后把r+1旋转到根节点的右节点: 这样的话,根节点的右节点的左子树就 ...
- loadmore & scroll
loadmore <link rel="stylesheet" href="http://code.jquery.com/mobile/1.0.1/jquery.m ...
- 年度十佳 DevOps 博客文章(后篇)
如果说 15 年你还没有将 DevOps 真正应用起来,16 年再不实践也未免太落伍了.在上篇文章中我们了解到 15 年十佳 DevOps 博客文章的第 6-10 名,有没有哪一篇抓住了您的眼球,让您 ...
- [string]Codeforces158C Cd and pwd commands
题目链接 题意很清楚 和linux的语句是一样的 pwd输出路径 cd进入 ..回上一层目录 此题完全是string的应用 String的用法 vector<string> s; int ...
- thinkphp 调用函数
1,定义为Common.php文件.自动加载. 2,配置文件config.php文件里配置'LOAD_EXT_FILE'=>'function'.则会自动加载function.php文件 3,使 ...
- char 和 varchar
固定长度或可变长度的字符数据类型. char [ ( n ) ] 固定长度,非 Unicode 字符数据,长度为 n 个字节.n 的取值范围为 1 至 8,000,存储大小是 n 个字节.char 的 ...
- Redisson使用起来很方便,但是需要redis环境支持eval命令
Redisson使用起来很方便,但是需要redis环境支持eval命令,否则一切都是悲剧,比如me.结果还是要用RedisCommands去写一套.例子就如下,获得一个RLock锁对象,然后tryLo ...
- 精通phthon的条件
1. 熟知主流硬件体系(x86, x64)2. 熟知 CPython 的具体实现,如若可能至少通读源码三遍以上3. 熟知每条 Python bytecode 如何被解释执行4. 熟知每条 Python ...
- BZOJ_1610_[Usaco2008_Feb]_Line连线游戏_(计算几何基础+暴力)
描述 http://www.lydsy.com/JudgeOnline/problem.php?id=1610 给出n个点,问两两确定的直线中,斜率不同的共有多少条. 分析 暴力枚举直线,算出来斜率放 ...
- WordPress Checkout插件跨站脚本漏洞和任意文件上传漏洞
漏洞名称: WordPress Checkout插件跨站脚本漏洞和任意文件上传漏洞 CNNVD编号: CNNVD-201311-015 发布时间: 2013-11-04 更新时间: 2013-11-0 ...