Oct 19, 2018
Effective Java 笔记(一)
以下的所有条例仅仅作为一种大多数情况下的选择,但是编写代码仍需以实际情况[权衡]
1: 考虑用静态方法替代构造器
指用静态方法返回某个类的实例
eg:
1 | public static Test getInstance() { |
优点:
- 可命名,更容易使调用者理解
- 实例化更加可控与可定制化
- 可用于单例模式,避免实例化重复对象
- 可返回原来类型的任何子类
- 创建参数化类型实例时更加简洁
2: 遇到多个构造器参数时,考虑使用构建器
eg:
1 | Student student = new Student.Builder("kevin", 23).grade("1年级").build(); |
优点:
- 链式调用易于编写和理解
- 避免编写过多的构造函数
- 避免使用set方法分步实例化,会有线程安全问题
3: 用枚举类型强化Singleton属性
eg:
1 | public enum Instance { |
优点:
- 无需实现readResolve方法即可保证反序列化无法破坏单例
4: 使用私有构造器强化不可实例化的能力
一些没有必要实例化的类[如工具类],应当私有其构造函数
优点:避免无意识[不小心]的实例化
缺点:导致其无法被继承
5: 避免创造不必要的对象
当你应该重用现有对象时,请不要创建新对象,这里的[应该]指创建新对象代价较高时
一些公用的,不变的实例,可以提出来只构建一次,而不是放在方法内部去每次创建新实例
自动装箱
会导致实例化其包装对象,因此在基本类型满足需求的情况下,不要使用其包装类型
6: 消除过期的对象引用
过期的对象引用会在极端情况下导致内存泄漏,内存泄漏常见于三个来源
自己管理内存
如果一个容器中对象引用的删除是逻辑管理[类似逻辑删除]的,即其没有被真正移除,而用户在逻辑上认为已被移除.那么应该警惕内存泄漏问题,一旦元素被释放掉,应当同时移除该对象的引用
eg:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30public class Strack {
private Object[] elements;
private int size = 0;
private static final int DEFAULT_INITIAL_CAPACITY = 16;
public Strack() {
elements = new Object[DEFAULT_INITIAL_CAPACITY];
}
public void push(Object e) {
ensureCapacity();
elements[size++] = e;
}
public Object pop() {
if (size == 0) {
throw new EmptyStackException();
}
Object result = return elements[--size];
// 移除该对象的引用
elements[size] = null;
return result;
}
private void ensureCapacity() {
if (elements.length == size) {
elements = Arrays.copyOf(elements, 2 * size + 1);
}
}
}例如eg中被弹出的对象,哪怕在别的地方已经失去引用,但在elements中其位置未被别的对象引用替换前,elements仍保持了其过期的引用,会导致该对象以及该对象引用的对象无法被垃圾回收
缓存
当对象的引用被放入缓存中后,很容易被遗忘或忽略
在某些情况下,如缓存的存在是否必要仅有外部引用而不是值来决定时,可以考虑使用WeakHashMap来处理
更常见的做法是使用某种策略定期清理不用的缓存,正如许多框架所做的那样
监听器与其他回调
7: 避免使用终结方法
finalizer方法通常是不可预测、不稳定的,java规范也并不保证其一定会执行。在java中,内存资源的回收通常交由垃圾回收器处理,而其他资源[如文件流]的回收通常用try-finally来完成,因此应该尽量[避免]使用finalizer来做这些事。
8: 覆盖equals时遵守通用约定
通常情况下,我们认为每一个对象都是独一无二的,因此它应该只等于其自身,Object类的equals方法也确实是这么做的
但equals方法的结果应当取决于用户当前期望什么样是相等的,即逻辑相等。所以在某些情况下我们需要重写equals方法以达到目的。
很多已有的类中也重写了equals方法,他们都遵循了一些通用的约定。若我们重写方法时没有遵循这些约定,会造成某些不可预知的错误。
- 自反性
对于任何非null的引用值x,x.equals(x)必须返回true - 对称性
对于任何非null的引用值x、y,当且仅当y.equals(x)为true时,x.equals(y)必须为true - 传递性
对于任何非null的引用值x、y、z,如果x.equals(y)为true,并且y.equals(z)也为true,那么x.equals(z)也必须为true - 一致性
对于任何非null的引用值x、y,只要equals的比较操作在对象中所用的信息没有被修改,多次调用equals方法返回的结果必须一致 - 对于任何非null的引用值x,x.equals(null)必须为false
9: 覆盖equals时总要覆盖hashCode
重写equals方法时应当重写hashCode方法,否则会导致该类无法结合所有基于散列的集合一起正常工作
- 若一个类的equals方法返回true,则hashCode方法应当返回相同的整数
- 若一个类的equals方法返回false,hashCode方法不一定要返回不同的整数,但返回不同的整数有利于提高性能
10: 使用要覆盖toString
应当尽可能的覆盖toString方法,返回该对象的内容摘要
有助于在任何可能输出该类信息的地方
使得使用者得到易于理解的信息