通过枚举实现单例模式
对于饿汉式单例模式和懒汉式单例模式了解的同学,使用以上两种单例模式,我们均可以通过反射的方式破坏单例模式的特性。那么,如何才能避免程序开发中使用反射的方式破坏单例的这种行为呢?接下来用过枚举的方式来创建的单例模式就可以避免上述的问题。
直接上代码:
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;
}
}
我们尝试使用反射的方式创建实例,看是否能够创建成功:
public class EnumSingletonTest {
public static void main(String[] args) {
EnumSingleton enumSingleton=EnumSingleton.getInstance();
enumSingleton.setData(new Object());
System.out.println("创建单例模式:"+enumSingleton);
try{
Class clazz= EnumSingleton.class;
Constructor c=clazz.getDeclaredConstructor(String.class,int.class);
c.setAccessible(true);
Object object=c.newInstance();
System.out.println(object);
}catch (Exception e){
e.printStackTrace();
}
}
}
会出现以下报错:
java.lang.IllegalArgumentException: Cannot reflectively create enum objects
at java.lang.reflect.Constructor.newInstance(Constructor.java:417)
at com.example.singleton.EnumSingletonTest.main(EnumSingletonTest.java:16)
我们直接定位到报错源码的位置(Constructor.java:417)发现如下内容:
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");
也就是说,在通过反射调用方法newInstance()创建实例时,源码会对class的修饰符做一个判断(clazz.getModifiers() & Modifier.ENUM) != 0)。如果创建实例的类是用enum修饰的,那么不能通过反射创建实例(Cannot reflectively create enum objects)