zoukankan      html  css  js  c++  java
  • 用私有构造器或者枚举类型强化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了。

    不当之处,请指正,谢谢。

  • 相关阅读:
    Oracle 中用 update 语句更新timestamp字段的格式
    Oracle 获取本周、本月、本季、本年的第一天和最后一天
    Linux服务器下,java程序上传文件,中文名乱码或显示问号的解决办法
    Java实现 Oracle decode函数 转换为 MySQL 可用的 case when
    C# Ling to Sql 几种模糊查询
    机器学习学习笔记:sklearn.preprocessing.PolynomialFeatures偏置值inlude_bias设置,以及在Pipeline中的设置
    SQL Server更新表(用一张表的数据更新另一张表的数据)
    windows server 2012 R2里IIS配置.net core2.1遇到的坑
    combobox控件重新绑定后会出现下拉后显示值不变
    List<>使用之坑
  • 原文地址:https://www.cnblogs.com/KuroNJQ/p/10833947.html
Copyright © 2011-2022 走看看