zoukankan      html  css  js  c++  java
  • Java设计模式-单例模式

    一、单例模式概述

    1. 定义:确保一个类只有一个实例,并提供一个全局访问点来访问这个实例

    简单的说,就是你有且只有一个女朋友(有多个女朋友的模式不是这里~~),并且你的女朋友很特殊,从来只听你的话,所以别人想和她交流(访问她)就必须通过你(全局访问点)来和她交流。

    运用:系统中用到单例模式的地方很多,比如Windows系统点击开始只能出现一个开始界面,Ctrl+Alt+T, 只能出现一个资源管理器,每个进程有且对应唯一一个进程ID等等

    单例模式是为了让资源得到最大化利用,不浪费资源

    同时假如不采用此模式,就可能在不同时刻打开同一个界面,但界面中的内容又各不相同,因而用户极易产生误解,影响使用效率。因此单例模式在系统中的应用非常重要

    2. 要点

    1)某一个单例类只能有一个实例

    2)必须自行创建这个实例

    3)必须向系统提供这个实例

    二、单例模式的结构和实现

    1. 结构:

    单例模式关键要素:

    1)私有的静态变量

    2)私有构造函数

    3)公有的静态方法

    4)根据变量数判断是否生成女朋友(唯一的对象)

    5)公有静态方法调用私有构造方法

    关键词释义:

    1)Singleton(单例):在单例类的内部创建它的唯一实例

    2)Getinstance(静态方法):通过它产生唯一实例

    3)instance(静态变量):判断是否可以产生实例

    2. 实现:

    /**
     * @author ***
     * @title: Singleton
     * @projectName design-pattern
     * @description TODO
     * @date 2021/7/5 15:24
     */
    public class Singleton {
    
       /**
        * 静态私有成员变量
        */
        private static Singleton instance = null;
    
    
        /**
         * 私有构造函数
         */
        private Singleton() {
            System.out.println("恭喜你,获得一个女朋友~~");
        }
    
    
        /**
         * 静态公有方法,返回实例
         *
         * @return
         */
        public static Singleton getInstance() {
            //没女朋友
            if (instance == null) {
                //生成一个吧
                instance = new Singleton();
            }
            //有就返回当前的,
            return instance;
        }
    }
    /**
     * @author ***
     * @title: Program
     * @projectName design-pattern
     * @description TODO
     * @date 2021/7/5 15:24
     */
    public class Program {
        public static void main(String[] args) {
            Singleton s1 = Singleton.getInstance();
            Singleton s2 = Singleton.getInstance();
            if (s1 == s2) {
                System.out.println("怎么能想要共有一个女朋友呢?S2 赶紧换一个吧...");
            }
        }
    }

    控制台输出:

    三、饿汉式和懒汉式单例

    1. 饿汉

    如名字一般,就是出于很饥饿的状态,所以在定义静态变量时就实例化了单例

    /**
     * @author ***
     * @title: HungryManSingleton
     * @projectName design-pattern
     * @description TODO
     * @date 2021/7/5 15:37
     */
    public class HungryManSingleton {
    
        //静态变量实例化单例类
        private static HungryManSingleton instance = new HungryManSingleton();
    
        private HungryManSingleton() {
        }
    
        public static HungryManSingleton getInstance() {
            return instance;
        }
    }

    2. 懒汉

    懒汉式单例类则是在类第一次被引用时将自己实例化,单例类被加载时不会实例化,所以这很符合懒汉的气质。

    但是在这里要注意的是,在定义静态变量时没有实例化单例类,而是在第一次调用静态方法时实例化单例类,这就会产生问题,高并发,多线程实现懒汉式单例时会创建多个对象,从而违背了单例模式的设计意图。

    也就是还是要对女朋友的个数进行判断。这要怎么办呢?

    在多线程的情况下,就要对该代码段进行控制,即每次只让一个线程进入并创建实例,也就是相当于现在的“共享女友”,帮你拍照啊,陪你去看电影啊 ,巴拉巴拉

    但是,该“共享女友”有且只有一个,即单例类的唯一实例。所以,土豪们(各个线程)得一个一个租用,上一个用完了下一个才能租用。因此代码如下:

    /**
     * @author ***
     * @title: LazyManSingleton
     * @projectName design-pattern
     * @description TODO
     * @date 2021/7/5 15:56
     */
    public class LazyManSingleton {
        private static LazyManSingleton instance = null;
        //看做一个门
        private static final Object root = new Object();
        
        //程序运行时创建只读辅助对象
        private LazyManSingleton() {
    
        }
    
        public static LazyManSingleton getInstance() {
            //在房间外问:房间里有人吗 ? 没人回应 ,可能没,可能下一秒有人进去 ,我却以为没人
            if (instance == null) {
                //第二次判断 
                //把门关了,外面线程进不来,只能里面的出来,外面的才能进
                synchronized (root){
                    //继续问,房间里有人吗?  有就真的有,没有就真的没
                    if (instance == null) {
                        //创建实例
                        instance = new LazyManSingleton();
                    }
                }
            }
            return instance;
        }
    
    }

    二者比较:

    1)饿汉式单例

    优点:无需考虑多线程同时访问的问题,确保实例唯一性。调用速度和反应时间快于懒汉模式,因为饿汉一开始就创建,后面则直接拿来用就可以了。

    缺点:不管单例对象是否需要,都会在类加载时创建,这样不如懒汉式单例,资源利用不高,且加载时间较长。如启动VS,Eclipse等,需要loading许多可能要的可能不要的,要等啊...

    2)懒汉式单例

    优点:第一次使用时创建,不会一直占用资源,即延迟加载。

    缺点:必须考虑多线程问题,特别是单例类作为资源控制器时,会涉及资源初始化,也会耗费许多时间,也会出现多线程同时首次引用此类,造成拥堵,导致系能性能降低

    四、单例模式的优缺点和适用环境

    1.  单例模式的优点

    1)提供唯一实例的受控访问,可以严格控制何时访问

    2)由于只存在一个对象,可以节省系统资源

    3)如果将单例模式的实例数目变为可变,即将单例模式进行扩展,即可获得指定数目的实例对象,即节省资源,又提高效率(相当于多例类)

    2. 单例模式的缺点

    1)没有抽象层,扩展有较大困难

    2)单例类职责过重,一定程度上违背了单一职责原则(单例类即提供业务方法,又提供了创建对象的方法(工厂方法),对象创建和对象本身耦合在了一起)

    3)C#、JAVA 拥有GC(自动垃圾回收机制) (可以去了解下) ,在实例化的对象长时间不被利用,会被误认为垃圾进行自动销毁,下次利用又要重新创建,导致共享的单例对象丢失(女朋友丢了可不好受啊..)

    3. 单例模式的适用环境

    1)在系统只要一个实例对象(man只要一个girlfriend)/(资源管理器).....

    2)客户调用类的单个实例只允许使用一个公共访问点,除了该点外,不允许其他途径来访问实例

    五、其他写法

    1.  饿汉式代码

    /**
     * @author ***
     * @title: SingletonDemo1
     * @projectName design-pattern
     * @description 单例模式:饿汉式
     * @date 2021/7/5 16:41
     */
    public class SingletonDemo1 {
        //类初始化立即加载,由于类加载时完成初始化,线程安全。不用同步快,调用效率高。
        private static SingletonDemo1 instance = new SingletonDemo1();
    
        private SingletonDemo1() {
    
        }
    
        public static SingletonDemo1 getInstance() {
            return instance;
        }
    }

    2. 懒汉式 同步方法

    /**
     * @author ***
     * @title: SingletonDemo2
     * @projectName design-pattern
     * @description 单例模式:懒汉式线程安全
     * @date 2021/7/5 16:42
     */
    public class SingletonDemo2 {
    
            //类初始化延迟加载,方法同步线程安全,调用效率低。
            private static SingletonDemo2 instance = null;
    
            private SingletonDemo2() {
            }
    
            public static synchronized SingletonDemo2 getInstance() {
    
                if (instance == null) {
                    instance = new SingletonDemo2();
                }
                return instance;
            }
    }

    3. 懒汉式 同步方法

    /**
     * @author ***
     * @title: SingletonDemo3
     * @projectName design-pattern
     * @description 单例模式:懒汉式双重检查锁
     * @date 2021/7/5 16:44
     */
    public class SingletonDemo3 {
        //由于编译器优化和JVM内部模型原因,同步块会有问题,会出现问题
        private static SingletonDemo3 instance = null;
    
        private SingletonDemo3() {
    
        }
    
        public static SingletonDemo3 getInstance() {
    
            if (instance == null) {
                synchronized (SingletonDemo3.class) {
                    if (instance == null) {
                        instance = new SingletonDemo3();
                    }
                }
    
            }
            return instance;
        }
    }

    4 .静态内部类

    /**
     * @author ***
     * @title: SingletonDemo4
     * @projectName design-pattern
     * @description 单例模式:静态内部类懒加载
     * @date 2021/7/5 16:45
     */
    public class SingletonDemo4 {
        //外部没有static属性,不会立即加载对象
        //真正调用getInstance,才会加载内部类
        //高效+安全+延迟加载
        private static class SingletonInstance {
            public static SingletonDemo4 instance = new SingletonDemo4();
        }
    
        private SingletonDemo4() {
    
        }
    
        public static SingletonDemo4 getInstance() {
            return SingletonInstance.instance;
        }
    }

    5.  枚举类

    /**
     * @author ***
     * @title: SingletonDemo4
     * @projectName design-pattern
     * @description 单例模式:枚举类
     * @date 2021/7/5 16:45
     */
    public enum SingletonDemo5 {
        //直接用SingletonDemo06.INSTANCE本身代理,代表一个对象
        //避免反射、反序列化,效率高
        //没有懒加载
        INSTANCE;
        Enum ss;
    }

    6. 枚举类实现懒加载

    /**
     * @author ***
     * @title: SingletonDemo6
     * @projectName design-pattern
     * @description  单例模式:枚举类实现懒加载
     * @date 2021/7/5 16:49
     */
    public class SingletonDemo6 {
        // 私有构造函数
        private SingletonDemo6() {
    
        }
    
        public static SingletonDemo6 getInstance() {
            return Singleton.INSTANCE.getInstance();
        }
    
        private enum Singleton {
            INSTANCE;
    
            private SingletonDemo6 singleton;
    
            // JVM保证这个方法绝对只调用一次
            Singleton() {
                singleton = new SingletonDemo6();
            }
    
            public SingletonDemo6 getInstance() {
                return singleton;
            }
        }
    }

    7. 解决反射漏铜和序列化漏铜

    /**
     * @author ***
     * @title: SingletonDemo7
     * @projectName design-pattern
     * @description 单例模式:解决反射漏铜,通过设置constructor.setAccessible(true);//跳过权限校验,是的创建实例不一致
     *              解决序列化漏铜,readResolve()
     * @date 2021/7/5 16:51
     */
    public class SingletonDemo7 {
    
        //类初始化延迟加载,方法同步线程安全,调用效率低。
        private static SingletonDemo7 instance = null;
    
        private SingletonDemo7() {
            if (instance != null) {//防止通过反射破解单例
                throw new RuntimeException();
            }
        }
    
        public static synchronized SingletonDemo7 getInstance() {
    
            if (instance == null) {
                instance = new SingletonDemo7();
            }
            return instance;
        }
    
        //反序列化时,直接返回此对象,而不生成新对象
        private Object readResolve() throws ObjectStreamException {
            return instance;
        }
    }
  • 相关阅读:
    关于enum ,调用webservice,用户控件与主页面之间的交互,datsource属性,net面试题,反射类生成sql语句,URl重写一个小实例
    一个很简单的图片上传后立即显示在页面的控件(c#)
    委托之实现异步调用
    跟我学Linq
    w3c关于sql sever的基础操作
    join操作基础
    表操作基础
    javascript理论篇(详情见地址)
    android universal-image-loader的使用
    json相关类库,java对象与json相互转换
  • 原文地址:https://www.cnblogs.com/mylqm/p/14972897.html
Copyright © 2011-2022 走看看