2015-09-16-创建和销毁对象

第1条:考虑用静态工厂方法代替构造器

该规则的优势:

1.静态工厂方法有名称。构造器的参数本身没有确切描述正被返回的对象。当一个类需要多个带有相同签名的构造器时,用静态工厂方法代替。
2.不必每次调用它们都创建一个新对象。

3.可以返回原返回类型的任何子类型的对象。

4.在创建参数化类型实例的时候,他们使代码更见简洁。

(还是不太理解,等之后再看看)

静态工厂方法的缺点:

第2条:遇到多个构造参数时考虑用构建器

由于当有多个构造参数时,且大多数构造参数是可选的,如果使用构造器的话,会使代码难以阅读,不清楚构造器中的构造参数具体是什么。此时应该使用构建器。

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

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

    private int calories = 0;
    private int fat = 0;
    private int carbohydrate = 0;
    private int sodium = 0;


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

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

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

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

//NutritionFacts  cocaCola = new NutritionFacts.Builder(240,8).calories(100).sodium(35).carbohydrate(27).build();

需要注意的时,由于为了创建对象必须先创建它的构建器,在十分注重性能的时候,这可能会成为一个问题。

第3条:用私有构造器或者枚举类型强化Singleton属性

Singleton是指仅仅被实例化一次的类。
方法一:

public class Elvis{
       public static final Elvis INSTANCE = new Elvis();
       private Elvis(){...}
       ...
}

方法二:

public class Elvis{
       private static final Elvis INSTANCE = new Elvis();
       private Elvis(){...}

       public static Elvis getInstance(){return INSTANCE};

       ....
}

方法三(最佳):

public enum Elvis{
        INSTANCE;
    ...
}

通过私有构造器强化不可实例化的能力

有些工具类不希望被实例化,但是如果没有显示的构造器(自己没写构造器)的话,编译器会自动提供一个公有的、无参的缺省构造器,那么这时候我们就需要通过构造一个私有的构造器来强化这种能力。

public class UtilityClass{
       private UtilityClass(){

       }
}

由上面我们知道,添加私有构造器的主要原因有两个:

1.希望类具有不可实例化的能力

2.没有显示构造器时,编译器会自动提供一个公有的无参的缺省构造器。

(私有的构造器也是显示的)

这种用法的副作用是,它使得一个类不能子类化,即其他类不能继承该类。原因是所有子类都显示/隐式的调用夫类的构造器。

避免创建不必要的对象

明显的,当我们创建的对象越多,对程序的性能也有一定的影响。因此应该避免创建不必要的对象。如下面例子:

publci class Person{
       private final Date birthDate;

       public boolean isBabyBoomer(){
          Calendar gmtCal =
                   Calendar.getInstance(TimeZon.getTimeZon("GMT"));
              gmt.set(1946,Calendar.JANUARY,1,0,0,0);
          Date boomStart = gmtCal.getTime();
          gmt.set(1965,Calendar.JANUARY,1,0,0,0);
          Date boomEnd = gmtCal.getTime();
          return birthDate.compare(boomStart)>= 0 &&
                 birthDate.compare(boomEnd)>=0;
       }
}

改进

publci class Person{
       private final Date birthDate;
       private static final Date BOOM_START;
       private static final Date BOOM_END;

       static{
        Calendar gmtCal =
                   Calendar.getInstance(TimeZon.getTimeZon("GMT"));
              gmt.set(1946,Calendar.JANUARY,1,0,0,0);
          BOOM_START = gmtCal.getTime();
          gmt.set(1965,Calendar.JANUARY,1,0,0,0);
          BOOM_END = gmtCal.getTime();

       }

       public boolean isBabyBoomer(){
      return birthDate.compare(boomStart)>= 0 &&
                 birthDate.compare(boomEnd)>=0;
       }
}

同时需要注意的是:要优先使用基本类型而不是装箱基本类型,要当心无意识的自动装箱。如long,Long

消除过期的对象引用

在Java中具有垃圾回收的功能,但是有时候也会造成内存泄露。所谓过期的对象引用是指永远也不会再被解除的引用。

public class stack{
       private Object[] elements;
       private int size = 0;
       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];
          /*改进
          Object result = elements[--size];
          elements[size] = null;
          return result;
          */
       }
       public void ensureCapa(){
        if(elements.length == size)
            elements = Array.copyOf(elements, 2*siz+1);
       }
}

这里造成内存泄露的原因是当调用pop函数时,虽然栈的程序不再引用这些对象,但是这些对象也不会被回收,从而造成过期引用。
清楚过期引用的另一个好处是,如果以后它们又被错误的解除引用,程序会立即抛出NullPointerException异常,而不是悄悄的错误下去。

只要类是自己管理内存的,那么程序员就应该警惕内存泄露的问题。
内存泄露的另一个常见的来源是缓存。
第三个常见的来源是监听器和其他回调。

避免使用终结方法

Java中的终结方法不同于C++的析构器,Java中的终结方法通常是不可预测的,也是很危险的,一般情况下是不必要的。

版权声明:本文为博主原创文章,未经博主允许不得转载。

posted @ 2015-10-02 00:20  mlhy  阅读(116)  评论(0编辑  收藏  举报