单例模式,安全问题如何解决??
长按添加关注(猛男在线)
思考?
以上三种模式一定就是安全的嘛?
java中有一门技术可以破坏他的单例性-------反射
如图所示 两个对象指向的是同一个
利用反射来破坏他的单例性
public static void main(String[] args) throws Exception {
LazyMan instance = LazyMan.getInstance();
Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);//无视私有修饰
LazyMan instance2 = declaredConstructor.newInstance();
System.out.println("instance = " + instance);
System.out.println("instance2 = " + instance2);
}
运行结果如图
如何解决呢
private LazyMan(){
synchronized (LazyMan.class){
if (lazyMan!=null){
throw new RuntimeException("不要试图使用反射破坏单例-异常抛出");
}
}
}
在构造参数中添加synchronized 再次加锁
进行三次加锁
再次执行 抛出异常
单例解决了吗?还有没。
如果两次都使用反射来创建对象呢
两次都使用反射来创建对象 单例模式又被破坏了。
解决方法 可以通过创建一个标识位来标识类的创建
private static boolean yuanjian = false;
private LazyMan(){
synchronized (LazyMan.class){
if (yuanjian==false){
yuanjian = true;
}else{
throw new RuntimeException("不要试图使用反射破坏单例-异常抛出");
}
}
}
可以通过设置一个字段标识
再次运行结果:
这样解决了 通过反射创建对象的单例,但是
重点来了 但是!
他还是不安全的
假设通过某种方法(很多种方法比如反编译)知道了参数名还是可以通过反射来修改他
public static void main(String[] args) throws Exception {
//LazyMan instance = LazyMan.getInstance();
Field yuanjian = LazyMan.class.getDeclaredField("yuanjian");
yuanjian.setAccessible(true);//无视私有修饰符private
Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);//无视私有修饰符private
LazyMan instance = declaredConstructor.newInstance();
yuanjian.setBoolean(instance,false);
LazyMan instance2 = declaredConstructor.newInstance();
System.out.println("instance = " + instance);
System.out.println("instance2 = " + instance2);
}
运行结果如图
单例模式还是被破坏了。
以上关于反射所有的问题都可以用枚举(Enum)来解决
枚举反射不能破坏。
在反射的源码里有如果类是一个枚举类那么直接抛异常
使用枚举 没有无参构造
总结完,下回分析枚举
其他文章
(2089)