zoukankan      html  css  js  c++  java
  • 单例模式防反射及性能(二)

    单例模式的目的是创建一个对象,但是反射的方式,或者使用反序列的方式,就会对这种目的造成威胁,那么我们先来看看如何使用反射,如何使用反序列化,创建构造函数私有化的对象,以及我们如何防止反序列化创建对象。

    1.补充:如何选用单例模式

     (1)占用资源少,不需要延迟加载的,一般使用的是枚举和饿汉式,但是枚举比饿汉式安全。

     (2)占用资源大,需要延迟加载,一般使用静态内部类和懒汉式,静态内部类好于懒汉式,因为他更加的懒汉式、线程安全、调用的效率高。

    2.使用反射破解的时候,一般是不包括枚举的,所以他的安全性是恒爱的。

    3.方式一:反射破解

    package kw.test.sjms;
    
    import java.io.Serializable;
    
    /*
     * 懒汉式防止破解,原始单例程序
     */
    public class FDemoLH implements Serializable{
        private static FDemoLH instance ;
    
        private FDemoLH()
        {}
        
        public synchronized static FDemoLH  getinstance(){
            if(instance == null)
            {
                instance = new FDemoLH();
            }
            return instance;
        }
    }

    破解程序

    package kw.test.sjms;
    
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import java.lang.reflect.Constructor;
    
    public class FClient {
        public static void main(String[] args) throws Exception {
          /*
           * 反射的方法调用私有的构造函数。
           */
            FDemoLH fDemoLH1 = FDemoLH.getinstance();
            FDemoLH fDemoLH2 = FDemoLH.getinstance();
            System.out.println(fDemoLH1 == fDemoLH2);
            
            String className = "kw.test.sjms.FClient";
            Class<FDemoLH> clazz = (Class<FDemoLH>)Class.forName("kw.test.sjms.FDemoLH");
            Constructor<FDemoLH> c= clazz.getDeclaredConstructor(null);
            c.setAccessible(true); //跳过检查
            FDemoLH fd1 = c.newInstance();
            FDemoLH fd2 = c.newInstance();
            
            System.out.println(fd1);
            System.out.println(fd2);
            
        }
    }

      方式二:反序列化破解

    package kw.test.sjms;
    
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import java.lang.reflect.Constructor;
    
    public class FClient {
        public static void main(String[] args) throws Exception {        
            /*
             * 序列化
             */
            FDemoLH fDemoLH1 = FDemoLH.getinstance();
            System.out.println(fDemoLH1);
            FileOutputStream fileOutputStream = new FileOutputStream("D:/a.txt");
            ObjectOutputStream oos = new ObjectOutputStream(fileOutputStream);
            oos.writeObject(fDemoLH1);
            oos.close();
            fileOutputStream.close();
            /*
             * 反序列化
             */
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:/a.txt"));
            FDemoLH fDemoLH = (FDemoLH)ois.readObject();
            System.out.println(fDemoLH);
        }
    }

    其他的单例方式类似

    4.防止破解的方式

    package kw.test.sjms;
    
    import java.io.Serializable;
    
    /*
     * 懒汉式防止破解
     */
    public class FDemoLH implements Serializable{
        private static FDemoLH instance ;
    
        private FDemoLH()
        {}
        
        public synchronized static FDemoLH  getinstance(){
            if(instance == null)
            {
                instance = new FDemoLH();
            }
            return instance;
        }
        
        /*
         * 此方法是私有的,我尝试过公有,好像是不起作用的
         * */
        private Object readResolve()  
        {
            return instance;
        }
    }

    5.下来测试效率,模拟多线程,打印出每种方式占用的时间。或者使用CountDownLatch类,他是一个多线程的辅助类,观察其他线程有没有执行完。它的内部有一个计算器,当一个线程执行完了就会将计数器减一。Wait方法等待其他线程执行完。

    最终的结果是饿汉式最快、枚举、静态内部类、双重检测式、懒汉式依次变慢。

  • 相关阅读:
    二叉树
    消息
    线性表 及Java实现 顺序表、链表、栈、队列
    Memcache简介
    redis例子
    redis简介
    Android客户端采用Http 协议Post方式请求与服务端进行数据交互(转)
    jQueryValidate实现重复性验证
    mybatis中${}和#{}的区别
    List转换为数组Array的方法
  • 原文地址:https://www.cnblogs.com/kw28188151/p/8548338.html
Copyright © 2011-2022 走看看