用私有构造器或者枚举类型强化Singleton

参考Effective Java第三版 Joshua J. Bloch

参与编写JDK的大佬,上次看Collections的源码时看见了他的名字,然后翻了翻书,竟然就是他写的!

1.常见的一种:

public class Singleton {

private static final Singleton INSTANCE=new Singleton();

private Singleton(){
    //如果没有判断,可以通过反射使用构造函数创建对象,然后就不是单例了
    if (INSTANCE!=null){
        //throw Exception
    }
}

public static Singleton getInstance(){
    return INSTANCE;
}

public void doSomething(){
    //...
}

}

通过反射:可以看到singleton的两个实例不是同一个。

class Main {

public static void main(String[] args) {
    testSingleton();
}

private static void testSingleton() {

    Singleton s1 = Singleton.getInstance();
    Class<Singleton> clazz = Singleton.class;
    try {
        Constructor<Singleton> constructor = clazz.getDeclaredConstructor(new Class[]{});
        constructor.setAccessible(true);
        Singleton s2 = constructor.newInstance(new Class[]{});
        System.out.println(s1 == s2);
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InstantiationException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    }
}

}

2.用枚举:推荐的方法

优点:引用Effective Java的话:简洁,无偿的提供了序列化机制,绝对防止多次实例化,即使是在面对复杂的序列化或者反射攻击的时候。单元素的枚举类常是实现Singleton的最佳方法。如果Singleton必须扩展一个超类,而不是扩展Enum时,不适宜使用这个方法。

public enum EnumSingleton {

INSTANCE;

public void doSomething(){
    //...
}

}

按照第一个测试的时候会报错的。

3.序列化

序列化有个问题就是,反序列化时会创建一个新的实例,破坏单例,下面让原来那个类实现Serializable接口。

public class Singleton implements Serializable {

private static final Singleton INSTANCE=new Singleton();

private Singleton(){
    if (INSTANCE!=null){
        try {
            throw new Exception("INSTANCE已存在!");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

public static Singleton getInstance(){
    return INSTANCE;
}

public void doSomething(){
    //...
}

}

测试一下:Effective Java的第9条 使用try-with-resources优于try-finally,关闭资源的时候。

private static void testSerializableSingleton() {

    File file=new File("singleton.out");
    try(ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream(file));
        ObjectInputStream in=new ObjectInputStream(new FileInputStream(file))){

        out.writeObject(Singleton.getInstance());
        Singleton singleton= (Singleton) in.readObject();
        System.out.println(singleton == Singleton.getInstance());
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }

}

打印的结果是false,说明序列化破化了单例,因为反序列化是反射调用了无参构造函数。

解决方法:在类里加入这个方法,详见Effective Java第89条

private Object readResolve() {
return INSTANCE;
}
然后结果就是true了。

深圳网站建设:https://www.sz886.com

猜你喜欢

转载自blog.csdn.net/chenmh12/article/details/90019967
今日推荐