zoukankan      html  css  js  c++  java
  • Java单例---反射攻击单例和解决方法

    静态内部类中引出了反射攻击的问题

    import java.lang.reflect.Constructor;
    import java.lang.reflect.InvocationTargetException;
    
    public class Test1 {
    
        public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
            Class objClass = StaticInnerClass.class;
            //获取类的构造器
            Constructor constructor = objClass.getDeclaredConstructor();
            //把构造器私有权限放开
            constructor.setAccessible(true);
            //正常的获取实例方式
            StaticInnerClass staticInnerClass = StaticInnerClass.getInstance();
            //反射创建实例
            StaticInnerClass newStaticInnerClass = (StaticInnerClass) constructor.newInstance();
    
    
            System.out.println(staticInnerClass);
            System.out.println(newStaticInnerClass);
            System.out.println(staticInnerClass == newStaticInnerClass);
    
        }
    
    }

    上面这个代码的运行结果:
    com.ygz.designpatterns.singleton.StaticInnerClass@4d7e1886
    com.ygz.designpatterns.singleton.StaticInnerClass@3cd1a2f1
    false

    出现了两个不同的实例,这就违反了我们使用单例原则,不能保证只有一个实例,那么如何解决呢?

    public class StaticInnerClass {
    
        private static class InnerClass{
            private static StaticInnerClass staticInnerClass = new StaticInnerClass();
        }
    
        public static StaticInnerClass getInstance(){
            return InnerClass.staticInnerClass;
        }
    
        private StaticInnerClass(){
            //构造器判断,防止反射攻击,大家可以在下面这行if判断打断点来测试一下这个方法的过程,很好理解的
            if(InnerClass.staticInnerClass != null){
                throw new IllegalStateException();
            }
        }
    
    }

    序列化问题

    public class SerSingleton implements Serializable {
     2     private volatile static SerSingleton uniqueInstance;
     3     private  String content;
     4     public String getContent() {
     5         return content;
     6     }
     7 
     8     public void setContent(String content) {
     9         this.content = content;
    10     }
    11     private SerSingleton() {
    12     }
    13 
    14     public static SerSingleton getInstance() {
    15         if (uniqueInstance == null) {
    16             synchronized (SerSingleton.class) {
    17                 if (uniqueInstance == null) {
    18                     uniqueInstance = new SerSingleton();
    19                 }
    20             }
    21         }
    22         return uniqueInstance;
    23     }
    24 
    25     
    26     public static void main(String[] args) throws IOException, ClassNotFoundException {
    27         SerSingleton s = SerSingleton.getInstance();
    28         s.setContent("单例序列化");
    29         System.out.println("序列化前读取其中的内容:"+s.getContent());
    30         ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("SerSingleton.obj"));
    31         oos.writeObject(s);
    32         oos.flush();
    33         oos.close();
    34 
    35         FileInputStream fis = new FileInputStream("SerSingleton.obj");
    36         ObjectInputStream ois = new ObjectInputStream(fis);
    37         SerSingleton s1 = (SerSingleton)ois.readObject();
    38         ois.close();
    39         System.out.println(s+"
    "+s1);
    40         System.out.println("序列化后读取其中的内容:"+s1.getContent());
    41         System.out.println("序列化前后两个是否同一个:"+(s==s1));
    42     }
    43     
    44 }

    输出为:

    序列化前读取其中的内容:单例序列化
    com.lxp.pattern.singleton.SerSingleton@135fbaa4
    com.lxp.pattern.singleton.SerSingleton@58372a00
    序列化后读取其中的内容:单例序列化
    序列化前后两个是否同一个:false

     可以看出,序列化前后两个对象并不想等。为什么会出现这种问题呢?这个讲起来,又可以写一篇博客了,简单来说“任何一个readObject方法,不管是显式的还是默认的,它都会返回一个新建的实例,这个新建的实例不同于该类初始化时创建的实例。”当然,这个问题也是可以解决的,想详细了解的同学可以翻看《effective java》第77条:对于实例控制,枚举类型优于readResolve。

    避免序列化问题
    public enum  SerEnumSingleton implements Serializable {
     2     INSTANCE;
     3     private  String content;
     4     public String getContent() {
     5         return content;
     6     }
     7     public void setContent(String content) {
     8         this.content = content;
     9     }
    10     private SerEnumSingleton() {
    11     }
    12 
    13     public static void main(String[] args) throws IOException, ClassNotFoundException {
    14         SerEnumSingleton s = SerEnumSingleton.INSTANCE;
    15         s.setContent("枚举单例序列化");
    16         System.out.println("枚举序列化前读取其中的内容:"+s.getContent());
    17         ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("SerEnumSingleton.obj"));
    18         oos.writeObject(s);
    19         oos.flush();
    20         oos.close();
    21 
    22         FileInputStream fis = new FileInputStream("SerEnumSingleton.obj");
    23         ObjectInputStream ois = new ObjectInputStream(fis);
    24         SerEnumSingleton s1 = (SerEnumSingleton)ois.readObject();
    25         ois.close();
    26         System.out.println(s+"
    "+s1);
    27         System.out.println("枚举序列化后读取其中的内容:"+s1.getContent());
    28         System.out.println("枚举序列化前后两个是否同一个:"+(s==s1));
    29     }
    30 }

    运行结果如下:

    1 枚举序列化前读取其中的内容:枚举单例序列化
    2 INSTANCE
    3 INSTANCE
    4 枚举序列化后读取其中的内容:枚举单例序列化
    5 枚举序列化前后两个是否同一个:true



  • 相关阅读:
    EXTJS 4.2 资料 控件之checkboxgroup的用法(静态数据)
    EXTJS 4.2 资料 控件之Window窗体相关属性的用法
    EXTJS 4.2 资料 控件之textfield文本框加事件的用法
    Entity Framework 学习笔记(一)之数据模型 数据库
    EXTJS 4.2 资料 控件之checkboxgroup的用法(动态数据)
    EXTJS 4.2 资料 控件之Grid 列鼠标悬停提示
    Entity Framework 学习笔记(二)之数据模型 Model 使用过程
    EXTJS 4.2 资料 控件之radiogroup 的用法
    EXTJS API
    vue移动端弹框组件,vue-layer-mobile
  • 原文地址:https://www.cnblogs.com/wangzhanhua/p/10551073.html
Copyright © 2011-2022 走看看