工作中常用的 6 种设计模式
之前见到公司代码中的

DomainCfg domainCfg = MdmCommandFactory.INSTANCE.getDomain(domain);

来获取Domaincfg对象,感觉哪里怪怪的,一直没去看,今天有时间去研究了一波。

网上的单例模式已经讲了很多,特别是双重检测锁实现单例模式已经有很多文章分析了。
但是枚举方式都是一笔带过,平时也没怎么去看,今天记录一下。

首先对比双重校验实现

public class Singleton {  
    private volatile static Singleton singleton;  
    private Singleton (){}  
    public static Singleton getSingleton() {  
    if (singleton == null) {  
        synchronized (Singleton.class) {  
        if (singleton == null) {  
            singleton = new Singleton();  
        }  
        }  
    }  
    return singleton;  
    }  
}  

枚举实现

public enum Singleton {  
    INSTANCE;  
    public void whateverMethod() {  
    }  
} 

显然枚举代码简洁的不止一点点
优点1:

使用非枚举的方式实现单例,都要自己来保证线程安全,所以,这就导致其他方法必然是比较臃肿的。
而我们定义的一个枚举,在第一次被真正用到的时候,会被虚拟机加载并初始化,而这个初始化过程是线程安全的。而我们知道,解决单例的并发问题,主要解决的就是初始化过程中的线程安全问题。
所以,由于枚举的以上特性,枚举实现的单例是天生线程安全的。

优点2:

普通的Java类的反序列化过程中,会通过反射调用类的默认构造函数来初始化对象。所以,即使单例中构造函数是私有的,也会被反射给破坏掉。由于反序列化后的对象是重新new出来的,所以这就破坏了单例。
但是,枚举的反序列化并不是通过反射实现的。所以,也就不会发生由于反序列化导致的单例破坏问题。

所以,用枚举实现单例简单方便。举个获取用户的例子

public enum ObjFactory {
    /**
     * 单例
     */
    INSTANCE;

    private Map<String, User> userCfg = new ConcurrentHashMap<>();

    ObjFactory() {
        loadx();
    }

    private void loadx() {
        loadXX1();
        loadXX2();
    }
    private void loadXX2() {
    }
    private void loadXX1() {
        userCfg.put("boss",new User()
                .setName("悠哈")
                .setAge(3)
                .setEmail("111@qq.com"));
    }

    public User getUser(String key){
        User user = userCfg.get(key);
        return user;
    }




}
    @Test
    void test003(){
        User user1 = ObjFactory.INSTANCE.getUser("boss");
        User user2 = ObjFactory.INSTANCE.getUser("boss");
        System.out.println(user1==user2);
    }

相关文章
为什么我墙裂建议大家使用枚举来实现单例。
Java枚举实现单例模式原理
深度分析Java的枚举类型—-枚举的线程安全性及序列化问题 - 风动静泉 - 博客园 (cnblogs.com)

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐