看单例模式视频时,发现老师通过在构造方法里面增加判断的方式防止被反射,但是实际测试代码中,发现如果先反射,然后再用getInstance方法,获取的对象不是同一个,请大神帮忙看看,谢谢~
public class DoubleCheckLazySingleton {
private DoubleCheckLazySingleton() throws Exception {
if (instance != null) {
throw new Exception("单例不允许被反射");
}
}
private static DoubleCheckLazySingleton instance;
public static DoubleCheckLazySingleton getInstance() throws Exception {
if (instance == null) {
synchronized (DoubleCheckLazySingleton.class) {
if (instance == null) {
instance = new DoubleCheckLazySingleton();
}
}
}
return instance;
}
}
测试方法
public class DestroySingleByReflect {
public static void main(String[] args) throws Exception {
DoubleCheckLazySingleton oinstance = null;
DoubleCheckLazySingleton oinstance2 = null;
DoubleCheckLazySingleton oinstance3 = null;
Class clazz = DoubleCheckLazySingleton.class;
try {
Constructor c = clazz.getDeclaredConstructor();
c.setAccessible(true);
oinstance = (DoubleCheckLazySingleton) c.newInstance();
oinstance2 = (DoubleCheckLazySingleton) c.newInstance();
oinstance3 = (DoubleCheckLazySingleton) c.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
DoubleCheckLazySingleton instance = DoubleCheckLazySingleton.getInstance();
System.out.println(oinstance);
System.out.println(oinstance2);
System.out.println(oinstance3);
System.out.println(instance);
}
}
测试结果
com.xx.singleton.lazy.DoubleCheckLazySingleton@1b6d3586
com.xx.singleton.lazy.DoubleCheckLazySingleton@4554617c
com.xx.singleton.lazy.DoubleCheckLazySingleton@74a14482
com.xx.singleton.lazy.DoubleCheckLazySingleton@1540e19d
从调式的角度来看,当从反射的入口进来时,instance当时的值为null,直接执行成功,创建好实例对象,可此时并未将此实例对象赋值给类中的instance变量,而只是在jvm中存在该实例对象。而通过getInstance的方式进来后,该类下的变量的值的确还是为null。所以最终返回两个内存地址是正确的。
这个跟类加载和jvm内存模型有关,目前理解不深。只能简单聊下,虽然变量通过static 修饰,但也只是提前在jvm中分配一个地址放变量,而通过默认构造器也只是实例个对象,两者之间并没有引用关系。所以单例中的双重校验也无法正确校验。