zoukankan      html  css  js  c++  java
  • 反射如何破坏单例模式

    一个单例类:

    
    
    public class Singleton {
        private static Singleton instance = new Singleton();   
    
        private Singleton() {} 
    
        public static Singleton getInstance() {
            return instance;
        }
    }
     

    通过反射破坏单例模式:

    
    
    public class Test {
        public static void main(String[] args) throws Exception{
            Singleton s1 = Singleton.getInstance();
    
            Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();
            constructor.setAccessible(true);
            Singleton s2 = constructor.newInstance();
    
            System.out.println(s1.hashCode());
            System.out.println(s2.hashCode());
    
        }
    }
    
    
    

      

    输出结果:
    671631440

    935563443

    结果表明s1和s2是两个不同的实例了。

    通过反射获得单例类的构造函数,由于该构造函数是private的,通过setAccessible(true)指示反射的对象在使用时应该取消 Java 语言访问检查,使得私有的构造函数能够被访问,这样使得单例模式失效。

     

    如果要抵御这种攻击,要防止构造函数被成功调用两次。需要在构造函数中对实例化次数进行统计,大于一次就抛出异常。

    
    
    public class Singleton {
        private static int count = 0;
    
        private static Singleton instance = null; 
    
        private Singleton(){
            synchronized (Singleton.class) {
                if(count > 0){
                    throw new RuntimeException("创建了两个实例");
                }
                count++;
            }
    
        }
    
        public static Singleton getInstance() {
            if(instance == null) {
                instance = new Singleton();
            }
            return instance;
        }
    
        public static void main(String[] args) throws Exception {
    
            Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();
            constructor.setAccessible(true);
            Singleton s1 = constructor.newInstance();
            Singleton s2 = constructor.newInstance();
        }
    
    }
    
    执行结果:
    Exception in thread "main" java.lang.reflect.InvocationTargetException
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
        at java.lang.reflect.Constructor.newInstance(Unknown Source)
        at com.yzz.reflect.Singleton.main(Singleton.java:33)
    Caused by: java.lang.RuntimeException: 创建了两个实例
        at com.yzz.reflect.Singleton.<init>(Singleton.java:14)
        ... 5 more
    
    
    
    在通过反射创建第二个实例时抛出异常,防止实例化多个对象。构造函数中的synchronized是为了防止多线程情况下实例化多个对象。
  • 相关阅读:
    rowid去重(转)
    Oracle中 row_number() over()分析函数(转)
    oracle分页计算公式
    vue 生产环境和线上环境配置
    vue postcss 样式等比缩放
    element-ui 表单输入手机号验证是否注册或者存在
    使用vue-qr 生成 二维码
    vue下载excel文件,后台传过来的是文件流解决办法
    前端请求接口发送的路径用域名代替ip
    将本地端口映射子域名
  • 原文地址:https://www.cnblogs.com/wyb628/p/6371827.html
Copyright © 2011-2022 走看看