大家有没有发现,之前介绍的单例模式的构造方法除了加上 private 以外,没有做任何处理。如果我们使用反射来调用其构造方法,然后,再调用 getInstance()方法,应该就会两个不同的实例。
1.问题复现
现在来看一段测试代码,以 LazyInnerClassSingleton 为例:
public class LazyInnerClassSingletonTest {
public static void main(String[] args) {
try{
// 很无聊的情况下,进行破坏
Class<?> clazz = LazyInnerClassSingleton.class;
// 通过反射拿到私有的构造方法
Constructor c = clazz.getDeclaredConstructor(null);
// 强制访问,强吻,不愿意也要吻
c.setAccessible(true);
// 暴力初始化
Object o1 = c.newInstance();
// 调用了两次构造方法,相当于 new 了两次
// 犯了原则性问题!!!
Object o2 = c.newInstance();
System.out.println(o1 == o2);
// Object o2 = c.newInstance();
}catch (Exception e){
e.printStackTrace();
}
}
}
运行结果如下:
false
显然,是创建了两个不同的实例。
2.问题解决
现在,我们在其构造方法中做一些限制,一旦出现多次重复创建,则直接抛出异常。来看优化后的代码:
// 这种形式兼顾饿汉式的内存浪费,也兼顾 synchronized 性能问题
// 完美地屏蔽了这两个缺点
public class LazyInnerClassSingleton {
// 默认使用 LazyInnerClassGeneral 的时候,会先初始化内部类
// 如果没使用的话,内部类是不加载的
private LazyInnerClassSingleton(){
if(LazyHolder.LAZY != null){
throw new RuntimeException("不允许创建多个实例");
}
}
// 每一个关键字都不是多余的
// static 是为了使单例的空间共享
// final 保证这个方法不会被重写,重载
public static final LazyInnerClassSingleton getInstance(){
// 在返回结果以前,一定会先加载内部类
return LazyHolder.LAZY;
}
// 默认不加载
private static class LazyHolder{
private static final LazyInnerClassSingleton LAZY = new LazyInnerClassSingleton();
}
}
再运行测试代码,会得到以下结果:
本文标题:【设计模式】创建型:单例模式(二)反射破坏单例问题及解决方案
本文链接:https://blog.quwenai.cn/post/10174.html
版权声明:本文不使用任何协议授权,您可以任何形式自由转载或使用。







还没有评论,来说两句吧...