JAVA学习之 Effective JAVA (读书笔记),
JAVA学习之 Effective JAVA (读书笔记),
EFFECTIVE JAVA 学习笔记
... 21
第一章 绪论
第二章 创建和销毁对象
Item 1:考虑采用静态工厂方法替代构造函数
JAVA SE范例:原生基本类型的包装类
优点:
1. 工厂方法带有名字
2. 不必每次调用都实例化对象(更为灵活、尤其适用于不可变类)
3. 能够返回任意子类对象(适用于基于接口的框架,如集合框架、以及更为灵活的服务提供者框架, 如JCE)
缺点:
1. 如不提供公共或受保护的构造函数则不能被子类化(继承)
2. 难以同其他的静态方法区分(采用命名约定有一定效果)
Item 2:通过私有化构造函数保证单件特性
实现单件有两种方式:
1. 采用公共静态成员变量+类静态初始化
2. 采用私有静态成员+静态工厂方法(更为灵活)。
Item 3:通过私有构造函数保证类不可实例化
JAVA SE范例:数学、集合等实用工具类
优点:
1. 保证类不可实例化,达到仅提供工具函数的目的,同时也保证不被继承(导致子类实例化)
缺点:
1. 容易滥用,导致过程式编程
Item 4:避免创建冗余对象
JAVA SE范例:基本对象的包装类
优点:
1. 减少冗余、提高了复用,同时性能也大为提升(尤其适用于不可变对象)
Item 5:消除孤立的对象引用
JAVA SE范例:堆栈的数组实现方式
优点:
1. 减少内存泄露,提高性能
Item 6:避免使用Finalizer
缺点:
1. 不能保证及时执行
2. 不能依赖其保证持久状态
第三章 所有对象通用的方法
Item 7:重写equals()方法时遵守通用协议
JAVA SE范例:线程、随机、集合等类。
不进行重写的情况:
1. 类的实例独一无二
2. 并不需要类进行逻辑相等测试
3. 超类已经提供可用equals()方法
4. 类为私有类(重写为抛出异常)
重写时遵守的协议:
1. 反身性
2. 对等性
3. 传递性
4. 一致性
5. 非空性
高质量equals()方法的特征:
1. 使用==操作符先行测试
2. 使用instanceof操作符测试
3. 使用cast进行类型转换
4. 相应字段进行相等性测试
5. 进行对等性、传递性、一致性
重写equals()方法时的注意事项:
1. 重写equals()方法时重写hashCode()方法
2. 不要过于聪明
3. 不要依赖不可靠资源
4. 不要子类化参数类型
Item 8:重写equals()时总重写hashCode()方法
JAVA SE范例:HashMap、HashTable、HashSet
重写时遵守的协议:
1. 相等测试字段不变时,hashCode()方法返回相同整数
2. 相等的两个对象返回相同的hashCode整数
3. 不相等的两个对象也可能产生相同的hashCode整数
4. 可以通过一个私有字段缓存hashCode值
构建Hash函数的基本步骤:
1. 存储非零整数值如17到整形变量result
2. 计算相关字段对应的hash值c
3. result = 37*result + c
4. 返回result
5. 测试是否相等对象拥有相同hash值
Item 9:总是重写toString()方法
JAVA SE范例:Object对象的toString()方法
优点:
1. 更为方便使用、更为清晰
重写toString()时的注意事项:
1. 应该包含对象中的所有有意义的信息
2. 注释文档的意图(不管采用特定格式与否)
3. 方法返回的值最好能够编程访问
Item 10:谨慎地重写clon()方法
JAVA SE范例:Clonable接口
优点:
1. 提供更为灵活的clon()方法实现
2. 能够充当构造函数
Item 11:考虑实现Comparable接口
JAVA SE范例:List、Array等有序集合
实现compareTo()方法时遵守的规范:
1. 对称性
2. 传递性
3. 等值性
4. 相等性
5. 方法在比较不同类对象时抛出异常
第四章 类和接口
Item 12:最小化类及其成员的访问性
信息隐藏或封装是这一原则的基本概念,通过封装解藕模块关系。Java中内置有访问控制机制,提供private、protected、包访问等。
最小化原则:
1. 尽可能使得类或成员不可访问
2. 顶层类尽可能提供包私有访问
3. 成员尽可能提供私有访问
4. 不要提供public静态数组字段
Item 13:青睐不可变性
JAVA SE范例:String、包装类等
类不可变的五大原则:
1. 不提供修改对象的方法
2. 方法都不能被复写
3. 字段均为final型
4. 字段私有
5. 排除对可变组件的访问
优点:
1. 简单
2. 线程安全
3. 自由共享
4. 为其他对象提供大型构建快
缺点:
1. 每个值都得提供一个对象
总结:
1. 尽量使得类不可变
2. 限制类的可变性
3. 构造函数提供对像创建的所有信息
Item 14:青睐组合多于继承
继承的不足:
1. 破坏了封装
2. 容易导致过深的继承层次
Item 15:继承的设计、编档与禁止
如何为了类的后续继承而设计、编档?
1. 必修对重写任何方法的效果进行精确的注释
2. 类可能必修通过受保护的方法提供到内部工作的钩子
3. 构造函数禁止调用(直接、间接)可重写的方法
4. 将readResolve方法或writeReplace方法设为受保护的
5. 设计一个用于继承的类添加了子类化限制
6. 适当的时候应当禁止子类化
Item 16:相对抽象类偏好接口
1. 已经存在的类能够容易的实现一个新的接口
2. 接口是定义“微类型”的理想选择
3. 接口允许非继承的类型层次结构
4. 接口保证了安全、强大的功能增强
5. 抽象类比接口更容易演化(例外)
Item 17:仅仅用于定义类型的接口
一个典型范例——常量接口,常量接口是接口的一个比较差的应用。总的来说接口不应当仅仅用于定义类型
Item 18:青睐静态成员类而非非静态
JAVA中包含四种内联类:
1. 静态成员类
2. 非静态成员类
3. 匿名类
4. 本地类
第五章 C构造的替代物
Item 19:用类替代结构
类相比于结构体现出更好的封装性,但有时基于性能的考虑也有例外。
Item 20:类继承替代联合
Item 21:类替代枚举结构
C中的枚举仅定义了命名整数常量,JAVA中提供了新的类型安全枚举模式作为替代物。
Item 22:用类和接口替代函数指针
第六章 方法
Item 23:检查参数的有效性
通常方法对传入的参数的有效性有一定约束,如索引越界、非法参数、空指针等。具体检测策略:
1. 对公用方法用throws注释
2. 内部方法通过assert结构强化处理
总之,写方法是应该认真考虑参数的限制、予以注释并进行相应的检查。
Item 24:必要时进行防卫式拷贝
有必要进行防卫式编程,以保证不可变性,通常这需要通过防卫式拷贝予以实现。防卫式拷贝在参数有效性之前执行,并通常不使用Clon方法实现。
Item 25:仔细设计方法签名
1. 仔细的选择方法名
2. 不要在提供便利方法上走过头
3. 避免长参数列表
4. 对于参数类型,青睐接口而非类
5. 谨慎使用功能对象
Item 26:谨慎的使用重载
1. 编译期静态决定调用那个重载方法
2. 重载方法的选择是静态的、重写方法的选择是动态的
3. 要避免重载方法的混淆
4. 不要导出两个参数数相同的重载方法
Item 27:返回零长数组而非NULL
Item 28:对所有的导出API注释
1. 方法的注释应该描述其功能及与客户的契约
2. 以一致的规范编写注释
第七章 通用程序设计
Item 29:最小化局部变量的作用域
最小化局部变量作用域,增强了代码的可读性和可维护性、同时降低了犯错的几率。
1. 当局部变量第一次使用时,声明局部变量
2. 几乎每个局部变量的声明都应同时初始化
3. 循环变量最好置于FOR循环之中
4. 保持方法的短小精悍
Item 30:了解并应用库
1. 应用库你就利用了专家知识和前人的经验
2. 应用库也节约了开发时间
3. 易于掌握新添特性
4. 降低了学习成本
5. 不用重新发明轮子
Item 31:如果要求准确的结果则避免用FLOAT和DOUBLE
FLOAT、DOUBLE型变量不适用于准确十进制计算,尤其在财务计算中,这是做好用BigDicimal等类型。
Item 32:其他类型更合适时避免用String类型
1. String不适于充当其他值类型的替代物
2. String不适于充当枚举类型的替代物
3. String不适于充当聚合类型的替代物
4. String不适于充当功能的替代物
5. 更好的类型存在或能构造时,不要使用String
Item 33:明了字符串连接的性能
1. 字符串连接符缺乏性能表现
2. 用StringBuffer
Item 34:使用接口引用对象
1. 合适的接口存在,则优先使用接口
2. 应用接口,程序更为灵活
3. 不存在接口类型才使用类引用对象
Item 35:相较于反射青睐接口
反射的缺点:
1. 失去了编译期类型检查
2. 不容易书写、繁杂
3. 性能损耗
作为规则,运行时对象不应当以反射的方式访问。总之,反射一般仅用于初始化对象,除非对象完全不可知(接口类型都不存在)。
Item 36:谨慎使用JAVA本地方法
Item 37:谨慎地进行性能调优
Item 38:坚持一贯的命名惯例
第八章 异常
Item 39:仅当异常情况下使用异常
滥用异常进行程序流程控制的缺点:
1. 异常处理的代价高昂
2. JVM优化
3. 用异常进行边界检查没必要
4. 异常不应当用于正常的控制流
5. 设计良好的API不应当让用户处理正常控制异常
Item 40:对可恢复状况使用受检异常、对程序错误使用运行时异常
JAVA中包含三种异常:受检异常、运行时异常、错误。
异常的使用时机:
1. 当调用者被期望从异常状况中恢复时使用受检异常
2. 使用运行时异常指明程序错误
3. 所有的非受检异常都应该继承自RuntimeException
Item 41:避免不必要的受检异常
受检异常往往容易加重使用负担,强迫处理异常状况。仅当下列两种情况同时成立时,才使用受检异常:
1. 异常状况不能通过API的适当使用避免
2. 开发人员希望进行额外的处理
Item 42:青睐标准异常
JAVA中提供了一系列的标准非受检异常,开发人员可以在适当的时机予以重用。重用标准异常的好处:
1. API学习成本更低
2. 程序更易阅读
3. 更少的类、更快的类加载
常用的标准异常:
1. 非法参数异常
2. 非法状态异常
3. 空指针异常
4. 下标越界异常
5. 同步修改异常
6. 不支持操作异常
Item 43:抛出与抽象级适应的异常
JAVA中的异常链在进行异常传播时,通常对应着异常抽象级别的降低。高层应该捕获底层异常,抛出解释高层抽象的异常。
Item 44:注释方法抛出的所有异常
1. 总是声明受检异常,并通过@Throws精确注释
2. 使用@throws注释非受检异常,但不包含在方法声明中
3. 一个异常因同一原因在类的多个方法出现,则最好在类级别注释该异常
Item 45:在详细信息中包含失败捕获信息
JAVA中未捕获的异常将打出异常信息,该信息来自异常的toString方法。失败捕获信息中应该包含对异常有贡献的参数的值。
Item 46:争取失败原子性
失败的方法调用应该将对象的状态恢复到调用之前——失败原子。达到失败原子性的途径:
1. 不可变对象
2. 调用前参数校验
3. 排序计算,将修改置于发生失败的计算之后
4. 工作于对象的临时拷贝
Item 47: 不要忽略异常
通常,开发人员会通过一个空的catch快忽略异常,然而这通常并非所要的处理方式。这种方式违背了异常的目的——要求进行适当处理。至少,catch代码快应该包含为什么忽略异常处理的合理解释。
第九章 线程
Item 48:共享可变数据的同步访问
线程神话:基于性能的考虑,应当避免使用原子数据的读写同步。同步在线程间通信和排除可变性一样是必修的。总之,只要多个线程共享可变数据,读写数据的线程就应该进行加锁。
Item 49:避免越界的同步
1. 为避免死锁,不要在同步方法(代码快)内将控制转到客户端。
2. 作为规则,尽量保持同步代码的短小。
Item 50:禁止在loop循环外调用wait方法
对象的wait方法用来让线程等待适当的条件,Wait方法的标准使用方法是:在被锁定对象的同步代码中调用。Wait方法的使用方式:总是在循环中调用wait方法,以等待特定条件成立。
Item 51:不要依赖于线程调度
1. 依赖线程调度的程序更没有可移植性
2. 线程优先级是JAVA平台中最不具备移植性的特性
3. Thread.yield方法仅用于提高测试时的并发量
Item 52: 注释线程安全性
类应该清楚的表明其线程安全性的原因:
1. 同步代码是实现细节,并非导出API的一部分
2. 为保证安全的多线程使用,类必须在规格层面指明其支持的线程安全性
Item 53:避免线程组
1. 线程组没有提供任何的安全功能
2. 线程组在线程安全方面很弱
3. 线程组基本上是多余的
第十章 序列化(略)
相关文章
- 暂无相关文章
用户点评