第一章是引言,所以这里不做笔记,总结一下书中第一章的主要内容是向我们解释了这本书所做的事情:指导Java程序员如何编写出清晰、正确、可用、健壮、灵活和可维护的程序。
2.1考虑用静态工厂方法代替构造器
静态工厂方法与构造器相比有四大优势:
(1)静态工厂方法有名称,具有适当名称的静态工厂方法易于使用、易于阅读;
(2)不必每次在调用它们的时候都创建一个新的对象;
(3)可以返回原返回类型的任何子类型的对象;
(4)在创建参数化类型实例的时候,它们使代码变得更加简洁。
同时静态工厂方法也有两大缺点:
(1)类如果不含公有的或者受保护的构造器,就不能被子类化;
(2)它们与其它静态方法实际上没有任何区别。
静态工厂方法的一些惯用名称如下:
valueOf------该方法返回的实例与它的参数具有相同的值,实际上是类型转换方法;
getInstance------返回的实例是通过方法的参数来描述的,对于单例模式(Singleton)来说,该方法无参数,并返回唯一的实例;
newInstance------功能同getInstance,但与getInstance不同的是,它能够确保返回的每个实例都与其它实例不同。
因此在写程序的时候我们可以优先考虑静态工厂方法,然后再考虑构造器。
2.2遇到多个构造器参数时要考虑用构建器
如果类的构造器或者静态工厂中具有多个参数,设计这种类时,Builder模式就是种不错的选择。
其与传统构造器的优势是易于阅读和编写,其与JavaBeans的优势是安全(它能保证所有的可选参数都有默认值)。
书中的这个例子非常的好,我们可以从中体会到使用Builder模式的方法和好处:
<span style="font-family:Comic Sans MS;font-size:18px;">public class NutritionFacts {
private final int servingSize; //(ml) required
private final int servings; //(per container) required
private final int colories; // optional
private final int fat; //(g) optional
private final int sodium; //(mg) optional
private final int carbohydrate; //(g) optional
public static class Builder{
//required parameters
private final int servingSize;
private final int servings;
//optional parameters ------initialized to default values
private int colories = 0;
private int fat = 0;
private int sodium = 0;
private int carbohydrate = 0;
public Builder(int servingSize, int servings){
this.servingSize = servingSize;
this.servings = servings;
}
public Builder setColories(int val){
this.colories = val;
return this;
}
public Builder setFat(int val){
this.fat = val;
return this;
}
public Builder setSodium(int val){
this.sodium = val;
return this;
}
public Builder setCarbohydrate(int val){
this.carbohydrate = val;
return this;
}
public NutritionFacts build(){
return new NutritionFacts(this);
}
}
private NutritionFacts(Builder builder){
this.servingSize = builder.servingSize;
this.servings = builder.servings;
this.colories = builder.colories;
this.fat = builder.fat;
this.sodium = builder.sodium;
this.carbohydrate = builder.carbohydrate;
}
}
</span>
这样写了之后我们在别的地方去新建一个NutritionFacts实例,方法参数变短,并可设置任意个可选参数,且构造过程非常安全,比如写这样的语句去创建新的NutitionFacts实例:
<span style="font-family:Comic Sans MS;font-size:18px;">NutritionFacts nutritionFacts = new NutritionFacts.Builder(240, 8)
.setCarbohydrate(10).build();</span>
2.3用私有构造器或者枚举类型强化Singleton属性
例子代码如下:
<span style="font-family:Comic Sans MS;font-size:18px;">public class Elvis {
private static final Elvis INSTANCE = new Elvis();
private Elvis(){
}
public static Elvis getInstance() {
return INSTANCE;
}
}</span>
私有构造器仅被调用一次,用来实例化public的静态final域INSTANCE,由于没有public和protected构造器,所以保证了Elvis的全局唯一性。
2.4通过私有构造器强化不可实例化的能力
当我们在写一个工具类的时候,我们为这个工具类定义了很多静态方法,我们通过“类名.方法名”这种方式来使用这个类的功能,所以我们不希望这个类能被实例化,但是如果我们不定义构造器,Java编译器会自动的提供一个public无参的缺省构造器给这个类,所以这个时候我们为这个工具类创建一个私有构造器,来强化其不可实例化的能力,示例代码如下:
<span style="font-family:Comic Sans MS;font-size:18px;">//Noninstantiable utility class
public class UtilityClass {
//Suppress default constructor for noninstantiability
private UtilityClass(){
throw new AssertionError();
}
}
</span>
由于构造器是private,所以在类的外部不能访问它,AssertionError()可以避免不小心在类的内部调用构造器,它保证该类在任何情况下都不会被实例化。
2.5避免创建不必要的对象
一段示例代码如下:
<span style="font-family:Comic Sans MS;font-size:18px;">public class Person {
private Date birthDate;
//other fields,methods,and constructor omitted
public boolean isBabyBoomer(){
Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
gmtCal.set(1946, Calendar.JANUARY,1,0,0,0);
Date boomStart = gmtCal.getTime();
gmtCal.set(1965, Calendar.JANUARY,1,0,0,0);
Date boomEnd = gmtCal.getTime();
return birthDate.compareTo(boomStart)>=0 && birthDate.compareTo(boomEnd)<0;
}
}</span>
像boomStart和boomEnd这两个对象在程序中是不会改变的对象,但写成局部变量,每次调用isBabyBoomer()方法的时候都会去创建两个对象,所以这个时候用一个静态初始化器,避免这种低效率的情况的发生:
<span style="font-family:Comic Sans MS;font-size:18px;">public class NewPerson {
private Date birthDate;
private static final Date boomStart;
private static final Date boomEnd;
static {
Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
gmtCal.set(1946, Calendar.JANUARY, 1, 0, 0, 0);
boomStart = gmtCal.getTime();
gmtCal.set(1965, Calendar.JANUARY, 1, 0, 0, 0);
boomEnd = gmtCal.getTime();
}
public boolean isBabyBoomer(){
return birthDate.compareTo(boomStart)>=0 && birthDate.compareTo(boomEnd)<0;
}
}</span>
另外在写代码的时候,如果不需要用到包装器类型,尽量使用基本类型来做,而不要使用包装器类型,这样程序的效率更高。
2.6消除过期的对象引用
<span style="font-family:Comic Sans MS;font-size:18px;">public class Stack {
private Object[] elements;
private int size;
private static final int DEFAULT_INITIAL_CAPACITY = 16;
public Stack(){
elements = new Object[DEFAULT_INITIAL_CAPACITY];
}
public void push(Object e){
ensureCapacity();
elements[size++] = e;
}
public Object pop(){
if(size == 0){
throw new EmptyStackException();
}
return elements[--size];
}
/**
* Ensure space for at least one more element
*/
private void ensureCapacity(){
if(elements.length == size){
elements = Arrays.copyOf(elements, 2 * size + 1);
}
}
}</span>
栈内部维护着过期引用,有可能引起泄漏,因此这里将代码改为:
<span style="font-family:Comic Sans MS;font-size:18px;">public Object pop(){
if(size == 0){
throw new EmptyStackException();
}
Object result = elements[--size];
elements[size] = null;
return result;
}</span>
清空过期引用。
2.7避免使用终结方法
终结方法(finally)通常是不可预测的,是很危险的,一般情况下是不必要的。使用终结方法会导致行为不稳定、降低性能,以及可移植性问题。除非作为安全网,或者是为了终止非关键的本地资源,否则请不要使用终结方法。