Effective Java 第三版

条目3:通过私有构造方法或者枚举创建单例

2018-02-12  本文已影响0人  lmtoo

由于无法模拟替换单例,使测试更加困难,除非单例实现了自己的接口。

有两种比较常见的方式来实现单例:都是通过私有构造器和暴露静态成员来访问实例的方式

第一种:final的静态成员变量

public class Elvis {

        public static final Elvis INSTANCE = new Elvis();

        private Elvis() { ... }

        public void leaveTheBuilding() { ... }

}

初始化静态成员变量INSTANCE 时只调用私有构造函数一次。

但是通过反射并且设置AccessibleObject.setAccessible可以创建多个实例。

如果不允许这样做可以在构造函数中做判断并抛出异常。

优点:

由于final类型的成员变量,所以它只会总是包含同一个对象的引用,并且很简单

第二种:提供静态工厂方法

// Singleton with static factory

public class Elvis {

        private static final Elvis INSTANCE = new Elvis();

        private Elvis() { ... }

        public static Elvis getInstance() { return INSTANCE; }

        public void leaveTheBuilding() { ... }

}

此方法也有同样的缺点:通过反射并且设置AccessibleObject.setAccessible可以创建多个实例。

优点:

可灵活的返回任何对象,而不用改变API;同时这个方式可以确保单个线程范围的单例

可以编写泛型的单例工厂

静态工厂可以作为一个Supplier:

Elvis::instance 是一个 Supplier<Elvis>

防止通过反序列化产生多个对象

对于可序列化的单例对象,可以设置实例字段为transient,并且提供readResolve方法

// readResolve method to preserve singleton property

private Object readResolve() {

    // Return the one true Elvis and let the garbage collector

    // take care of the Elvis impersonator.

    return INSTANCE;

}

第三种:单元素的枚举

// Enum singleton - the preferred approach

public enum Elvis {

    INSTANCE;

    public void leaveTheBuilding() { ... }

}

单元素的枚举是实现单例的最佳方式

上一篇下一篇

猜你喜欢

热点阅读