Singleton只不过是指仅仅实例化一次的类。Singleton通常被用来代表那些本质上唯一的系统组件,比如窗口管理器或者文件系统。使类成为Singleton会使它的客户端测试变得十分困难,因为无法给Singleton替换模拟实现,除非它实现一个充当其类型的接口。
单例的实现
在[Java]1.5发行版本之前,实现Singleton有两种方法。这两种方法都要把构造器保持为私有的,并导出公有的静态成员,以便允许客户端能够访问该类的唯一实例。
第一种:公有静态final成员
public class Singleton {
public static final Singleton INSTANCE = new Singleton();
private Singleton() {
// 初始化操作
}
public void execute() {
System.out.println("execute Singleton");
}
}
但是,这种写法可以通过反射机制调用私有的构造器。
Class singletonClass = Class.forName("effactive.java.eff003.Singleton");
Constructor constructor = singletonClass.getDeclaredConstructor();
constructor.setAccessible(Boolean.TRUE);
Singleton singleton = (Singleton) constructor.newInstance();
singleton.execute();
为了避免反射机制调用私有的构造器需要在修改私有的构造器,当试图创建第二个实例是抛出异常。
private Singleton() {
if (INSTANCE != null){
throw new IllegalStateException("Already instantiated");
}
}
这样,在创建第二个实例是就会抛出异常。保证始终只有一个实例。
第二种:公有的静态工厂方法
public class Singleton2 {
private static final Singleton2 INSTANCE = new Singleton2();
private Singleton2() {
if (INSTANCE != null){
throw new IllegalStateException("Already instantiated");
}
}
public static Singleton2 getInstance(){
return INSTANCE;
}
public void execute() {
System.out.println("execute Singleton2");
}
}
静态工厂方法要比第一种公有静态final成员灵活一些。可以在不改变API的前提下,改变该类是否是单例的想法。但是,这种写法仍可以通过反射机制调用私有的构造器。
在Java 1.5之后我们有第三种。
第三种:单个元素的枚举类型
public enum Singleton3 {
INSTANCE;
public void execute() {
System.out.println("execute Singleton3");
}
}
使用的话:
Singleton3.INSTANCE.execute();
由于Java的枚举类型实现了Serializable接口,默认是可以序列化的,而且还能包证反序列化之后不会重新创建一个实例。
单例的序列化
如果我们将单例序列化,那么当我们反序列化,还会单例吗?
对于第一种和第二种来说,反序列化之后,我们相当与重新创建了一个新的实例。不能再保证单例了。
对于第三种,由于JAVA在枚举类型反序列化时候与一般类的不一样,可以保证反序列化之后的依然是单例。
下面我们来解决第一种和第二种反序列化的问题。
public class Singleton4 implements Serializable{
private static final Singleton4 INSTANCE = new Singleton4();
private Singleton4() {
if (INSTANCE != null){
throw new IllegalStateException("Already instantiated");
}
}
public static Singleton4 getInstance(){
return INSTANCE;
}
//需要该方法来保证反序列化后仍为同一对象
private Object readResolve() {
return Singleton4.INSTANCE;
}
public void execute() {
System.out.println("execute Singleton4");
}
}
下面是测试的代码
File file = new File("/home/pj/person.out");
ObjectOutputStream oout = new ObjectOutputStream(new FileOutputStream(file));
oout.writeObject(Singleton4.getInstance());
oout.close();
ObjectInputStream oin = new ObjectInputStream(new FileInputStream(file));
Singleton4 singleton4 = (Singleton4) oin.readObject();
oin.close();
System.out.println(Singleton4.getInstance());
System.out.println(singleton4);
System.out.println(Singleton4.getInstance() == singleton4);
总结:
对比来看,单元素的枚举类型应该是实现单例的最佳方式了。
网友评论