美文网首页程序员effectivejava
用私有构造器或者枚举来强化Singleton属性

用私有构造器或者枚举来强化Singleton属性

作者: 想飞的僵尸 | 来源:发表于2016-06-24 16:51 被阅读182次

    第三条:用私有构造器或者枚举来强化Singleton(单例)属性


    1. 用私有构造器来强化

    很简单,就是将构造器声明为private类型的,但是需要注意一点,享有特权的客户端可以利用反射机制来调用到私有的构造器,为了进一步确保单例的唯一性,我们可以在私有的构造方法中判断唯一实例是否存在,存在的话抛出异常,就像这样:

    public class Singleton {
            private static final Singleton INSTANCE = new Singleton();
    
            private Singleton() {
                if (INSTANCE != null) {
                    throw new UnsupportedOperationException("Instance already exist");
                }
            }
    
            public static Singleton getInstance() {
                return INSTANCE;
            }
    }
    

    这样做就可以绝对防止出现多个实例了么?NO!
    现在还有一种情况下会出现多个实例,那就是在你序列化这个对象之后,在进行反序列化,这个时候,你将再次得到一个新的对象,不信?来吧,代码说明一切:

    1).首先将上面的一段代码实现Serializable接口:

    public class Singleton implements Serializable{
                            ...
    }
    

    2).开始进行序列化测试,测试代码如下(保证简洁好理解,不保证严谨性):

    public class SerializableTest {
            @Test
            public void serializableTest() throws Exception{
                serializable(Singleton.getInstance(), "test");
                Singleton singleton = deserializable("test");
                Assert.assertEquals(singleton, Singleton.getInstance());
            }
    
            //序列化
            private void serializable(Singleton singleton, String filename) throws IOException {
                FileOutputStream fos = new FileOutputStream(filename);
                ObjectOutputStream oos = new ObjectOutputStream(fos);
                oos.writeObject(singleton);
                oos.flush();
            }
            //反序列化
            @SuppressWarnings("unchecked")
            private <T> T deserializable(String filename) throwsIOException,
                                                    ClassNotFoundException {
                FileInputStream fis = new FileInputStream(filename);
                ObjectInputStream ois = new ObjectInputStream(fis);
                return (T) ois.readObject();
            }
    }
    

    3). 运行测试:
    啊偶~报错了,得到了两个不同的对象,看图:


    测试结果.png

    从结果中可以看出,得到了两个不同的对象,一个Singleton@deb6432和一个Singleton@1b4fb997;
    好吧,解决方案当然是有的,你只要在单例类里面加上下面这个方法:

    private Object readResolve() {
                return INSTANCE;
    }
    

    在运行一遍测试代码,问题解决!

    2. 利用枚举来强化Singleton(最佳方案)

    利用单元素的枚举来实现单例(Singleton),绝对防止多次实例化,上面的问题在这里都不会出现,实现代码如下:

    public enum SingletonEnum {
            INSTANCE;
    
            private String filed01;
    
            public String getFiled01() {
                return filed01;
            }
            public void setFiled01(String filed01) {
                this.filed01 = filed01;
            }
    }
    

    使用的时候跟我们常用的单例方式也十分相似:

    SingletonEnum.INSTANCE.setFiled01("123");
    System.out.println(SingletonEnum.INSTANCE.getFiled01());
    

    运行结果就不用贴出来了,大家都懂。

    相关文章

      网友评论

        本文标题:用私有构造器或者枚举来强化Singleton属性

        本文链接:https://www.haomeiwen.com/subject/spvodttx.html