美文网首页
Effective Java - 用私有构造器或者枚举类型强化S

Effective Java - 用私有构造器或者枚举类型强化S

作者: DZQANN | 来源:发表于2022-05-03 22:40 被阅读0次

    使类成为Singleton会使得它的客户端代码测试变得困难,因为无法给它替换模拟实现,,除非它实现了一个充当其类型的接口。当使用DI框架的时候,一定给每一个注册的Component抽出来一个借口,方便客户端进行测试。

    Singleton暴露getInstance方法的好处

    书中提到的Singleton最好不要直接把INSTANCE对象暴露给外面,而是封装一个getInstance方法:

    public class Elvis {
        privaye static final Elvis INSTANCE = new Elvis();
    
        private Elvis() { }
    
        public static Elvis getInstance() {
          return INSTANCE;
        }
      
        public void leaveTheBuilding() {
         
        }
    }
    

    这样做有3袋内好处:

    1. 可以在不修改API的情况下改变是否该类为Singleton。具体的意思就是,可以在不修改getIntance的参数和返回值的情况下,通过修改实现控制是Application唯一还是线程唯一(ThreadLocal)

    2. 可以用来使用泛型Singleton工厂,这个内容不是很容易理解,书中提到了一个例子,Collections.emptySet方法:

      public static final <T> Set<T> emptySet() {
              return (Set<T>) EMPTY_SET;
      }
      public static final Set EMPTY_SET = new EmptySet<>();
      

      使用这个方法会发生警告,因为Set并不一定是Set<T>,但是对于空Set来说是无所谓的

    3. 可以通过方法引用作为提供者

      比如上面的Elivis::getInstance就是Supplier\<Elivis\>

    序列化问题

    序列化可以打破单例

    public class SingletonClass implements Serializable {
        private static final SingletonClass INSTANCE = new SingletonClass();
    
        private SingletonClass() {
        }
    
        public static SingletonClass getInstance() {
            return INSTANCE;
        }
    }
    
    public class Test {
    
        public static void main(String[] args) throws Exception {
            writeObject();
            Object obj = readObject();
            System.out.println(obj instanceof SingletonClass);
            System.out.println(SingletonClass.getInstance() == obj);
        }
    
        private static Object readObject() throws IOException, ClassNotFoundException {
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("test_singleton"));
            Object obj = ois.readObject();
            ois.close();
            return obj;
        }
    
        private static void writeObject() throws IOException {
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("test_singleton"));
            oos.writeObject(SingletonClass.getInstance());
            oos.flush();
            oos.close();
        }
    }
    

    上面那段代码是讲SingletonClass的instance序列化后再反序列化,程序的执行结果是:

    image.png

    可以看到,用了序列化之后就不能保证单例了。解决这个问题的方式就是给SingletonClass加一个readResolve方法:

    public class SingletonClass implements Serializable {
        private static final SingletonClass INSTANCE = new SingletonClass();
    
        private SingletonClass() {
        }
    
        public static SingletonClass getInstance() {
            return INSTANCE;
        }
    
        @Serial
        private Object readResolve() {
            return INSTANCE;
        }
    }
    

    这个时候再执行程序,结果就是一样的了:

    image.png

    单例的最佳方法

    单例模式的最佳方法是使用枚举:

    public enum Elvis {
        INSTANCE;
        
        public void leaveTheBuilding(){}
    }
    
    

    使用枚举的特性避免了线程安全和序列化问题

    相关文章

      网友评论

          本文标题:Effective Java - 用私有构造器或者枚举类型强化S

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