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

  • 采用私有构造器来强化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;
}
  

这里写图片描述

  • 利用枚举来强化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();即可。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值