反射、枚举与单例
来源:互联网 发布:8个数据库设计典型实例 编辑:程序博客网 时间:2024/06/05 03:09
通常我们所使用的单例模式,我们都可以使用反射使它不再单例,如下饿汉式的单例模式:
public final class Singleton { private static final Singleton instance=new Singleton(); private Singleton(){ public static Singleton getInstance(){ return instance; }}
测试案例如下:
Singleton singleton1=Singleton.getInstance(); Singleton singleton2=Singleton.getInstance(); Constructor<Singleton> constructor=Singleton.class.getDeclaredConstructor(); constructor.setAccessible(true); Singleton singleton3=constructor.newInstance(); System.out.println(singleton1); System.out.println(singleton2); System.out.println(singleton3); System.out.println(singleton1==singleton2); System.out.println(singleton1==singleton3);
其中singleton1、singleton2都是通过我们所实现的单例模式来获取的对象,他们应该是同一个对象,singleton3则是通过反射获取无参构造器,constructor.setAccessible(true)来获取访问权限,最后通过无参构造器来创建一个对象singleton3,singleton3和上述两者应该不是同一个对象,测试结果如下:
com.lg.design.singleton.hungry.Singleton@15e3d24acom.lg.design.singleton.hungry.Singleton@15e3d24acom.lg.design.singleton.hungry.Singleton@20030380truefalse
所以说通常我们所使用的单例模式,我们都可以使用反射使它不再单例。然而单例使用枚举的话,却可以避免被反射。
单例如下:
public enum Singleton { instance; private Singleton(){}}
反射如下:
Singleton singleton1=Singleton.instance; Singleton singleton2=Singleton.instance; Constructor<Singleton> constructor=Singleton.class.getDeclaredConstructor(); constructor.setAccessible(true); Singleton singleton3=constructor.newInstance(); System.out.println(singleton1); System.out.println(singleton2); System.out.println(singleton3); System.out.println(singleton1==singleton2); System.out.println(singleton1==singleton3);
然后就报错:
Exception in thread "main" java.lang.NoSuchMethodException: com.lg.design.singleton.enumsingleton.Singleton.<init>() at java.lang.Class.getConstructor0(Class.java:2849) at java.lang.Class.getDeclaredConstructor(Class.java:2053) at com.lg.design.singleton.enumsingleton.Test.main(Test.java:14)
没有这个无参构造器,通过调试Singleton.class.getDeclaredConstructors()获取所有构造器,会发现并没有我们所设置的无参构造器,只有一个参数为(String.class,int.class)构造器,然后我们就可以明白了,这里的参数其实就是枚举的名字和所在枚举中位置,即枚举的name和ordinal两个属性,枚举的源码如下:
public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializable { private final String name; public final String name() { return name; } private final int ordinal; public final int ordinal() { return ordinal; } protected Enum(String name, int ordinal) { this.name = name; this.ordinal = ordinal; } public String toString() { return name; } //略}
枚举Enum是一个抽象类,一个类一旦声明为枚举,其实就是继承了Enum,所以会有(String.class,int.class)的构造器(就是父类Enum的构造器),具体的原理可以根据生成的字节码反编译后得知,可以参考这篇文章http://pf-miles.iteye.com/blog/187155#bc2340028。
既然是可以获取到父类Enum的构造器,那我们就使用该构造器看能不能创建出对象:
Singleton singleton1=Singleton.instance; Singleton singleton2=Singleton.instance; Constructor<Singleton> constructor=Singleton.class.getDeclaredConstructor(String.class,int.class); //Constructor<Singleton> constructor=Singleton.class.getDeclaredConstructor(); constructor.setAccessible(true); Singleton singleton3=constructor.newInstance("otherInstance",9); //Singleton singleton3=constructor.newInstance(); System.out.println(singleton1); System.out.println(singleton2); System.out.println(singleton3); System.out.println(singleton1==singleton2); System.out.println(singleton1==singleton3);
然后也报错:
Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects at java.lang.reflect.Constructor.newInstance(Constructor.java:521) at com.lg.design.singleton.enumsingleton.Test.main(Test.java:16)
之前的错是说没有构造器,这次我们能够拿到构造器了,只是在使用构造器执行newInstance(“otherInstance”,9)方法时抛出异常,说不能够反射枚举,具体源码如下:
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); } }//我们关注的重点,如果类含有ENUM修饰,调用该方法时直接报错 if ((clazz.getModifiers() & Modifier.ENUM) != 0) throw new IllegalArgumentException("Cannot reflectively create enum objects"); ConstructorAccessor ca = constructorAccessor; // read volatile if (ca == null) { ca = acquireConstructorAccessor(); } return (T) ca.newInstance(initargs); }
也就是说反射在通过newInstance创建对象时,会检查该类是否是枚举类,如果是,则抛出异常,反射失败。
也就是说使用枚举可以避免被反射,从而可以达到单例的效果。
- 反射、枚举与单例
- 单例与反射
- 单例与枚举类型
- 【Java】反射与单例
- 单例,枚举,反射,序列化--effectiveJava读书笔记
- 枚举单例模式如何防止反射攻击
- 枚举与单例和策略
- 枚举与单例和策略
- java 枚举的原理与使用枚举设计单例
- C#反射 与 枚举类型
- 黑马程序员-枚举与反射
- 黑马程序员--枚举与反射
- 黑马程序员---枚举与反射
- 黑马程序员 --- 枚举与反射
- 09_枚举与反射
- 黑马程序员——JavaSE之对单例枚举和反射的看法一
- 枚举实现单例
- Java 枚举单例
- MAC10.11 Python3.6 安装Scrapy
- python 爬虫入门(2) 爬虫基础知识 ; urllib 模块 ;urllib2 模块
- 习题 4.6 有一个函数:y=x (x<1) y=2x-1 (1<=x<10) y=3x-11 (x>=10) 写程序,输入x的值,输出y相应的值。
- 2017年数据科学15个最好用的Python库
- 装了Visual Studio 2013 Redistributable. MySQL安装失败
- 反射、枚举与单例
- 2017年下半年软考全国省市报名时间及报名网址
- hdu
- 笨办法学 Python · 续 练习 2:创造力
- c++-模板不支持分离编译错误分析
- Spring的IOC
- 在activity中回传数据
- kafka简介
- maven pom.xml配置文件