设计模式——使用枚举实现单例模式
普通的Java类的反序列化过程中,会通过反射调用类的默认构造函数来初始化对象。而我们定义的一个枚举,在第一次被真正用到的时候,会被虚拟机加载并初始化,而这个初始化过程是线程安全的。而我们知道,解决单例的并发问题,主要解决的就是初始化过程中的线程安全问题。使用非枚举的方式实现单例,都要自己来保证线程安全,所以,这就导致其他方法必然是比较臃肿的。网上的单例模式已经讲了很多,特别是双重检测锁实现单例模式
工作中常用的 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)
更多推荐
所有评论(0)