设计模式之单例模式

设计模式之单例模式

概述

  • 单例模式:确保一个类只有一个实例,并提供一个全局访问点。
  • 单例设计模式,是采取一定的方法措施以保证在整个系统中,某个类只能存在一个对象实例,并且这个类只提供静态方法用来获取该对象实例。

理解

简单例子

  • 我们使用笔记本时,会将不用的文件移入到废纸篓(mac)或者回收站(windows)中,对于笔记本来说,废纸篓或者回收站就是唯一一个实例。
  • 一个公司的董事长A的职位只有一个,对于该公司而言,A就是该公司的唯一的董事长的实例。
  • 同理,在软件开发过程中,开发一个系统时,有一些实例在系统中要求只能存在一个对象实例,为了确保在系统中该类只有一个实例,故引出了⏤⏤单例模式

条件

  • 构造方法为私有private,以确保无法在该类之外通过new操作符创建对象;
  • 类本身提供一个静态static方法用来获取该对象实例;
  • 在类内部调用私有的构造方法创建静态static私有private的实例化对象,因为类方法是静态的,静态方法访问静态的实例,所以该成员变量也应该是静态的。

核心代码

饿汉式

  • 随着类的加载而加载,在类加载时便初始化实例,可以避免线程同步问题,线程安全。但是无延迟加载,某些情况下,会造成内存浪费。

  • 饿汉式,使用静态常量,不推荐

    class Singleton1 {
        private Singleton1(){}
      	//类初始化时给该成员变量赋值,可以加final关键字
      	//静态常量
        private static final Singleton1 Instance = new Singleton1();
        public static Singleton1 getInstance(){
            return Instance;
        }
    }
    
  • 饿汉式,使用静态代码块,不推荐

    class Singleton2 {
        private Singleton2(){}
        private static  Singleton2 Instance;
      	//静态代码块
        static {
            Instance = new Singleton2();
        }
        public static Singleton2 getInstance(){
            return Instance;
        }
    }
    

懒汉式

  • 支持延迟加载;但是会出现频繁加锁、释放锁,以及并发度低的问题。

  • 懒汉式 ,线程不安全,多线程情况下,不能保证单个实例,不推荐

    class Singleton3 {
        private Singleton3(){}
        private static Singleton3 Instance;
        public static Singleton3 getInstance(){
            if (Instance == null){
                Instance = new Singleton3();
            }
            return Instance;
        }
    }
    
  • 懒汉式,线程安全,方法上加synchronized关键字来保证线程安全,调用getInstance()该方法时,每次都要同步,效率低,不推荐

    class Singleton4 {
    
        private Singleton4(){}
    
        private static Singleton4 Instance;
    		//使用 synchronized 关键字保证线程安全  同步方法
        public static synchronized Singleton4 getInstance(){
            if (Instance == null){
                Instance = new Singleton4();
            }
            return Instance;
        }
    }
    

双重锁校验DCL

  • 支持延迟加载,支持高并发,双重校验,推荐

    1. 第一次:如果存在已经实例化的实例,直接返回,不执行同步方法里的代码,提高性能。
    2. 第二次:多线程情况下,如果不加第二次校验,A线程、B线程同时通过了第一次的校验,则最终会产生两个实例,不能保证单实例。
  • 缺点:多线程环境中,指令重排可能导致访问获得一个未初始化的对象。解决方案便是禁止多线程情况下的指令重排序,使用volatile关键字修饰Instance

    class Singleton5 {
        private Singleton5(){}
    		//volatile关键字 防止指令重排
        private static volatile Singleton5 Instance;
        public static Singleton5 getInstance(){
            if(Instance == null){
                synchronized(Singleton5.class){
                    if (Instance == null){
                        Instance = new Singleton5();
                    }
                }
            }
            return Instance;
        }
    }
    

静态内部类

  • 支持延迟加载、支持高并发、代码量比DCL少,推荐

  • 静态成员变量与静态代码块在类被调用的时候才会初始化,而静态内部类只有当被外部类调用到的时候才会初始化。也就是说,只有调用getInstance()时,才会被加载,这就保证了延迟加载的特点。

    class Singleton6 {
        private Singleton6() {}
        private static class Singleton6Instance{
            private static final Singleton6 Instance = new Singleton6();
        }
        public static Singleton6 getInstance(){
            return Singleton6Instance.Instance;
        }
    
    }
    

枚举类

  • 可以避免多线程同步问题,可以防止反序列化重新创建新的对象,推荐

    enum Singleton7 {
        Instance;
    }
    

反思

  • 我们知道,现有的单例模式写法一般推荐的都是DCL、静态内部类和枚举类;我们也知道java反射可以在运行时创建类的实例,那么如果使用反射创建类实例,上面三种还能保证单个实例么?

  • DCL 反射不安全,通过反射可以得到另外的实例;

     //DCL使用反射 并比较是否是同一个实例
     Singleton5 singleton5one = Singleton5.getInstance();
     Singleton5 singleton5two = null;
     Class<Singleton5> clazz = Singleton5.class;
     Constructor<Singleton5> constructor = clazz.getDeclaredConstructor(null);
     constructor.setAccessible(true);
     singleton5two = constructor.newInstance();
     System.out.println("singleton5one=" + singleton5one);
     System.out.println("singleton5two=" + singleton5two);
     //DCL打印结果:
     //singleton5one=com.practice.singleton.type8.Singleton5@63947c6b
     //singleton5two=com.practice.singleton.type8.Singleton5@2b193f2d    
    
  • 静态内部类,反射不安全;

     //静态内部类使用反射
     Singleton6 singleton6one = Singleton6.getInstance();
     Singleton6 singleton6two = null;
     Class clazz6 =  Singleton6.class;
     Constructor<Singleton6> constructor6 = clazz6.getDeclaredConstructor(null);
     constructor6.setAccessible(true);
     singleton6two = constructor6.newInstance();
     System.out.println("singleton6one=" + singleton6one);
     System.out.println("singleton6two=" + singleton6two);
     //静态内部类打印结果
     //singleton6one=com.practice.singleton.type8.Singleton6@355da254
     //singleton6two=com.practice.singleton.type8.Singleton6@4dc63996
    
  • 枚举类,反射安全。

    //枚举类使用反射
    Singleton7 singleton7one = Singleton7.Instance;
    Singleton7 singleton7two = null
    Class clazz7 =  Singleton7.class;
    //直接打印出构造方法,根据构造方法创建出实例 就不再反编译Singleton7.class类了
    Constructor<Singleton7>[] constructor7 = clazz7.getDeclaredConstructors();
    for (Constructor<Singleton7> singleton7Constructor : constructor7) {
        System.out.println(singleton7Constructor);
      //执行打印结果为:
      //private com.practice.singleton.type8.Singleton7(java.lang.String,int)
      //也就是说,构造器类似于:private Singleton7(String,int){}
        }
    //通过上一步得到的结果,得到参数为String、int类型的构造方法
    Constructor<Singleton7> constructor1 = clazz7.getDeclaredConstructor(String.class,int.class);
    constructor1.setAccessible(true);
    singleton7two = constructor1.newInstance("枚举",1);
    System.out.println("singleton7one=" + singleton7one);
    System.out.println("singleton7two=" + singleton7two);
    //打印结果:
    //java.lang.IllegalArgumentException: Cannot reflectively create enum objects
    

总结

  • 软件开发过程中,当需要单一的对象工作时,我们可以使用单例模式,推荐使用DCL、静态内部类和枚举方式,虽然DCL、静态内部类方式在某种情况下不能保证绝对的单例,但对于系统中的绝大多数情况例如延迟加载、线程安全等都已经满足了,我们可以根据自己的需要选择合适的单例模式实现方式。
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看REAdMe.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看REAdMe.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看READme.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值