Effective Java 读书笔记( 2 创建和销毁对象)

来源:互联网 发布:如何随机生成一组数据 编辑:程序博客网 时间:2024/06/14 10:09

第一章是引言,所以这里不做笔记,总结一下书中第一章的主要内容是向我们解释了这本书所做的事情:指导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)requiredprivate final int servings;//(per container)requiredprivate final int colories;//optionalprivate final int fat;//(g)optionalprivate final int sodium;//(mg)optionalprivate final int carbohydrate;//(g)optionalpublic static class Builder{//required parametersprivate final int servingSize;private final int servings;//optional parameters ------initialized to default valuesprivate 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 classpublic class UtilityClass {//Suppress default constructor for noninstantiabilityprivate 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 omittedpublic 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)通常是不可预测的,是很危险的,一般情况下是不必要的。使用终结方法会导致行为不稳定、降低性能,以及可移植性问题。除非作为安全网,或者是为了终止非关键的本地资源,否则请不要使用终结方法。

1 0
原创粉丝点击