今天看第三条:使用私有构造方法或枚类强化 Singleton 属性。
文章首先介绍了两种常见的实现单例的方法。第一种是用final修饰的常量。
// Singleton with public final field
public class Elvis {
public static final Elvis INSTANCE = new Elvis();
private Elvis() { ... }
public void leaveTheBuilding() { ... }
}
这样因为构造器是私有的,所以别人无法在创建出对应的实例。接着作者提醒采用反射的方法还是可以调用私有方法构造出实例,所以还要在构造器中加以判断来以防万一。这一点我是没有想到的,虽然平时自己也写过反射,但是在这种时候就联想不到反射可能带来的危害,以后需要更加重视反射。
接着第二种方法是用一个静态的工厂来实现单例。
// 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() { ... }
}
同样是私有的构造器,这次连实例对象也是私有的了,都靠getInstance的静态方法来实现细节。它相比前一种方法就更灵活,在不改变API的前提下,就可以改变该类是否单例的实现。其次它可以通过方法引用(method reference)作为提供者,例如 Elvis::instance 等同于 Supplier<Elvis> 。
最后作者还介绍了java1.5以后支持的用单例来实现singleton的方法。
// Enum singleton - the preferred approach
public enum Elvis {
INSTANCE;
public void leaveTheBuilding() { ... }
}
这种方式类似于公共属性方法,但更简洁,无偿地提供了序列化机制,并提供了防止多个实例化的坚固保证,即使是在复杂的序列化或反射攻击的情况下。这种方法可能感觉有点不自然,但是单一元素枚举类通常是实现单例的最佳方式。但是如果单例必须继承 Enum 以外的父类(尽管可以声明一个Enum 来实现接口),那么就不能使用这种方法。
想想我们系统中的单例,一般常见的都是通过 getInstance 方法来获取的,好像很少看到通过单一元素枚举来实现的,确实这样做需要新建一个枚举类,可能更为麻烦,有机会自己可以实现一个这样的单例来比较一下。
网友评论