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