vlambda博客
学习文章列表

单例模式,安全问题如何解决??


长按添加关注(猛男在线)

思考?

以上三种模式一定就是安全的嘛?

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)