本文是《Effective Java》一书的整理笔记使类和成员的可访问性最小化 设计良好的模块会隐藏所有的实现细节,把它的API与它的实现清晰地隔离开来。然后,模块之间只通过它们的API进行通信,一个模块不需要知道其他模块的内部工作情况,这一概念被称为信息隐藏(information hiding
本文是《Effective Java》1书的整理笔记
使类和成员的可访问性最小化
设计良好的模块会隐藏所有的实现细节,把它的API与它的实现清晰地隔离开来。然后,模块之间只通过它们的API进行通讯,1个模块不需要知道其他模块的内部工作情况,这1概念被称为信息隐藏(information hiding)或封装(encapsulation)
封装的好处
它可以有效地消除组成系统各模块之间的耦合关系,使得这些模块可以独立的开发、测试、优化、使用、理解和修改。这样可以加快系统开发的速度,由于可以并行开发。
减轻保护的负担,由于程序员可以更快的理解这些模块,并在调试的时候可以不影响其他模块的负担。
虽然信息隐藏不会带来更好的性能,但可以有效的调理性能:通过剖析肯定了那些模块影响了性能。
提高了软件的可重用性
下降了构建大型系统的风险,由于即便全部系统不可用,但这些独立的模块却有多是可用的。
封装的规则
有两种解决方式,1是将数组变成私有,并添加1个公有的不可变列表
private static final Thing[] PRIVATE_VALUES = {};
public static final List<Thing> VALUES=
Collections.unmodifiableList(Arrays.asList(PRIVATE_VALUES));
2是使数组变成私有的,并添加1个公有方法,返回数组的备份。
private static final Thing[] PRIVATE_VALUES = {};
public static final Thing[] values() {
return PRIVATE_VALUES.clone();
}
使用方法访问类成员变量
使用setter和getter方法访问类成员变量,有以下好处
* 可以添加束缚条件
* 可以灵活的修改该类的内部表示方式
如何设计不可变类
不可变类的每一个实例包括的所有信息都必须在创建该实例的时候就提供,>并在对象的全部生命周期内固定不变。
不可变类遵守的规则
- 不要提供任何会修改对象属性的方法
- 保证类不会扩大
- 使所有域都是
final
的
- 所有的域都成为私有的
- 确保对任何可变组件的互斥访问
如果类中有指向可变对象的变量,要确保不会被该类的客户端取得指向这些对象的援用。在构造器、访问方法和readObject
方法中使用保护性拷贝。
不可变类的设计
不可变对象本质上是线程安全的,它们不要求同步。
可以提供1个静态工厂将该类经常使用的实例缓存起来,基本包装类和BigInteger
都有这样的静态工厂。
不要用为不可变对象提供clone
方法或拷贝构造器
不可变类的缺点是对不同的值都需要1个单独的对象。
可以提供1个配套的公有的可变配套类,避免在大范围操作时酿成的性能消耗,例如String
和StringBuilder
声明构造器为private
可以避免该类被继承。
- 许多不可变类具有多个非
final
域,当它们第1次被要求计算时,把1些开消昂贵的结果缓存到这些域中,以便下次再次要求一样的计算,就直接返回这些缓存的值。
- 如果需要让不可变类实现
Serializable
接口,就必须显示的提供1个readObject
或readResolve
方法,或使用ObjectOutputStream.writeUnshared
和ObjectInputStream.readUnshared
方法。
- 如果1个类不能被设计成不可变的,应当尽可能限制其可变性。
组合优于继承
《Java编程思想》称为组合,《Effective Java》称为复合
4.1 继承的缺点
这里所说的缺点不包括,接口继承和完全为继承而设计的超类
- 继承打破了封装性,子类依赖超类中特定功能的实现细节,但超类的实现有可能随着发行版本的不同而有所变化。
- 如果超类在后续版本中添加了1个新的方法,该方法与子类中的某1方法签名相同但返回值不同,将致使编译毛病。
取代继承的方式
- 使用转发的方式代替继承(装潢器模式)
- 采取组合方式代替继承
甚么时候需要继承
- 只有当二者之间确切存在“is-a”关系的时候
- 当超类的API设计有缺点时,采取继承机制会传播缺点,复合则允许新的API隐藏这类缺点
要末为继承而设计,并提供文档说明,要末制止继承
可覆盖的方法是指非final
的,public
或protected
的方法
- 该类必须有文档说明它可覆盖的方法的自用性
也就是说必须精确的描写覆盖每一个方法所带来的影响。
对可覆盖的方法或非private
构造器,必须指明它调用了那些可覆盖的方法,是以甚么顺序调用的,每一个调用结果又是如何影响后续的处理进程。那些情况下它会调用可覆盖的方法
- 构造器不能调用可被覆盖的方法
超类构造器将在子类构造器之前被调用,所以子类中覆盖的版本方法将在子类构造器之前被调用,如果该方法依赖构造器所履行的初始化方法,那末将有可能致使程序失败。
- 对为继承而设计的类中实现
Cloneable
或Serializable
接口,不管clone
还是readObject
都不可以调用可覆盖的方法,不管是间接还是直接。
- 对为继承而设计的类中实现
Serializable
,并且该来有readResolve
或writeReplace
方法,就必须使其成为protected
,避免子类疏忽这两个方法。
- 对不专门为继承而设计的类,最好要制止子类化
接口优于抽象
- 现有类可以很容易被更新,以实现新的接口
- 接口是定义mixin的理想选择
mixin是指这样的类型,类除实现它的“基本类型”以外,还可以实现这个mixin类型,以表明它提供了某些可供选择的行动。
接口之所以被称为mixin,是由于它允许任选的功能可被混合到类型的主要功能中。
- 接口允许我们构造非层次结构的类型框架
- 接口可以安全的增强类的功能
- 可以将接口和抽象类结合起来,提供1个抽象的骨架实现。例如
AbstractCollection
类
接口只用于定义类型
接口应当只被用来定义类型,不应当用于声明常量
优先斟酌静态成员类
- 嵌套类(nested class)是指定义在另外一个类的内部的类,包括静态成员类、非静态成员类、匿名类、局部类。
- 嵌套类存在的目的应当只是为它的外围类提供服务。
- 如果1个嵌套类在单个方法以外依然可见,或它太长了,不合适放在方法内部,就应当使用成员类。
- 如果成员类的每个实例都需要1个指向外围实例的援用,就要把成员类声明为非静态的。否则就要声明为静态的。
- 如果这个嵌套类属于1个方法的内部,并且你只需要在1个地方创建实例,而且已有1个预置的类型可以说明这个类的特点,就要把它做成匿名类。
静态成员类
- 静态成员类是外围类的1个静态成员,与其他静态成员1样,遵照一样的可访问性规则。
- 静态成员类的常见用法是作为公有的辅助类,仅当与它的外部类1起使用时才成心义。
- 私有静态成员类的另外一个常见用法是作为外围类所代表对象的1个组件。
非静态成员类
- 非静态成员类的1个常见用法是定义1个
Adapter
,它允许外部类的实例被看做另外一个不相干的类的实例。例如Map
接口中的集合视图。
- 非静态成员类的每一个实例都将包括1个额外的指向外围对象的援用,这需要消耗额外的空间和时间,并且会致使外围实例在符合垃圾回收时仍然得以保存,因此除非必须访问外围实例,否则要始终把成员类声明为
static
。
匿名类
- 匿名类要尽可能简短,否则会影响程序的可读性。10行或更少。
- 匿名类的1个常见用法是动态的创建函数对象,例如匿名
Comparator
实例。
- 匿名类的另外一个常见用法是创建进程对象,例如
Runnable
、Thread
。
欢迎大家访问我的博客,转载请注明出处
http://blog.csdn.net/abyss521