创建销毁对象(第三条:使用私有的构造器或者枚举类型来保证单例)

单例就是一个类只能被实例化一次。单例通常代表无状态的对象比如functionItem 24),或者系统某一组件,从本质上讲,这种组件是唯一的。将一个类设置为单例会使测试它的客户端变得艰难。因为想要用一个mock的实现类去代替单例是不可能的,除非这个单例实现了一个充当单例类型的接口(不是很懂,个人猜测可能是这个接口是单例类的实现接口。原文unless it implements an interfacethat serves as its type)。

通常有两种方式来实现单例。两个都是把构造方法设置成私有的,并且为访问这个唯一的对象提供一个公共的静态成员。在一个方法中,这个成员是final的类变量。

// Singleton with public final field
public class Elvis {
    public static final Elvis INSTANCE = new Elvis();

    private Elvis() {
    }

    public void leaveTheBuilding() {
    }
}

用来初始化公共的finalElvis.INSTANCE的私有构造器只被调用了一次。publicprotected构造器的缺失保证了“垄断的”领域:一旦Elvis被初始化,仅仅一个Elvis实例将会存在,不多不少。客户端做什么也改变不了,除非:在反射方法AccessibleObject.setAccessible的帮助下,有特权的客户端可以调用这个私有的构造方法。如果需要防御这种攻击的话,将构造器修改为:如果请求创建第二个实例,让构造方法抛出一个异常。

第二种用来实现单例的方法中,公共的成员是一个静态工厂方法:

// 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() {
    }
}

所有调用Elvis.getInstance返回相对的对象引用,不会有其他的Elvis对象被创建(除了上面提到的警告)。

提供公共参数的单例方法的主要优点是API清楚的声明了类是一个单例:因为公共的静态成员是final的,所以它永远都会只包含相同的对象引用。第二种方法的有点是它比较简单。静态工厂方法的一个优点是,对于要不要将类设置成单例并且不用改变API,它提供了灵活性。现在工厂方法返回这个唯一的实例,但是它可以被修改为每个线程调用它都返回一个不同的实例。第二个优点是,如果有必要可以写generic singleton factoryItem 30)。最后一个优点是,方法引用可以充当提供者去使用,比如Elvis::instance is aSupplier<Elvis>。除非涉及这三个中的某个优点,否则第一种方法是更好的。让一个使用这两种方法的某一个来实现的单例可序列化,仅仅在类的声明上加上implements Serializable是不够的。为了保证单例,将所有的成员参数生命为transient并且提供一个readResolve方法(Item 89)。否则,每次一个序列化的实例被反序列化时,一个新的对象就会被创建,就我们的例子而言,就会导致一个假的Elvis。使用readResolve方法来防止这类情况的发生:

//readResolve method to preserve singleton property
    privateObject readResolve() {
        //Return the one true Elvis and let the garbage collector

        //take care of the Elvis impersonator.
        returnINSTANCE;
    }

第三种实现单例的方式是声明一个单个成员的枚举:

public enum Elvis {INSTANCE;
    publicvoid leaveTheBuilding() {
    }
}


这种方法跟使用公共成员实现单例是相似的,但是它更加简洁,提供了免费的序列化机制,甚至面对复杂的序列化或者反射攻击也能提供绝对的单例的保证。这种方法可能会感觉怪怪得。但是单个元素的枚举通常是实现单例的最好的选择。注意如果你的单例必须继承一个父类而不是枚举那么你就不能用这种方法了(尽管你能声明一个枚举去实现接口)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值