• 第3条:用私有构造器或者枚举类型强化Singleton属性


      1:采用私有构造器来强化Singleton属性

      顾名思义,即我们需要定义一个private的构造器,但要注意一点,即使我们定义了私有的构造器,但是客户端还是可以借助AccessibleObject.setAccessible方法,通过反射来调用私有的构造器,因此,我们需要修改构造器来抵御这种工具,下面代码很好阐述了这个。

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

      然而这样做就可以绝对防止出现多个实例了么?其实现在还有一种情况下会出现多个实例,那就是在你序列化这个对象之后,在进行反序列化,这个时候,你将再次得到一个新的对象,让我们看下例子,首先实现序列化接口

    public class Singleton implements Serializable{
                            ...
    }

      接着我们来看看序列化和反序列化后的情况

    public class SerializableTest {
    
          //序列化
          private static  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 static <T> T deserializable(String filename) throws IOException,
                                                  ClassNotFoundException {
              FileInputStream fis = new FileInputStream(filename);
              ObjectInputStream ois = new ObjectInputStream(fis);
              return (T) ois.readObject();
          }
          
          public static void main(String[] args) throws IOException, ClassNotFoundException {
            //序列化到文件test.txt中
            serializable(Singleton.getInstance(),"F://test.txt");
            //反序列化
            Singleton singleton = deserializable("F://test.txt");
            
            //比较反序列化后得到的singleton和Singleton.getInstance的地址是否一样。
            System.out.println(singleton);
            System.out.println(Singleton.getInstance());
        }
    }

      测试运行的结果如下图所示,因此,我们很明显可以看出得到的是两个不同的对象

      

      那么如何解决序列化问题呢,其实很简单,只要我们在Singleton中加入下面方法即可,为什么只需要加入readResolve就好了呢,因为任何一个readObject方法,不管显示还是默认的,它都会返回一个新建的实例,这就是为什么上面两个实例的地址是不一样的原因了,而加入了readResolve之后,那么在反序列化之后,新建对象上的readResolve方法会被调用,然后该方法返回的对象引用将取代新建的对象,指向新建对象的引用就不会保留下来,立即成为垃圾回收的对象了。

     private Object readResolve() {
            return INSTANCE;
     }

      

      

      2:利用枚举来强化Singleton(最优方案)

      枚举来强化方式很简单,也不会出现上面这些情况,利用单元素的枚举来实现单例(Singleton),绝对防止多次实例化,实现代码如下:

    public enum Elvis{
        INSTANCE;
              
        private String[] favoriteSongs = {"Hound Dog","Heartbreak Hotl"};
        public void printFavorites(){
             System.out.println(Arrays.toString(favoriteSongs ));
        }      
    }

      调用时只需要Elvis.INSTANCE.printFavorites();即可。

    作者:哀&RT
    出处:博客园哀&RT的技术博客--http://www.cnblogs.com/Tony-Anne/
    您的支持是对博主最大的鼓励,感谢您的认真阅读。
    本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    vi里面全局替换
    guanbi selinux
    ntop
    Java:求一个数组中连续子元素最大和
    LeetCode.643. 子数组最大平均数 I
    分治法-最大子数组问题
    Java实现最大连续子数组和
    golang xorm cmd xorm工具使用 reverse 反转一个数据库结构,生成代码
    golang中xorm的基本使用
    xorm入门
  • 原文地址:https://www.cnblogs.com/Tony-Anne/p/6694277.html
Copyright © 2020-2023  润新知