单例模式防反射及性能(二)

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

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方法等待其他线程执行完。

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

 

posted @ 2018-03-12 11:27  kwdeep  阅读(173)  评论(0编辑  收藏  举报