第一章:代码应该被重用,而不是被拷贝。模块之间的依赖性应该尽可能地降到最小。错误应该尽早被检测出来,最好是在编译时刻。
第2条:处理多参数构造器
public class NutritionFacts {
private int servingSize;
private int servings;
private int calories;
private int fat;
public NutritionFacts(int servingSize, int servings) {
this(servingSize, servingSize, 0); //调用了有三个参数的构造函数
}
public NutritionFacts(int servingSize, int servings, int calories) {
this(servingSize, servings, calories, 0);
}
public NutritionFacts(int servingSize, int servings, int calories, int fat) {
this.servingSize = servingSize;
this.servings = servings;
this.calories = calories;
this.fat = fat;
}
}
第3条:实现Singleton的两种方式
①方式一
public class Elvis {
public static final Elvis INSTANCE = new Elvis();//public域
private Elvis(){//私有的构造函数
}
}
②方式二
public class Elvis {
private static final Elvis INSTANCE = new Elvis(); //private的域
private Elvis(){//私有的构造函数
}
public static Elvis getInstance(){
return INSTANCE;
}
}
第5条:避免创建不必要的对象
String s = new String("stringette"); //Don't do this
该语句每次被执行的时候都创建一个新的String实例,但是这些创建对象的动作全都是不必要的。传递给String构造器的参数"stringette"本身就是一个String实例,功能各方面等同于构造器创建的所有对象。
改进后的版本如下:
String s = "stringette";
基本类型和装箱基本类型:
public static void main(String[] args) {
Long sum = 0L; //这意味着程序会构造大约2的31次方个多余的Long实例
for (long i = 0; i < Integer.MAX_VALUE; i++) {
sum += i;
}
System.out.println(sum);
}
将sum的声明从Long改为long,运行时间从43秒减少到了6.8秒。结论很明确,要优先使用基本类型而不是装箱基本类型。
第6条:一般而言,只要类是自己管理内存,程序员就应该警惕内存泄露问题。一旦元素被释放掉,则该元素中包含的任何对象引用都应该被清空。
public Object pop() {
if (size == 0) {
throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null;// 清空对象引用
return result;
}
}
第8条:
①使用instanceof操作符检查“参数是否为正确的类型”
②覆盖equals时总要覆盖hashCode
第13条:使类和成员的可访问性最小化
第19条:接口应该只被用来定义类型,它们不应该被用来导出变量
第24条:消除非受检警告
可以用@SuppressWarnings("unchecked")注解来禁止警告
第41条:慎用重载
“能够重载方法”并不意味着就“应该重载方法”。一般情况下,对于多个具有相同参数数目的方法来说,应该尽量避免重载方法。
第43条:返回零长度的数组或者集合,而不是null
对于一个返回null而不是零长度数组或者集合的方法,客户端每次都要判断方法返回的值是否为null这种曲折的处理方式。
第45条:将局部变量的作用域最小化
要使局部变量的作用域最小化,最有力的方法就是在第一次使用它的地方声明。几乎每个局部变量的声明都应该包含一个初始化表达式。
第46条:for-each循环优先于传统的for循环
对集合和数组的遍历首选做法如下:
for(Element e : elements){
doSomething(e);
}
注意:这样做不会有性能上的损失,在某些情况下,比起普通的for循环,甚至还稍有性能优势。
第48条:如果需要精确的答案,请避免使用float和double
float和double类型尤其不适合用于货币计算。解决的办法是使用BigDecimal、int或者long进行货币计算。BigDecimal有两个缺点:与使用基本运算类型相比,这样做很不方便,而且很慢。在使用int和long时,需要自己处理十进制小数点。
另外,float和double都是线程不安全的。
第49条:基本类型优先于装箱基本类型
基本类型和装箱基本类型之间有三个主要区别:
①基本类型只有值,而装箱基本类型则具有与它们的值不同的同一性。
Comparator<Integer> naturalOrder = new Comparator<Integer>(){
public int compare(Integer first,Integer second){
return first < second ? -1 : (first == second);
}
};
对于上面这段代码,如果比较Integer(42)和Integer(42),这两个Integer实例都表示相同的值42,因此这个比较器应该返回0,而事实上,它的输出却为1。这是因为还要对这两个对象引用进行同一性比较。当程序使用==操作符比较两个装箱基本类型时,它做了个同一性比较,这几乎肯定不是你所希望的;
②基本类型只有功能完备的值,而每个装箱基本类型除了它对应基本类型的所有功能之外,还有个非功能值:null;
③基本类型通常比装箱基本类型更节省时间和空间。
第51条:当心字符串连接的性能
为连接n个字符串而重复地使用字符串连接操作符(+, String concatenation operator),需要n的平方级的时间。如果数量巨大的字符串连接,为了获得可以接受的性能,请使用StringBuilder替代String。
第52条:通过接口引用对象
应该优先使用接口而不是类来引用对象,这样会使程序更加灵活。
第66条:同步访问共享的可变数据
Java语言规范保证读或者写一个变量是原子的(atomic),但是long和double类型除外,这两个类型变量不是线程安全的。
增量操作符(++)不是原子的。
当多个线程共享可变数据的时候,每个读或者写数据的线程,都必须执行同步。
第69条:并发工具优先于wait和notify
java.util.concurrent中更高级的工具分成三类:Executor Framework、并发集合(Concurrent Collection. 如ConcurrentMap)以及同步器(Synchronizer)
对于间歇式的定时,始终应该优先使用 System.nanoTime,而不是使用System.currentTimeMills。 System.nanoTime更加准确也更加精确,它不受系统的实时时钟的调整所影响。
第72条:不要依赖于线程调度器
线程优先级是Java平台上最不可移植的特征了。