设计模式(三)----创建型模式之单例模式(一)

news2024/10/6 10:35:43

一、创建型模式

创建型模式的主要关注点是“怎样创建对象?”,它的主要特点是“将对象的创建与使用分离”。

这样可以降低系统的耦合度,使用者不需要关注对象的创建细节。

创建型模式分为:

  • 单例模式

  • 工厂方法模式

  • 抽象工厂模式

  • 原型模式

  • 建造者模式

1.1 单例设计模式

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

1.1.1 单例模式的结构

单例模式的主要有以下角色:

  • 单例类。只能创建一个实例的类

  • 访问类(测试类)。使用单例类

1.1.2 单例模式的实现

单例设计模式分类两种:

饿汉式:类加载就会导致该单实例对象被创建

懒汉式:类加载不会导致该单实例对象被创建,而是首次使用该对象时才会创建

  1. 饿汉式-方式1(静态变量方式)

    /**
     * 饿汉式
     *      静态变量创建类的对象
     */
    public class Singleton {
        //私有构造方法
        private Singleton() {}
    ​
        //在成员位置创建该类的对象
        private static Singleton instance = new Singleton();
    ​
        //对外提供静态方法获取该对象
        public static Singleton getInstance() {
            return instance;
        }
    }

    说明:

    该方式在成员位置声明Singleton类型的静态变量,并创建Singleton类的对象instance。instance对象是随着类的加载而创建的。如果该对象足够大的话,而一直没有使用就会造成内存的浪费。

    用下面的代码来验证一下

    public class Client {
        public static void main(String[] args) {
            //创建Singleton类的对象
            Singleton instance = Singleton.getInstance();
    ​
            Singleton instance1 = Singleton.getInstance();
    ​
            //判断获取到的两个是否是同一个对象
            System.out.println(instance == instance1);
        }
    }

    可以得出单例模式得到的对象是一模一样的。

  2. 饿汉式-方式2(静态代码块方式)

    /**
     * 饿汉式
     *      在静态代码块中创建该类对象
     */
    public class Singleton {
    ​
        //私有构造方法
        private Singleton() {}
    ​
        //在成员位置创建该类的对象
        private static Singleton instance;
    ​
        static {
            instance = new Singleton();
        }
    ​
        //对外提供静态方法获取该对象
        public static Singleton getInstance() {
            return instance;
        }
    }

    说明:

    该方式在成员位置声明Singleton类型的静态变量,而对象的创建是在静态代码块中,也是对着类的加载而创建。所以和饿汉式的方式1基本上一样,当然该方式也存在内存浪费问题。

    验证方式同上可得出相同结论。

  3. 懒汉式-方式1(线程不安全)

    /**
     * 懒汉式
     *  线程不安全
     */
    public class Singleton {
        //私有构造方法
        private Singleton() {}
    ​
        //在成员位置创建该类的对象
        private static Singleton instance;
    ​
        //对外提供静态方法获取该对象
        public static Singleton getInstance() {
            //判断instance是否为null,如果为null,说明还没有创建Singleton类的对象
            //如果没有,创建一个并返回,如果有,直接返回
            if(instance == null) {
                //线程1等待,线程2获取到cpu的执行权,也会进入到该判断里面
                instance = new Singleton();
            }
            return instance;
        }
    }

    说明:

    从上面代码我们可以看出该方式在成员位置声明Singleton类型的静态变量,并没有进行对象的赋值操作,那么什么时候赋值的呢?当调用getInstance()方法获取Singleton类的对象的时候才创建Singleton类的对象,这样就实现了懒加载的效果。但是,如果是多线程环境,会出现线程安全问题。

  4. 懒汉式-方式2(线程安全)

    /**
     * 懒汉式
     *  线程安全
     */
    public class Singleton {
        //私有构造方法
        private Singleton() {}
    ​
        //在成员位置创建该类的对象
        private static Singleton instance;
    ​
        //对外提供静态方法获取该对象
        public static synchronized Singleton getInstance() {
    ​
            if(instance == null) {
                instance = new Singleton();
            }
            return instance;
        }
    }

    说明:

    该方式也实现了懒加载效果,同时又解决了线程安全问题。但是在getInstance()方法上添加了synchronized关键字,导致该方法的执行效果特别低。从上面代码我们可以看出,其实就是在初始化instance的时候才会出现线程安全问题,一旦初始化完成就不存在了。

  5. 懒汉式-方式3(双重检查锁)

    再来讨论一下懒汉模式中加锁的问题,对于 getInstance() 方法来说,绝大部分的操作都是读操作,读操作是线程安全的,所以我们没必让每个线程必须持有锁才能调用该方法,我们需要调整加锁的时机。由此也产生了一种新的实现模式:双重检查锁模式

    /**
     * 双重检查方式
     */
    public class Singleton { 
    ​
        //私有构造方法
        private Singleton() {}
    ​
        private static Singleton instance;
    ​
       //对外提供静态方法获取该对象
        public static Singleton getInstance() {
            //第一次判断,如果instance不为null,不进入抢锁阶段,直接返回实例
            if(instance == null) {
                synchronized (Singleton.class) {
                    //抢到锁之后再次判断是否为null
                    if(instance == null) {
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    }

    双重检查锁模式是一种非常好的单例实现模式,解决了单例、性能、线程安全问题,上面的双重检测锁模式看上去完美无缺,其实是存在问题,在多线程的情况下,可能会出现空指针问题,出现问题的原因是JVM在实例化对象的时候会进行优化和指令重排序操作。

    要解决双重检查锁模式带来空指针异常的问题,只需要使用 volatile 关键字, volatile 关键字可以保证可见性和有序性。

    /**
     * 双重检查方式
     */
    public class Singleton {
    ​
        //私有构造方法
        private Singleton() {}
    ​
        private static volatile Singleton instance;
    ​
       //对外提供静态方法获取该对象
        public static Singleton getInstance() {
            //第一次判断,如果instance不为null,不进入抢锁阶段,直接返回实际
            if(instance == null) {
                synchronized (Singleton.class) {
                    //抢到锁之后再次判断是否为空
                    if(instance == null) {
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    }

    小结:

    添加 volatile 关键字之后的双重检查锁模式是一种比较好的单例实现模式,能够保证在多线程的情况下线程安全也不会有性能问题。

  6. 懒汉式-方式4(静态内部类方式)

    静态内部类单例模式中实例由内部类创建,由于 JVM 在加载外部类的过程中, 是不会加载静态内部类的, 只有内部类的属性/方法被调用时才会被加载, 并初始化其静态属性。静态属性由于被 static 修饰,保证只被实例化一次,并且严格保证实例化顺序。

    /**
     * 静态内部类方式
     */
    public class Singleton {
    ​
        //私有构造方法
        private Singleton() {}
    ​
        //定义一个静态内部类
        private static class SingletonHolder {
            //在内部类中声明并初始化外部类的对
            private static final Singleton INSTANCE = new Singleton();
        }
    ​
        //对外提供静态方法获取该对象
        public static Singleton getInstance() {
            return SingletonHolder.INSTANCE;
        }
    }

    验证是否正确的方式同上。

    说明:

    第一次加载Singleton类时不会去初始化INSTANCE,只有第一次调用getInstance,虚拟机加载SingletonHolder

    并初始化INSTANCE,这样不仅能确保线程安全,也能保证 Singleton 类的唯一性。

    小结:

    静态内部类单例模式是一种优秀的单例模式,是开源项目中比较常用的一种单例模式。在没有加任何锁的情况下,保证了多线程下的安全,并且没有任何性能影响和空间的浪费。

  7. 枚举方式

    枚举类实现单例模式是极力推荐的单例实现模式,因为枚举类型是线程安全的,并且只会装载一次,设计者充分的利用了枚举的这个特性来实现单例模式,枚举的写法非常简单,而且枚举类型是所用单例实现中唯一一种不会被破坏的单例实现模式。

    /**
     * 枚举方式
     */
    public enum Singleton {
        INSTANCE;
    }

    说明:

    枚举方式属于饿汉式方式。

    未完待续。。。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/195935.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

英语学习 3

1 词汇积累 1、ships 船 2、class 级 3、marvels 奇迹 4、marvelous 非凡的、了不起的、极好的 5、cursed 诅咒、被诅咒的 6、the most luxurious ships 最豪华的船 7、luxury 奢侈、奢华的 8、luxurious 心满意足的、舒适的 9、utmost 极度的、最大的 10、kind 种类 11、voya…

Kali Linux神秘工具教程(详细版)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录前言一、Kali Linux - 安装和配置信息收集工具二、NMAP隐形扫描搜索Searchsploit域名系统工具dnsenum.plDNSMAPdnstracerLBDHping3漏洞分析工具Cisco-torch工具Cisco…

回溯算法(基础)

目录 一、基本概念 二、以简单全排列认识回溯 (一)决策树 (二)回溯示意图 (三)核心代码 (四)完整代码 三、组合问题 (一)问题 &#xff08…

如何通过groovy扩展方法

最近一直使用jmeter做接口测试,虽然好用,但是每次解析结果都要写大量重复代码。然后想到groovy是可以在运行时动态增强jvm字节码的,比如Date中就有大量增强的方法,比如format,upto,downto......,既然groovy可以&#x…

用 NFTScan 的角度解析 Yuga labs NFT 项目系列

如果要说 NFT 影响力最大的公司是哪个?如果说是 Yuga Labs 应该我想大家应该都不会否认。一个创立一年多的 NFT 营销和开发公司,多次的并购以及行销操作都立下 NFT 界的标竿典范,尤其 BAYC NFT 系列取得巨大成功之后,该团队已成为…

DSP_定义一个大的全局数组_探索之路

前言 最近在做基于dsp平台的无通信接口系统辨识,辨识的时候会有很大的数据需要存到一个数组当中,而dsp如果定义一个很大的全局数组,编译会报错。 本文将探索如何解决这个报错以及全局数组的大小极限。 正文 首先,我们定义了一个…

数学库:Extreme Optimization Numerical 8.1.4 Crack

Extreme Optimization Numerical.NET 的极端优化数值库,更快地构建金融、工程和科学应用程序,具有置信度和预测带的非线性曲线拟合,用于 .NET的极端优化数值库是为 Microsoft .NET 框架构建的通用数学和统计类的集合。用于 .NET的极端优化数值…

将无风险资产与单个风险资产进行组合

目录 1. 基本概念 2. 将无风险资产与单个风险资产进行组合 3. 有效资产组合 1. 基本概念 无风险资产和风险资产。 我的理解:无风险资产利率完全可确定,风险资产的利率称为预期收益率,并且有标准差。 关于风险资产预期收益率和标准差的计…

NC65 自由报表发布为节点如何显示以及如何取消已发布的报表节点

NC65 自由报表发布为节点如何显示以及如何取消已发布的报表节点? 一、NC65 自由报表发布为节点如何显示? 答:需要在动态建模平台-权限管理-职责管理下的职责节点进行功能分配,如下图: 二、如何取消已发布的报表节…

Javac Spire.Presentation 之PPT文本图片内容提取

目录结构前言文档准备引入Maven依赖代码块提取结果验证ppt_demo.ppt 提取结果pptx_demo.pptx 提取结果前言 应公司需求,需实现以下功能 PPT文本内容的替换;PPT文本内容的提取;PPT中图片的提取存放; 此文章将使用Spire.Presenta…

Mal-PEG-SCM,Maleimide PEG SCM,双功能修饰性PEG

Mal-PEG-SCM,SCM-PEG-Maleimide,Maleimide PEG SCM,Maleimide PEG Succinimidyl Carboxymethyl Ester马来酰亚胺-聚乙二醇-琥珀酰亚胺羧甲基酯,马来酰亚胺PEG琥珀酰亚胺羧甲基酯Product specifications:1.CAS No&#…

DataGrip下载安装及使用教程(详细版)

一.安装教程 1.下载 官网下载:DataGrip: The Cross-Platform IDE for Databases && SQL by JetBrains 2.点击Download跳转到下载页面 3.下载最新版本的可以直接点击 Download 下载,下载其他版本的点击 Other versions 下载其他版本 4. 4.选择…

改变Linux文件权限、所属用户组、所有者知识总结

✅作者简介:热爱国学的Java后端开发者,修心和技术同步精进。 🍎个人主页:Java Fans的博客 🍊个人信条:不迁怒,不贰过。小知识,大智慧。 💞当前专栏:Linux操作…

requestAnimationFrame详解-js性能优化

requestAnimationFrame 请求动画帧 它是一个浏览器的宏任务 requestAnimationFrame的用法与settimeout很相似,只是不需要设置时间间隔而已。requestAnimationFrame使用一个回调函数作为参数,这个回调函数会在浏览器重绘之前调用。它返回一个整数&#x…

快鲸SCRM打通工单系统,实现客户售前售后一体化管理

深度运营客户关系,做好客户售后服务,才符合企业的长远利益。然而大多数企业只注重售前售中,忽视了售后,导致客户售后服务现状不尽人意,主要体现在: 把客户问题抛到售后群后,便放任不管; 缺乏标…

自动驾驶感知——超声波技术

文章目录1. 超声波基本概念1.1 声波的频率与分类1.2 超声波的波速和波长1.3 超声波的指向性1.4 超声波的反射和折射1.5 超声波的衰减1.6 超声波产生的效应2. 超声波传感器原理及传感器分类2.1 超声波传感器原理2.2 压电式超声波传感器2.3 磁致伸缩式超声波传感器2.4 超声波传感…

MyBatis案例 | 使用映射配置文件实现CRUD操作——通过主键查询对应数据

本专栏主要是记录学习完JavaSE后学习JavaWeb部分的一些知识点总结以及遇到的一些问题等,如果刚开始学习Java的小伙伴可以点击下方连接查看专栏 本专栏地址:🔥JavaWeb Java入门篇: 🔥Java基础学习篇 Java进阶学习篇&…

关于Linux中断的相关查询

1.linux 内核 /proc/interrupts 在 /proc/interrupts 文件中记录了 Linux 内核的中断信息,我们可以通过命令查看 sudo cat /proc/interrupts 文件中以表格的形式列举出来所有的内核中断,其表头信息标注如下: 我们可以通过特定任务执行时…

计算机SCI论文一定要在指定的机构润色吗? - 易智编译EaseEditing

不一定要在指定的机构润色。 首先期刊要求润色,是非常正常的事情。国人投的中文论文,有的也会被要求润色。 更不要说国外的英文期刊了,咱们写的英文文章投过去,大部分都会被要求润色的。 为什么期刊总是要求润色语言呢&#xff…

Windows7操作系统安全(3)

实验简介 实验所属系列:网络安全实践 实验对象: 本科/专科信息安全专业 相关课程及专业:计算机基础,Linux基础 实验时数(学分):2学时 实验类别:实践实验类预备知识 Windows系列是目…