EffectiveJava学习笔记(一)

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

  静态工厂方法是返回一个类的实例的静态方法(此处介绍的静态工厂方法并不对应设计模式中的工厂模式),例:基本类型boolean转化为包装类:

public static Boolean valueOf(boolean b) {
    return b ? Boolean.TRUE : Boolean.FALSE;
}

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

  1. 静态工厂方法有名称,代码易于阅读。构造方法可以重载,但是函数名同类名,无法区分具体实现功能。所以一个类需要多个相同签名的构造器时,应提供静态工厂方法,同时函数名上加以区分。
  2. 静态工厂方法不必在每次调用时都创建一个新的对象。
  3. 可以返回原类型的任何子类型对象。应用:接口返回对象,同时不使对象的类变成公有的。
  4. 返回对象的类可以随着参数不同而改变,只要是已声明返回类型的子类型都是允许的。例:EnumSet没有公有的构造器,只有静态工厂方法,在noneOf静态工厂方法中,判断底层枚举类型的大小,返回不同类型的实例,同时不会影响客户端。
    public static extends Enum> EnumSet noneOf(Class elementType) {
            Enum[] universe = getUniverse(elementType);
            if (universe == null)
                throw new ClassCastException(elementType + " not an enum");
    
            if (universe.length <= 64)
                return new RegularEnumSet<>(elementType, universe);
            else
                return new JumboEnumSet<>(elementType, universe);
        }
  5. 方法返回对象的所属的类,在编写该静态方法时可以不存在。

  静态工厂方法的缺点:

  1. 类如果不含有公有的或者受保护的构造器,将不可以被继承。
  2. 静态工厂方法难以被发现。常用函数名:from、of、valueOf、instance、create、gatType、newType、type。

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

  构建器解决了构造器和静态工厂方法的局限性:当一个类有大量的可选参数时,需要重写多个构造器,代码难以维护。同时也有人提出使用JavaBean的方式(即setParam()),但是这种方式有一个很大的缺陷就是,构造过程被分到了许多调用中,JavaBean可能会处于数据不一致的状态。并且JavaBean模式把类做成不可变的可能性不复存在,需要额外保证它的线程安全。

  构造器模式实现:

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 {

        private final int servingSize;
        private final int servings;

        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);
        }
    }

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

    NutritionFacts nutritionFacts = new NutritionFacts.Builder(10,10)
            .calories(5)
            .carbohydrate(5)
            .fat(100)
            .sodium(7)
            .build();
}

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

  Singleton是指仅被实例化一次的类,通常用来代表一个无状态的对象,或本质上唯一的系统组件,实现Singleton的常用方式是将构造器变为私有。如下两种方式:

class Singleton{

    public static final Singleton INSTANCE = new Singleton();

    private Singleton(){ }
}

class Singleton2{

    private static final Singleton2 INSTANCE2 = new Singleton2();

    private Singleton2(){ }

    public static Singleton2 getInstance(){
        return INSTANCE2;
    }
}

  在方法一中,如果客户端使用setAccessible(true)方法调用私有的构造方法,实例的唯一性将无法保证,此时可以在构造器中判断,如已存在当前类的实例则抛出异常。如下:

public class SingletonDemo {

    public static void main(String[] args) throws Exception {

        Singleton singleton1 = Singleton.INSTANCE;
        Singleton singleton2 = Singleton.INSTANCE;
        Constructor constructor = Singleton.class.getDeclaredConstructor();
        constructor.setAccessible(true);
        Singleton singleton3 = constructor.newInstance();
        System.out.println(singleton1 + "~" + singleton2 + "~" + singleton3);
    }
}

class Singleton{

    public static final Singleton INSTANCE = new Singleton();

    private Singleton(){
        if(Objects.nonNull(INSTANCE)){
            throw new RuntimeException("实例已存在!!");
        }
    }
}

   如果要将Singleton类支持序列化,仅仅实现Serializable接口是不够的,为了保证每次反序列化时不构造一个新的Singleton对象,需要提供readResolve方法。

  第三种也是最佳的实现Singleton类的方法是声明一个包含单个元素的枚举类型,这种方式更加简洁,并且提供了序列化机制,避免重复生成实例。

 

你可能感兴趣的:(EffectiveJava学习笔记(一))