反射破坏单例模式解决方案
一、前言
如何利用反射破坏单例模式,以及如何防止反射机制破坏单例模式的解决方案。
如有想了解单例模式的实现方式(饿汉式、懒汉式、登记式、ThreadLocal 线程单例),请前往以下链接:
在此记录一下,分享给大家。
二、反射破坏单例 - 代码演示
/**
* 描述:懒汉式单例 - 静态内部类
* 优点:线程安全,兼顾饿汉式的内存浪费,也兼顾synchronized锁的性能问题
* 缺点:通过反射机制可以破坏单例
*
* @author yys
*/
public class LazyInnerClassSingleton {
// 私有构造,防止外界new对象
private LazyInnerClassSingleton() {}
// 关键字作用:static 是为了使单例的空间共享 / final 保证这个方法不会被重写,重载
public static final LazyInnerClassSingleton getInstance() {
// 在结果返回以前,会先加载内部类
return LazyHolder.LAZY;
}
// 默认不加载
private static class LazyHolder {
// 这里其实是饿汉式单例
private static final LazyInnerClassSingleton LAZY = new LazyInnerClassSingleton();
}
}
/**
* 描述:反射破坏单例测试
* @author yys
*/
public class LazyInnerClassSingletonTest {
public static void main(String[] args) {
try {
// 获取class对象
Class<?> clazz = LazyInnerClassSingleton.class;
// 通过反射拿到私有构造方法
Constructor<?> c = clazz.getDeclaredConstructor(null);
// private修饰:暴力反射
c.setAccessible(true);
// 调用构造,第一次
Object o1 = c.newInstance();
System.out.println(o1);
// 调用构造,第二次
Object o2 = c.newInstance();
System.out.println(o2);
System.out.println(o1 == o2);
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 描述:结果显示
* @author yys
*/
com.yys.demo.design.patterns.singleton.LazyInnerClassSingleton@7adf9f5f
com.yys.demo.design.patterns.singleton.LazyInnerClassSingleton@85ede7b
false
// 总结:通过结果可以看出,通过反射机制(违背单例模式只有一个实例的原则)成功破坏单例模式。
三、防止反射破坏单例 - 解决方案
/**
* 描述:懒汉式单例 - 静态内部类
* 优点:线程安全,兼顾饿汉式的内存浪费,也兼顾synchronized锁的性能问题
* 缺点:通过反射机制可以破坏单例
*
* 反射机制破坏单例解决方案:在构造方法中做一些限制,一旦出现多次重复创建,则直接抛出异常
*
* @author yys
*/
public class LazyInnerClassSingleton {
// 私有构造,防止外界new对象
private LazyInnerClassSingleton() {
/**
* 添加如下代码 - 防止反射机制破坏单例
*/
if(null != LazyHolder.LAZY) {
throw new RuntimeException("不允许通过反射机制创建多个实例");
}
}
// 关键字作用:static 是为了使单例的空间共享 / final 保证这个方法不会被重写,重载
public static final LazyInnerClassSingleton getInstance() {
// 在结果返回以前,会先加载内部类
return LazyHolder.LAZY;
}
// 默认不加载
private static class LazyHolder {
// 这里其实是饿汉式单例
private static final LazyInnerClassSingleton LAZY = new LazyInnerClassSingleton();
}
}
// 再次进行测试...
扫描二维码关注公众号,回复: 10813358 查看本文章
// 总结:通过在构造方法中做一些限制,一旦出现多次重复创建,则直接抛出异常。
四、枚举单例模式 - 得天独厚防止反射机制破坏单例
/**
* 描述:枚举式单例
* @author yys
*/
public enum EnumSingleton {
// 本质为饿汉式单例的实现
INSTANCE;
private Object data;
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public static EnumSingleton getInstance() {
return INSTANCE;
}
}
1、无参构造方式
/**
* 描述:反射破坏单例测试 - 无参构造测试
* @author yys
*/
public class EnumSingletonTest {
public static void main(String[] args) {
try {
/**
* 测试 - (无参构造)反射是否破坏单例
* java.lang.NoSuchMethodException: com.yys.demo.design.patterns.singleton.EnumSingleton.<init>() at java.lang.Class.getConstructor0 :没有找到无参的构造方法
*/
Class clazz = EnumSingleton.class;
Constructor c = clazz.getDeclaredConstructor();
c.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
}
}
// 进行测试...
// 总结:异常信息表明:枚举中并没有无参构造,故得天独厚的防止了无参构造反射获取实例。
// 查看枚举类源码,发现只有一个有参构造方法。jdk.1.8源码如下:
/**
* Sole constructor. Programmers cannot invoke this constructor.
* It is for use by code emitted by the compiler in response to
* enum type declarations.
*
* @param name - The name of this enum constant, which is the identifier
* used to declare it.
* @param ordinal - The ordinal of this enumeration constant (its position
* in the enum declaration, where the initial constant is assigned
* an ordinal of zero).
*/
protected Enum(String name, int ordinal) {
this.name = name;
this.ordinal = ordinal;
}
2、有参构造方式
/**
* 描述:反射破坏单例测试 - 有参构造测试
* @author yys
*/
public class EnumSingletonTest {
public static void main(String[] args) {
try {
/**
* 测试 - (有参构造)反射是否破坏单例
* java.lang.IllegalArgumentException: Cannot reflectively create enum objects : 不能用反射来创建枚举类型
*
* 在JDK1.8源码中,newInstance()方法中,416行, 判断如果是Modifier.ENUM枚举类型,直接抛出异常(源码强制性判断,为枚举式单例保驾护航)
*/
Class clazz2 = EnumSingleton.class;
Constructor c2 = clazz2.getDeclaredConstructor(String.class, int.class);
c2.setAccessible(true);
c2.newInstance("yys", 013);
} catch (Exception e) {
e.printStackTrace();
}
}
}
// 再次进行测试...
// 总结:异常信息表明:不能用反射来创建枚举类型
// 本质原因说明:在JDK1.8源码中,newInstance()方法中,416行,判断如果是Modifier.ENUM枚举类型,直接抛出异常(源码强制性判断,为枚举式单例保驾护航)
// 源码如下:
@CallerSensitive
public T newInstance(Object ... initargs)
throws InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException
{
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, null, modifiers);
}
}
if ((clazz.getModifiers() & Modifier.ENUM) != 0)
throw new IllegalArgumentException("Cannot reflectively create enum objects");
ConstructorAccessor ca = constructorAccessor; // read volatile
if (ca == null) {
ca = acquireConstructorAccessor();
}
@SuppressWarnings("unchecked")
T inst = (T) ca.newInstance(initargs);
return inst;
}
// 判断如果是Modifier.ENUM枚举类型,直接抛出异常
// 源码强制性判断,为枚举式单例保驾护航
if ((clazz.getModifiers() & Modifier.ENUM) != 0)
throw new IllegalArgumentException("Cannot reflectively create enum objects");
// 总结:枚举单例模式,天生就可以防止反射机制破坏单例。
Now ~ ~ ~写到这里,就写完了,如果有幸帮助到你,请记得关注我,共同一起见证我们的成长。