双重检验与静态内部类两种方法都可以实现延迟加载的单例模式。但是无法阻止反射破坏单例,因为反射可以无视修饰权限,直接调用构造方法创建对象,下面是一个例子:

package ThreadTest;

public class Singleton {

private Singleton(){

}
static class SingletonGen{
private static final Singleton instance = new Singleton();
}

public static Singleton getInstance(){
return SingletonGen.instance;
}


public static void main(String args[]){
Singleton s = Singleton.getInstance();
try {
Class clazz = Class.forName("ThreadTest.Singleton");
Singleton sss = (Singleton) clazz.newInstance();

System.out.println(s.hashCode());
System.out.println(sss.hashCode());
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}

最终两个对象的hashcode不同,说明是两个对象。

那么如何解决反射破坏呢?可以类内创建一个标志变量,在构造方法内部检测该标志变量,使得构造方法只执行一次。但是这个检测必须 是线程安全的,必须同步,那么是类锁还是对象锁?很显然是类锁,因为多线程中的反射是以类为单位进行的,所以需要类锁。

private static boolean flag = false;

private Singleton(){
synchronized (Singleton.class) {
if(!flag){
flag = true;
}else{
throw new RuntimeException("单例被破坏");
}
}
}


这样,不论以什么方式,只要是通过构造方法来破坏单例,都会被阻止。