创建者模式 -- 单例模式(反射&序列化)

看的视频:https://www.bilibili.com/video/av43896218/?p=286

实现方式:(构造器私有,提供一个外部可以访问的方法(可以提供实例)

1、饿汉式:线程安全,调用效率高, 不能延时加载

2、懒汉式:线程安全,调用效率不高,可以延时加载(要用的时候才加载  )

3、双重锁检测式:“由于JVM底层内部模型原因,偶尔会出现问题,不建议使用”

4、静态内部类式:线程安全,调用效率高,且可以实现延时加载

(上面的四种方式都可以通过反射和反序列化 破坏 单例模式)

5、枚举:线程安全,调用效率高,不能延时加载,可以天然的防止反射和反序列化

如果单例对象占用资源少,不需要延时加载:

枚举式 优于 饿汉式

如果单例对象占用资源大, 需要延时加载:

静态内部类式 优于 懒汉式 

 

饿汉式:

/*
饿汉式: 最开始就已经把实例给创建好了
 */
public class Singleton01 {
    public static Singleton01 instance = new Singleton01();  //一开始就已经创建好了
    private Singleton01(){
    }
    public static Singleton01 getInstance(){
        return instance;
    }
}

懒汉式:

/*
懒汉式: 用的时候才会初始化实例   效率不高就是因为有synchronized这个关键字
 */
public class Singleton02 {
    private static Singleton02 instance = null;
    private Singleton02(){
    }
    public static synchronized Singleton02 getInstance(){
        if(instance == null){
            instance = new Singleton02();
        }
        return instance;
    }
}

静态内部类:

/*
静态内部类: 只有在主动调用这个类的时候才会加载   外部类加载的时候 静态内部类不会加载
 */
public class Singleton03 {
    static class Inner{
        private static Singleton03 instance = new Singleton03();
    }
    private Singleton03(){

    }
    public static Singleton03 getInstance(){
        return Inner.instance;
    }
}

枚举:

/*
枚举  天然的可以防止 反射和反序列化
 */
public enum Singleton04 {
    INSTANCE; //就表示一个实例 获取的时候直接  Singleton04.INSTANCE 
    public void method(){
        //这里可以写一些方法
    }
}

 

通过反射破坏单例模式:

public class Test01 {
    public static void main(String[] args) throws Exception{
            Class<?> clazz = Class.forName("com.test.Singleton01");
            //获取构造函数:
            Constructor c = clazz.getDeclaredConstructor();
            c.setAccessible(true); //对于私有属性和方法 设置为允许访问
            //通过构造函数 构造实例
            Singleton01 singleton001 = (Singleton01)c.newInstance();
            Singleton01 singleton002 = (Singleton01)c.newInstance();
            System.out.println(singleton001 == singleton002);  // false
    }
}

如何破坏反射:

public class Singleton01 {
    private static boolean  flag = false; //标志位
    public static Singleton01 instance = new Singleton01();  //一开始就已经创建好了
    // 上面的已经调用过一次 这个私有的构造函数了

private Singleton01(){ //反射的时候操作的就是这个私有的构造函数 synchronized (Singleton01.class){ if(flag == false){ flag = true; }else{ //表示第二次用构造函数进行实例化 throw new RuntimeException("单例模式正在被破坏"); } } } public Singleton01 getInstance(){ return instance; } }

在用反射就会报错....

 

通过反序列化破坏单例模式:

 对象序列化:将对象状态转换为字节流的过程,可以将其保存到磁盘文件中或者通过网络发送到任何其他程序中。

 反序列化:将字节流转化为对象的过程。

public class Test01 {
    public static void main(String[] args) throws Exception{
//序列化 singleton001 Singleton01要实现 Serializable Singleton01 singleton001 = Singleton01.getInstance(); Singleton01 singleton002 = Singleton01.getInstance(); FileOutputStream fos = new FileOutputStream("d:/a.txt"); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(singleton001); oos.close(); fos.close(); //反序列化 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/a.txt")); Singleton01 singleton003 = (Singleton01)ois.readObject(); System.out.println(singleton001 == singleton002); //true System.out.println(singleton001 == singleton003); //false } }

防止反序列化:

public class Singleton01 implements Serializable {
    public static Singleton01 instance = new Singleton01();  //一开始就已经创建好了
    private Singleton01(){
    }
    public static Singleton01 getInstance(){
        return instance;
    }
    //在反序列化的时候  直接调用这个方法 返回instance   而不是返回反序列化后的新对象
    private Object readResolve() throws ObjectStreamException {
        return instance;
    }
}

 

posted @ 2019-06-03 12:03  DDiamondd  阅读(533)  评论(0编辑  收藏  举报
TOP