创建对象和销毁对象

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

  静态工厂方法:只是一个返回类实例的静态方法。

例:

  public static Boolean valueOf(boolean b){

    return b ? Boolean.TRUE : Boolean.FALSE;

  }

  提供静态工厂方法而不是公有的构造器的优势:

  1.静态工厂方法与构造器不同的第一大优势在于,它们有名称。(避免了多个构造器带来的参数过多,不方便记忆和使用)

  2.静态工厂方法与构造器不同的第二大优势在于,不必在每次调用它们的时候都创建一个新对象。(例如:单例)

  例:Boolean.valueOf(boolean);

  实例受控的类:原因:确保唯一或不可实例化。

  3.静态工厂方法与构造器不同的第三大优势在于,它们可以返回原返回类型的任何子类类型的对象。

  4.静态工厂方法与构造器不同的第四大优势在于,在创建参数化类型实例的时候,它们使代码变得更加简洁。

  复杂的:Map<String, List<String>> m = new HashMap<String, List<String>>();

  假设HashMap提供了如下的静态工厂:

  public static <K, V> HashMap<K, V> newInstance(){

    return new HashMap<K, V>();

  }

  Map<String, List<String>> m = HashMap.newInstance();

  5.静态工厂方法的主要缺点在于,类如果不含公有的或者受保护的构造器,就不能被子类化

  6.静态工厂方法的第二个缺点在于,它们与其他的静态方法实际上没有任何区别。

  valueOf、of、getInstance、newInstance、getType、newType

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

  静态工厂和构造器有个共同的局限性:它们都不能很好地扩展到大量的可选参数。

  面对必须的参数和可选的参数的构造器:

  1.习惯采用重叠构造器

  写多个构造器,第一个是必须参数的构造器,第二个是必须参数加上一个可选参数组成的构造器,第三个是必须参数加上两个个可选参数组成的构造器....

  重叠构造器模式可行,但是当有许多参数的时候,客户端代码会比较难写,并且难以阅读。

  2.采用javaBeans模式

  创建一个无参的构造器,然后利用setter方法来设置必须的参数以及每个相关的可选参数。  

  javaBeans模式自身的缺点:

    1.构造过程被分到几个调用中,在构造过程中JavaBean可能处于不一致的状态。类无法仅仅通过检验构造器参数的有效性来保证一致性。

    2.JavaBeans模式阻止了类做成不可变的可能。这可能需要程序员付出额外的努力来确保它的线程安全。

  3.采用Builder模式

  既能保证像重叠构造器模式那样的安全性,也能保证像JavaBeans模式那么好的可读性。 不直接生成想要的对象,而是让客户端利用所有必要的参数调用构造器(或静态工厂),得到一个builder对象。然后客户端在builser对象上调用类似于setter的方法,来设置每个相关的可选参数。最后,客户端调用午餐的builder方法来生成不可变的对象。

示例:

package unit.test.no2;

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 sodium       = 0;
        private int carbohydrate = 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 sodium(int val) {
            sodium =val;
            return this;
        }
        public Builder carbohydrate(int val) {
            carbohydrate =val;
            return this;
        }
        
        public NutritionFacts build() {
            return new NutritionFacts(this);
        }
        
    }
    
    
    public NutritionFacts(Builder builder) {
        servingSize = builder.servingSize;
        servings = builder.servings;
        calories = builder.calories;
        fat = builder.fat;
        sodium = builder.sodium;
        carbohydrate = builder.carbohydrate;
    }
    
}
View Code

客户端代码:

  NutritionFactscocaCola = new NutritionFacts.Builder(240,8).calories(100).sodium(35).build();

  builder像个构造器一样,可以对其参数强加约束条件。builder方法可以检验这些约束条件。将参数从builder拷贝到对象之后,并在对象域而不是builder域中对他们进行检验,这一点很重要,如果违反任何约束条件,build方法就应该抛出IllegalStateException。

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

  Singleton指仅仅被实例化一次的类。

  1.公有静态成员是个final域

  public class Elvis{

    public static final Elvis  INSTANCE = new Elvis();

    private Elvis(){...}

    public void leaveTheBuilding(){...}

  }  .

  私有构造器仅被调用一次,用来实例化公有的静态final域Elvis.INSTANCE.

  提醒:享有特权的客户端可借助AccessibleObject.setAccesssible方法,通过反射机制调用私有的构造器。如果想抵御这种攻击,可以修改构造器,让它在被要求创建第二个示例的时候抛出异常。

  2.公有成员是个静态工厂方法

  public class Elvis{

    private static final Elvis  INSTANCE = new Elvis();

    public Elvis(){...}

    public static Elvis getInstance(){ return INSTANCE;}

    public void leaveTheBuilding(){...}

  }

  唯一的优势是提供了灵活性:在不改变API的前提下,我们可以改变该类是否应该为Singleton的想法。  也要考虑第一种的被攻击的情况,抛除其他不相干的优势,第一种public域的方法比较简单。

  当Singleton类变成可序列化的(Serializable)时候?

  直接implements Serializable时,反序列化时都会创建一个新的实例,与Singleton的单例想法相违背。为了维护并保证Singleton,可以参考以下方法:

  1.implements Serializable,并且声明所有的实例域都是瞬时transient的,并提供一个readResolve方法。

  private Object readResolve(){

    return INSTANCE;

  }

  a. transient:仅存在调用者的内存中,而不会写到磁盘里持久化。 (常用在不想序列化的敏感字段(密码,账号等))

  b. readRosolve()是序列化操作提供的一个钩子(hook)---类中具有一个私有的被实例化的方法readResolve()。这个方法确保类开发人员在反序列化会返回怎样的object上具有发言权,虽不是静态的,但反序列化创建实例时会被调用。

  2.从java1.5发行版起,实现Singleton还有第三种方法,只需要编写一个包含单个元素的枚举类型。

  public enum Elvis{

    INSTANCE;

    public void leaveTheBuilding(){...}

  }

  这种方法在功能上与公有域方法相近,但是更加简洁,而且无偿提供了序列化机制,绝对防止多次实例化。可以不在考虑面对复杂的序列化或者反射攻击。 单元素的枚举类型已经成为实现Singleton的最佳方法。

参考博客:

  Java transient关键字使用小记  http://www.cnblogs.com/lanxuezaipiao/p/3369962.html

  序列化-理解readResolve()       https://blog.csdn.net/fg2006/article/details/6409423

  单例模式的八种写法比较     https://www.cnblogs.com/zhaoyan001/p/6365064.html

猜你喜欢

转载自www.cnblogs.com/muxi0407/p/9030460.html