《Effective Java》读书笔记一(创建与销毁对象)

No1 考虑用静态工厂方法代替构造器

静态工厂方法优势:

  1. 它们有名称,阅读性增强,如:BigInteger.probablePrime;
  2. 不必每次调用它们的时候都创建一个新对象;
  3. 它们可以返回原返回类型的任何子类型的对象;
  4. 在创建参数化类型实例的时候,它们使代码变得更加简洁。

静态工厂方法缺点:

  1. 类如果不含公有的或者受保护的构造器,就不能被子类化;
  2. 它们与其他的静态方法实际上没有任何区别。

惯用名称:

  • getInstance       返回的实例是通过方法的参数来描述的;
  • newInstance     像getInstance一样,但newInstance能够确保返回的每个实例都与所有其它实例不同。

No2 遇到多个构造器参数时要考虑用构建器

2.1多个构造器(缺点:许多参数时,客户端代码难写;不易于阅读);

// Telescoping constructor pattern - does not scale well! - Pages 11-12

public class NutritionFacts {
    private final int servingSize;   // (mL)            required
    private final int servings;      // (per container) required
    private final int calories;      //                 optional
    private final int fat;           // (g)             optional
    private final int sodium;        // (mg)            optional
    private final int carbohydrate;  // (g)             optional

    public NutritionFacts(int servingSize, int servings) {
        this(servingSize, servings, 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, servings, calories, fat, 0);
    }

    public NutritionFacts(int servingSize, int servings,
            int calories, int fat, int sodium) {
        this(servingSize, servings, calories, fat, sodium, 0);
    }

    public NutritionFacts(int servingSize, int servings,
           int calories, int fat, int sodium, int carbohydrate) {
        this.servingSize  = servingSize;
        this.servings     = servings;
        this.calories     = calories;
        this.fat          = fat;
        this.sodium       = sodium;
        this.carbohydrate = carbohydrate;
    }

    public static void main(String[] args) {
        NutritionFacts cocaCola =
            new NutritionFacts(240, 8, 100, 0, 35, 27);
    }
}

2.2 JavaBeans模式(缺点:对象可能处于不一致的状态);

// JavaBeans Pattern - allows inconsistency, mandates mutability - Pages 12-13

public class NutritionFacts {
    // Parameters initialized to default values (if any)
    private int servingSize  = -1;  // Required; no default value
    private int servings     = -1;  //     "     "     "      "
    private int calories     = 0;
    private int fat          = 0;
    private int sodium       = 0;
    private int carbohydrate = 0;

    public NutritionFacts() { }

    // Setters
    public void setServingSize(int val)  { servingSize = val; }
    public void setServings(int val)     { servings = val; }
    public void setCalories(int val)     { calories = val; }
    public void setFat(int val)          { fat = val; }
    public void setSodium(int val)       { sodium = val; }
    public void setCarbohydrate(int val) { carbohydrate = val; }

    public static void main(String[] args) {
        NutritionFacts cocaCola = new NutritionFacts();
        cocaCola.setServingSize(240);
        cocaCola.setServings(8);
        cocaCola.setCalories(100);
        cocaCola.setSodium(35);
        cocaCola.setCarbohydrate(27);
    }
}

2.3 Builder模式(用一个专门的对象来存放参数,形如:new NutritionFacts(Builder builder),所有参数封装在Builder类中)。

// Builder Pattern - Pages 14-15

public class NutritionFacts {
    private final int servingSize;
    private final int servings;
    private final int calories;
    private final int fat;
    private final int sodium;
    private final int carbohydrate;

    public static class Builder {
        // Required parameters
        private final int servingSize;
        private final int servings;

        // Optional parameters - initialized to default values
        private int calories      = 0;
        private int fat           = 0;
        private int carbohydrate  = 0;
        private int sodium        = 0;

        public Builder(int servingSize, int servings) {
            this.servingSize = servingSize;
            this.servings    = servings;
        }

        public Builder calories(int val)
            { calories = val;      return this; }
        public Builder fat(int val)
            { fat = val;           return this; }
        public Builder carbohydrate(int val)
            { carbohydrate = val;  return this; }
        public Builder sodium(int val)
            { sodium = val;        return this; }

        public NutritionFacts build() {
            return new NutritionFacts(this);
        }
    }

    private NutritionFacts(Builder builder) {
        servingSize  = builder.servingSize;
        servings     = builder.servings;
        calories     = builder.calories;
        fat          = builder.fat;
        sodium       = builder.sodium;
        carbohydrate = builder.carbohydrate;
    }

    public static void main(String[] args) {
        NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8).
            calories(100).sodium(35).carbohydrate(27).build();
    }
}

No5 避免创建不必要的对象

5.1 String对象的使用

String s = new String(“stringetee”);         // DON’T DO THIS
String s = “stringetee”;   // 正确方式,引用常量池中的对象

5.2 注意某些方法调用频繁生成对象时,应考虑将这些对象放置到类(对象)的局部变量中。

不好的代码如下:

// Creates lots of unnecessary duplicate objects - page 20-21
import java.util.*;
 

public class Person {

    private final Date birthDate;
 

    public Person(Date birthDate) {
        // Defensive copy - see Item 39
        this.birthDate = new Date(birthDate.getTime());
    }
 

    // Other fields, methods omitted
 

    // DON'T DO THIS!
    public boolean isBabyBoomer() {
        // Unnecessary allocation of expensive object
        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;
    }
}

 优化后的代码如下:

// Doesn't creates unnecessary duplicate objects - page 21
import java.util.*;
 

class Person {

    private final Date birthDate;
 

    public Person(Date birthDate) {
        // Defensive copy - see Item 39
        this.birthDate = new Date(birthDate.getTime());
    }
 

    // Other fields, methods
 

    /**
     * The starting and ending dates of the baby boom.
     */
    private static final Date BOOM_START;
    private static final Date BOOM_END;

    static {
        Calendar gmtCal =
            Calendar.getInstance(TimeZone.getTimeZone("GMT"));
        gmtCal.set(1946, Calendar.JANUARY, 1, 0, 0, 0);
        BOOM_START = gmtCal.getTime();
        gmtCal.set(1965, Calendar.JANUARY, 1, 0, 0, 0);
        BOOM_END = gmtCal.getTime();
    }
 

    public boolean isBabyBoomer() {
        return birthDate.compareTo(BOOM_START) >= 0 &&
               birthDate.compareTo(BOOM_END)   <  0;
    }
}

5.3 JDK1.5之后,自动装箱功能可能导致代码BUG。

如下代码:

// Hideously slow program! Can you spot the object creation?
public static void main(String[] args) {
    Long sum = 0L;

for (long i = 0; i < Integer.MAX_VALUE; i++) { sum += i; } System.out.println(sum); }

这段程序算出的结果是正确的,但是比实际情况要更慢一些,只因为打错了一个字符。变量sum被声明成Long而不是long。要优先使用基本类型而不是装箱基本类型,要当心无意意识的自动装箱。

No7 避免使用终结方法

Java语言规范不仅不保证终结方法会被及时地执行,而且根本就不保证它们会被执行。

显式终止方法的典型盒子是InputStream,OutputStream和java.sql.Connection上的close方法。另一个例子是java.util.Timer上的cancel方法,它执行必须的状态改变,使得与Timer实例相关联的该线程温和地终止自己。

显式的终止方法通常与try-finally结构结合起来使用,以确保及时终止。

posted @ 2013-08-08 11:23  那些年的事儿  阅读(388)  评论(0编辑  收藏  举报