作者简介:Joshua Bloch是Google的首席工程师,之前是Sun的杰出工程师,他领导了大量的java平台设计和实现工作,包括jdk5.0的语言增强和集合框架,拥有卡内基.梅隆大学的计算机科学的博士学位。
本书的目标是帮助你最有效的运用java编程语言及其基本库,java.lang、java.util和java.io。本书由57个条目组成,每个条目传达一个原则,这些原则是最有经验的程序员在实践过程中的一些有用的做法。
Creating and Destroying Object
Item 1:
考虑用静态工厂方法替代构造器
public static Boolean valueOf(boolean b) {
return (b?Boolean.TRUE:Boolean.FALSE);
}
考虑在构造函数之外提供static的factory mothod。
优点:1.名字可不与类名相同,可以采用更易理解的名称。2.不必每次都返回一个新的对象。3.可以返回子类型。
缺点:1.无法被继承。2.和其它static方法无法区分。
Item 2:
通过添加私有构造器来加强单例属性(singletom property)
public class Hello {
public static final Hello Instance = new Hell();
private Hello() {}
}
public class Hello {
private static final Hello Instance = new Hell();
private Hello(){}
public static Hello getInstance() {
return Instance;
}
}
第二种方法可以方便改变。
如果想让singleton类成为serializable,除了implements Serializable外,还要提供readResolve()方法返回该单例。
Item 3:
为构造器添加private以避免被实例化
对于只是由static属性和方法构成的工具类,要为构造器添加private,以免被实例化,而不添加的话,则可能有默认的构造器。
Item 4:
避免创建重复的对象
尤其是创建比较昂贵的。
对不可修改的对象尽量进行复用,这样效率和性能都会提高。例如如果循环100次String s = new String("hello")将创建100个对象 循环100次String s = "hello";则只创建了一个对象。很好的进行了复用。
Item 5:
消除绝对的对象引用
内存泄漏,在cache里易发生。
public Object pop(){
if(size == 0) {}
return elements[size--];
}
public Object pop() {
if(size == 0) {}
Object obj = elements[--size];
elements[size] = null;
return obj;
}
但是不要滥用,不要每个对象使用后都用NULL。
Item 6:
避免finalizer
不要当作C++中的析构函数,正确的方法是放在finally中。
Methods Common to All Objects
Item 7:
当你覆盖equals方法的时候一定要遵守general contact
覆盖equals的时候一定要加倍的小心,其实最好的办法就是不覆盖这个方法。比如在下面的情况下就可以不覆盖
1
这个类的每个实例都是唯一的,例如Thread类
2
如果你不关心这个类是否该提供一个测试逻辑相等的方法
3
超类已经覆盖了equals方法,并且它合适子类使用
4
如果这个类是private或者是package-private的,并且你确信他不会被调用
覆盖时遵守的原则是
必须与自身相等,对称性A<->B,传递性,一致性如果不改变始终都是一致的结果,不能与NULL相等。
Item 8:
当你覆盖equals的时候必须覆盖hashCode方法
不这么做的话,有关hash-based的集合操作会出错。
契约:
同一对象的hashCode返回相同的结果(equals比较属性没改变),如果equals相等,则hashCode相等,如果equals不等,则hashCode不必不等。
Item 9:
总是覆盖toString方法
Object
的toString方法返回的形式是Class的类型加上@加上16进制的hashcode,不利于描述对象的信息。
Item 10:
谨慎覆盖clone()方法
尽量不要实现,碰到deepcopy的问题。
Item 11:
考虑实现覆盖Comparable接口
契约:传递性,x.compareTo(y)>0,y.compareTo(z)>0,则x.compareTo(z)>0
如果x.compareTo(y)=0,则x.compareTo(z)和y.compareTo(z)结果一致
建议x.compareTo(y)=0 则 x.equals(y)=true,但不要求。
Classes and Interfaces
Item 12:
把类和成员的可访问范围降到最低
通常public类里不应该有public字段,除了常量,要注意常量应该是不可修改的。
public class Con {
public static final int[] data = {1,2,3};// it is bad
public static final String hello = "world";
public static final int i = 1;
}
Item 13:
偏爱不可修改的类
5
个原则保证不可修改
不提供可修改对象的方法,方法不可被覆盖(final),所有字段是final,所有字段是private,确保外部不能访问到类的可修改的组件
好处
可自由共享,线程安全
Item 14:
优先考虑合成(复合),其次是继承
利用wrapper class,除非确实是is a的关系。
Item 15:
如果要用继承那么设计以及文档都要有质量保证,否则就不要用继承
为了避免继承带来的问题,你必须提供精确的文档来说明覆盖相关方法可能出现的问题。
在构造器内千万不要调用可以被覆盖的方法
由于在Clone()或者序列化的时候非常类似构造器的功能,因此readObject()和clone()方法内最好也不要包括能被覆盖的方法。
Item 16:
使用接口代替抽象类
单重继承
Item 17:
接口只应该用来定义类型
常量不应该放在接口中。
Item 18:
优先考虑静态内部类,而非非静态内部类
Substitutes for C Constructs
Item 19:
用类代替结构
Item 20:
用类继承来代替联合
Item 21:
用类来代替enum结构
Item 22:
用类和接口来代替函数指针
Methods
Item 23:
检查参数的有效性
Item 24:
需要时使用保护性拷贝
Item 25:
谨慎设计方法的签名
命名,不要过分提供便利的方法,避免过长参数列表
对于参数类型,优先使用接口而不是类
谨慎的使用函数对象
Item 26:
谨慎的使用重载
Item 27:
返回零长度的数组而不是null
Item 28:
为所有导出的API元素编写文档注释
当代码能很好的说明问题时,可不写注释
General Programming
Item 29:
将局部变量的作用域最小化
不易阅读,在变量声明的时候初始化,try-catch例外
for
循环优于while循环,for完全独立,重用变量名不会有任何问题
for (int i=0,n=list.size(); i<n; i++) {
dosomething(list.get(i));
}
Item 30:
了解和使用库
Item 31:
如果想要知道精确的答案,就要避免使用double和float
使用int,long或BigDecimal
Item 32:
如果其他类型更适合,则尽量避免使用字符串
如枚举
Item 33:
了解字符串的连接功能
使用StringBuffer代替String
Item 34:
通过接口引用对象
参数、返回值,将会给函数带来很大的灵活
Item 35:
接口优先于反射机制
损失了编译期的类型检查的好处,性能上也会受损。
Item 36:
谨慎的使用本地方法
Item 37:
谨慎使用优化
不要因为性能而牺牲合理的代码结构
Item 38:
遵守普遍接受的命名惯例
Exceptions
Item 39:
仅在异常情况下使用异常
try {
int i=0;
while (true)
a[i++].f();
} catch (ArrayIndexOutOfBoundsException) {}
利用异常终止循环,不要这样滥用异常。
一个良好的设计不应该依靠异常去控制流程
Item 40:
可恢复状态使用检查异常(Exception),对编程错误使用运行期异常(RuntimeException)
Item 41:
避免不必要的使用检查异常
可以先检查是否有异常情况,而不是直接用try-catch控制程序流程
Item 42:
尽量使用标准异常
IllegalArgumentException
参数不合法,IllegalStateException状态不合法
NullPointerException
空指针,IndexOutOfBoundsException越界
Item 43:
引发的异常要与抽象对应
异常的分层,lower-level high-level
Item 44:
提供每个方法所抛出的异常的文档
用javadoc的@throws标签
一般针对检查异常
Item 45:
在messages中记录失败捕获的信息
Item 46:
使失败原子化
即使方法调用失败后,也要恢复对象的状态成为调用方法前的状态
Item 47:
不要忽略异常
不要catch块内什么都不做
Threads
Item 48:
同步访问共享可变的数据
private static int nextSerialNumber = 0;
//
需要同步
public static int generateSerialNumber() {
return nextSerialNumber++;
}
public class StoppableThread extends Thread {
private boolean stopRequested = false;
public void run() {
boolean done = false;
while (!stopRequested() && !done) {
//dosth
}
}
//
需要同步
public synchronized void requestStop() {
stopRequested = true;
}
private synchronized boolean stopRequested() {
return stopRequested;
}
}
Item 49:
避免过度使用同步
性能的下降
在同步块里尽可能少的操作
Item 50:
不要在循环外部调用wait
一般习惯的方法
synchronized (obj) {
while (<condition does not hold>)
obj.wait();
//Perform action appropriate to condition
}
Item 51:
不要依赖线程调度器
Item 52:
文档化线程安全
如果一个类支持线程安全的话,一定要文档说明
Item 53:
避免使用线程组
Serialization
Item 54:
谨慎实现Serializable
表面简单implements Serializable,实际上复杂
如果实现了,那私有或包私有的属性也会成为exported API的一部分,违背了隐藏内部实施细节的原则。
serialVersionUID
属性,如果代码中不指定的话,系统会根据类名,实现接口名,成员名称,生成一个
即使添加一个很普通的方法,也会改变该值,这样就失去兼容性。
Item 55:
考虑使用自定义的序列化格式
覆盖writeObject()和readObject()
Item 56:
保护性地编写readObject方法
存在安全漏洞,反序列化
Item 57:
必要时提供readResolve方法