首先我们还是拿饿汉模式作为栗子进行测试,饿汉模式的代码如下:
public class HungrySingleton implements Serializable { private static final HungrySingleton instance; static { instance = new HungrySingleton(); } private HungrySingleton(){ } public static HungrySingleton getInstance(){ return instance; } private Object readResolve(){ return instance; }
1、先写一个利用反射获取实例的方法和直接获取实例的方法,将两者的返回值进行比较,看返回的是否是一个实例。
代码如下:
public class Testreflection { public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { Class object = HungrySingleton.class; Constructor constructor = object.getDeclaredConstructor(); constructor.setAccessible(true); HungrySingleton instance = HungrySingleton.getInstance(); HungrySingleton newInstance = (HungrySingleton) constructor.newInstance(); System.out.println(instance); System.out.println(newInstance); System.out.println(instance == newInstance); } }
运行后的结果为:可见,两者的结果并不是一个对象,则饿汉模式毅然受到了反射的攻击。
2、那么改如何解决这反射攻击呢?我们知道是在类加载的时候就加载了这个实例的,因为是在类加载的时候就生成了词实例,那么我们可以在构造器里面加一个判断,进行反射防御。代码如下:
测试结果为:
这种方式有一个特点,也就是它对类加载这个时刻就把对象创建好的这种类是ok的,静态内部类的单例也可以用。
对于不是静态类的也需要解决下,要根据创建实例的顺序进行解决。但是无论如何反射都可以访问到类的方法和变量,进行修改,所以非
类加载这个时刻就把对象创建好的这种类,是不能防止反射攻击的。