设计模式之单例

184 阅读4分钟

单例模式

单例模式确保程序运行过程中一个特定的类只有一个实例对象,并提供对这个对象的外部访问
常见的单例模式应用如线程池、缓存、日志对象等
特点:
单例类必须自己创建自己的唯一实例,并且给其它对象提供这一个实例
应用场景:
1.工具类
2.需要重复创建/销毁的资源消耗过大的类
3.等等

饿汉式单例:程序一启动就创建对象实例,占用内存资源,提高程序效率(典型的以空间换时间)
懒汉式单例:程序一启动不创建对象实例,什么时候用到对象实例什么时候创建,节省资源


饿汉式单例

通过静态变量实现的单例
私有化静态变量和构造器不允许外界访问
提供供外界访问变量的方法
    
    
    
public class HungrySingleton {
    private static final HungrySingleton hungrySingleton = new HungrySingleton();
    private HungrySingleton(){}
    public static HungrySingleton getInstance(){
        return hungrySingleton;
    }
}
测试

    @Test
    public void test01 (){
        HungrySingleton instance = HungrySingleton.getInstance();
        HungrySingleton instance2 = HungrySingleton.getInstance();
        System.out.println(instance); // com.design.demo.singleton.HungrySingleton@1d6d1d42
        System.out.println(instance2); // com.design.demo.singleton.HungrySingleton@1d6d1d42

    }
静态代码块的实现
public class HungrySingleton {
    private static final HungrySingleton hungrySingleton ;
    static {
        hungrySingleton= new HungrySingleton();
    }
    private HungrySingleton(){}
    public static HungrySingleton getInstance(){
        return hungrySingleton;
    }
}

懒汉式单例

在调用的时候创建对象
public class LazyInnerClassSingleton   {

    private static LazyInnerClassSingleton lazySingleton = null;

    public static LazyInnerClassSingleton getInstance(){
        if(lazySingleton == null){
            lazySingleton = new LazyInnerClassSingleton();
        }
        return lazySingleton;
    }
}
这种方式的懒汉式单例在多线程环境会有安全隐患,可以使用synchronized关键字来增强
synchronized在线程较多时会导致程序性能大幅下降,此时可以使用双重检查锁增强
public class LazyInnerClassSingleton   {

    private  static volatile LazyInnerClassSingleton lazySingleton = null;
	// volatile关键字禁止指令重排
    public  static LazyInnerClassSingleton getInstance(){
        if(lazySingleton == null){
            synchronized (LazyInnerClassSingleton.class){
                if(lazySingleton == null){
                    lazySingleton = new LazyInnerClassSingleton();
                    //(1)在堆上开辟空间;(2)属性初始化;(3)引用指向对象
                }
            }
        }
        return lazySingleton;
    }
}
synchronized 对性能还是会有影响,这时可以使用静态内部类在类初始化的时候进行优化



public class LazyInnerClassSingleton   {
    // 默认先初始化内部类
    public  static LazyInnerClassSingleton getInstance(){
        // 返回之前,一定会加载内部类
        return LazyInner.lazy;
    }

    private static class LazyInner{
        private static final LazyInnerClassSingleton lazy = new LazyInnerClassSingleton();
    }
}

反射破坏单例

通过反射创建上述对象,可以看出反射可以破坏单例
     @Test
     public void test2(){ // 反射破坏对象
         try{
             // 1.获取class对象
             Class<LazyInnerClassSingleton> lazyInnerClassSingletonClass = LazyInnerClassSingleton.class;
             // 2.获取构造器对象(包括private)
             Constructor<LazyInnerClassSingleton> declaredConstructor = lazyInnerClassSingletonClass.getDeclaredConstructor(null);
             // 3.允许强制访问
             declaredConstructor.setAccessible(true);
             // 4.暴力初始化
             LazyInnerClassSingleton l1 = declaredConstructor.newInstance();
             LazyInnerClassSingleton l2 = declaredConstructor.newInstance();
             System.out.println(l1);// com.design.demo.bean.LazyInnerClassSingleton@123772c4
             System.out.println(l2);// com.design.demo.bean.LazyInnerClassSingleton@2d363fb3
             System.out.println(l2 == l1);// false

         }catch (Exception e){
             System.out.println("发生了异常");
         }


     }
此时可以在构造器中对反射调用的构造器进行条件限制

public class LazyInnerClassSingleton   {

    // 在构造器中对内部类的静态属性进行校验即可,反射会调用这个构造器方法
    private LazyInnerClassSingleton(){
        if(LazyInner.lazy != null){
            throw new RuntimeException("不允许创建多个实例");
        }
    }

    // 默认先初始化内部类
    public  static LazyInnerClassSingleton getInstance(){
        // 返回之前,一定会加载内部类
        return LazyInner.lazy;
    }

    private static class LazyInner{
        private static final LazyInnerClassSingleton lazy = new LazyInnerClassSingleton();
    }
}

序列化破坏单例

序列化:将对象持久化保存到硬盘中
反序列化:把硬盘中的对象读取到内存中
这个过程中的对象也不是单例的
对象序列化需要实现Serializable接口

public class LazyInnerClassSingleton   implements Serializable{


    private LazyInnerClassSingleton(){
        if(LazyInner.lazy != null){
            throw new RuntimeException("不允许创建多个实例");
        }
    }

    // 默认先初始化内部类
    public  static LazyInnerClassSingleton getInstance(){
        // 返回之前,一定会加载内部类
        return LazyInner.lazy;
    }

    private static class LazyInner{
        private static final LazyInnerClassSingleton lazy = new LazyInnerClassSingleton();
    }
}

序列化对象

    @Test
    public void test4(){ // 序列化破坏单例
        LazyInnerClassSingleton l1 = null;
        LazyInnerClassSingleton l2 = LazyInnerClassSingleton.getInstance();

        FileOutputStream fos = null;
        try{
            // 1.创建SerializableSingleton.text文件,写入单例对象的数据
            fos = new FileOutputStream("SerializableSingleton.text");
            // 2.创建对象输出流,需要一个输出流对象,
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            // 3.将对象输出到文件中
            oos.writeObject(l2);
            oos.flush();
            oos.close();
            // 4.创建输入流和对象输入流,将SerializableSingleton.text文件中的对象读取到程序中
            FileInputStream fis = new FileInputStream("SerializableSingleton.text");
            ObjectInputStream ois = new ObjectInputStream(fis);
            l1 =(LazyInnerClassSingleton) ois.readObject();
            ois.close();

            System.out.println(l1);// com.design.demo.bean.LazyInnerClassSingleton@27082746
            System.out.println(l2);// com.design.demo.bean.LazyInnerClassSingleton@368239c8
            System.out.println(l2 == l1);// false


        }catch (Exception e){
            e.printStackTrace();
        }

    }

根据ObjectInputStream的readOrdinaryObject()源码可知
如果存在readResolve()就调用
所以在实现了Serializable的类中添加readResolve方法就可以防止序列化破环单例
public class LazyInnerClassSingleton implements Serializable {

    private LazyInnerClassSingleton(){
        if(LazyHolder.LAZY != null){
            throw new RuntimeException("不允许创建多个实例");
        }
    }
    private Object readResolve(){
        return LazyHolder.LAZY;
    }

    public static final LazyInnerClassSingleton getInstance(){
        return LazyHolder.LAZY;
    }

    private static class LazyHolder{
        private static final LazyInnerClassSingleton LAZY = new LazyInnerClassSingleton();
    }
}