美文网首页
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