Effective Java学习

java代码优化——用私有构造器或者枚举类型强化Singleto

2017-01-20  本文已影响66人  Ruheng

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

总结:

对比来看,单元素的枚举类型应该是实现单例的最佳方式了。

上一篇 下一篇

猜你喜欢

热点阅读