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,主要论述语言的关键能力.在论述的过程中会结合华为各语言编程专家和华为电信软件内部的骨干开发人员的交流,摒弃语言偏好或者语言教派之争,尽量以 ...
随机推荐
- php抓取页面的几种方法详解
本篇文章是对php抓取页面的几种方法进行了详细的分析介绍,需要的朋友参考下 在 做一些天气预报或者RSS订阅的程序时,往往需要抓取非本地文件,一般情况下都是利用php模拟浏览器的访问,通过http请求 ...
- HDU 2985 Another lottery(坑题)
点我看题目 题意 : 有n个人,每个人可以玩m轮,每一轮中每个参与者都有cj张票,第 i 轮的奖金是2的i次方,问你每个人所能赢得最多的奖金的概率是多少. 思路 : 这个题比较坑啊,其实不用去算前几轮 ...
- 转一贴,今天实在写累了,也看累了--【Python异步非阻塞IO多路复用Select/Poll/Epoll使用】
下面这篇,原理理解了, 再结合 这一周来的心得体会,整个框架就差不多了... http://www.haiyun.me/archives/1056.html 有许多封装好的异步非阻塞IO多路复用框架, ...
- 汇编语言第二版 程序在dos中执行情况.P86-87
假设程序要被dos系统加载到sa:0000的内存中,在这个地址的内存开始会有256个字节的PSP程序,用于加载程序和dos系统的通信.ds中的地址为sa. 真正的程序会在这256个字节之后.所以真正程 ...
- Python和C|C++的混编(一):Python调用C、C++---Boost库
不使用boost.python库来直接构建dll的话比较繁琐,下面实例是借助boost库实现python对C.C++的调用 1 首先确定已经安装python和boost库,本例测试环境是python2 ...
- 【HDOJ】1356 The Balance
扩展欧几里得的应用. /* 1356 */ #include <iostream> #include <sstream> #include <string> #in ...
- POJ_2456_Agressive_cows_(二分,最大化最小值)
描述 http://poj.org/problem?id=2456 有n个小屋,线性排列在不同位置,m头牛,每头牛占据一个小屋,求最近的两头牛之间距离的最大值. Aggressive cows Tim ...
- 从头开始编写一个Orchard网上商店模块(2) - 配置您的Orchard开发环境
原文地址:http://skywalkersoftwaredevelopment.net/blog/writing-an-orchard-webshop-module-from-scratch-par ...
- [King.yue]关于CSLA框架的一些看法
CSLA.Net 是一个有帮助的成熟开发框架,但不适于初学者.该框架支持在任何地方.任何时间创建对象,值得我们花时间去学习了解这一框架.CSLA.Net 框架设计的业务对象,支持对完全透明的数据源进行 ...
- 使用页面对象模型(pageFactory)
页面对象模型可以使测试脚本有更高癿可维护性,减少了重复癿代码,把页面抽象出来. 页面对象设计模式提供了测试一个接口,测试可以像用户行为一样来操作页面. 通过隐藏页面元素定位,返有劣将测试代码和页面分离 ...