[设计模式Java实现附plantuml源码~创建型] 确保对象的唯一性~单例模式

news2024/12/23 19:05:21

前言:
为什么之前写过Golang 版的设计模式,还在重新写Java 版?
答:因为对于我而言,当然也希望对正在学习的大伙有帮助。Java作为一门纯面向对象的语言,更适合用于学习设计模式。
为什么类图要附上uml
因为很多人学习有做笔记的习惯,如果单纯的只是放一张图片,那么学习者也只能复制一张图片,可复用性较低,附上uml,方便有新理解时,快速出新图。

文章目录

      • 饿汉单例
      • 懒汉单例
      • 代码实现
        • 饿汉
        • 懒汉单锁
        • 懒汉双重检测
        • `IODH`实现懒汉模式
      • 单例模式总结
        • 1.主要优点单例模式的主要优点如下:
        • 单例模式的主要缺点如下:
        • 3.适用场景在以下情况下可以考虑使用单例模式:

饿汉单例

在这里插入图片描述

@startuml
class Singleton {
  - static instance = new Singleton
  + static getInstance(): Singleton
  + operation(): void
}
Singleton --> Singleton : getInstance()
@enduml

懒汉单例

在这里插入图片描述

@startuml
class Singleton {
  - static instance = null
  - constructor: Singleton
  + static getInstance(): Singleton
  + operation(): void
}
Singleton --> Singleton : getInstance()
@enduml

  • 懒汉加锁
    在这里插入图片描述
@startuml
class Singleton {
  - static volatile instance = null
  - constructor: Singleton
  + static getInstance(): synchronized Singleton
  + operation(): void
}
note left of Singleton::getInstance
if (instance == null)
	instance = construct();
return instance;
end note
Singleton --> Singleton : getInstance()
@enduml
  • 懒汉双重检测
    在这里插入图片描述
@startuml
class Singleton {
  - static volatile instance = null
  - constructor: Singleton
  + static getInstance(): synchronized Singleton
  + operation(): void
}
note left of Singleton::getInstance
// 一重检测
if (instance == null){
		synchronized(..) {
//	二重检测
				if(instance == null) {
							instance = construct();
				}
		}
}
return instance;
end note
Singleton --> Singleton : getInstance()
@enduml

代码实现

饿汉
public class EagerSingleton {
    private static final EagerSingleton instance = new EagerSingleton();

    // 私有构造方法,防止外部实例化
    private EagerSingleton() {
    }

    // 获取单例对象的静态方法
    public static EagerSingleton getInstance() {
        return instance;
    }
}

EagerSingleton 类的构造方法被声明为私有,以防止外部通过 new 关键字实例化该类。而类中定义了一个私有静态变量 instance,它在类加载的时候就被创建并初始化为 EagerSingleton 类的一个实例。
getInstance() 方法是获取单例对象的静态方法,它直接返回了已经创建好的 instance 对象。
由于饿汉单例在类加载的时候就创建了对象,因此它具有线程安全的特性,但可能会造成一定的资源浪费,因为无论是否使用该单例对象,都会被提前创建。

懒汉单锁
public class LazySingleton {
    private static LazySingleton instance;

    // 私有构造方法,防止外部实例化
    private LazySingleton() {
    }

    // 获取单例对象的静态方法,使用synchronized关键字实现线程安全
    public static synchronized LazySingleton getInstance() {
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
}
懒汉双重检测
public class LazySingleton {
    private static volatile LazySingleton instance;

    // 私有构造方法,防止外部实例化
    private LazySingleton() {
    }

    // 获取单例对象的静态方法,使用双重检测实现延迟加载和线程安全
    public static LazySingleton getInstance() {
        if (instance == null) {
            synchronized (LazySingleton.class) {
                if (instance == null) {
                    instance = new LazySingleton();
                }
            }
        }
        return instance;
    }
}

在上面的代码中,LazySingleton 类的构造方法被声明为私有,以防止外部通过 new 关键字实例化该类。类中定义了一个私有静态变量 instance,它在第一次调用 getInstance() 方法时才会被创建并初始化为 LazySingleton 类的一个实例。
getInstance() 方法是获取单例对象的静态方法。在方法中,首先检查 instance 是否为空,如果为空则进入同步代码块。在同步代码块内部,再次检查 instance 是否为空,这是为了防止多个线程同时通过了第一个检查而进入同步块,从而创建多个实例。如果 instance 仍然为空,则创建新的实例并将其赋值给 instance
使用 volatile 关键字修饰 instance 变量可以保证变量的可见性,从而避免在多线程环境下出现问题。
懒汉双重检测通过双重检查和同步块的方式实现了延迟加载和线程安全,同时也提高了性能。因此,它是一种常见的单例模式实现方式。


饿汉式单例类不能实现延迟加载,不管将来用不用,它始终占据内存;
懒汉式单例类线程安全控制烦琐,而且性能受影响。
可见,无论是饿汉式单例还是懒汉式单例都存在这样那样的问题。
有没有一种方法,能够将两种单例的缺点都克服,而将两者的优点合二为一呢?答案是肯定的。下面来学习这种更好的被称为 Initialization on Demand Holder(IoDH) 的技术。实现 IoDH 时,需在单例类中增加一个静态 (static) 内部类,在该内部类中创建单例对象,再将该单例对象通过 getInstance() 方法返回给外部使用

IODH实现懒汉模式
public class LazySingleton {
    private static class SingletonHolder {
        private static final LazySingleton instance = new LazySingleton();
    }

    // 私有构造方法,防止外部实例化
    private LazySingleton() {
    }

    // 获取单例对象的静态方法
    public static LazySingleton getInstance() {
        return SingletonHolder.instance;
    }
}

在上面的代码中,LazySingleton 类内部定义了一个私有静态内部类 SingletonHolder。在 SingletonHolder 内部,定义了一个私有静态变量 instance,它是 LazySingleton 类的一个实例。
由于静态内部类 SingletonHolder 只有在 getInstance() 方法被调用时才会被加载,所以在类加载的过程中并不会创建 instance 对象。只有当第一次调用 getInstance() 方法时,SingletonHolder 类会被加载,从而创建并初始化 instance 对象。这种方式利用了 Java 类加载的线程安全性和延迟加载的特性,实现了懒汉模式。
因此,通过 IODH 实现懒汉模式,可以在需要时延迟创建单例对象,并且保证了线程安全性。

单例模式总结

单例模式作为一种目标明确、结构简单、理解容易的设计模式,在软件开发中使用频率相当高,在很多应用软件和框架中都得以广泛应用。

1.主要优点单例模式的主要优点如下:

(1)单例模式提供了对唯一实例的受控访问。因为单例类封装了它的唯一实例,所以它可以严格控制客户怎样以及何时访问它。
(2)由于在系统内存中只存在一个对象,因此可以节约系统资源。对于一些需要频繁创建和销毁的对象,单例模式无疑可以提高系统的性能。(3)允许可变数目的实例。基于单例模式,开发人员可以进行扩展,使用与控制单例对象相似的方法来获得指定个数的实例对象,既节省系统资源,又解决了由于单例对象共享过多有损性能的问题。(注:自行提供指定数目实例对象的类可称之为多例类。)

单例模式的主要缺点如下:

(1)由于单例模式中没有抽象层,因此单例类的扩展有很大的困难。
(2)单例类的职责过重,在一定程度上违背了单一职责原则。因为单例类既提供了业务方法,又提供了创建对象的方法(工厂方法),将对象的创建和对象本身的功能耦合在一起。
(3)现在很多面向对象语言(如Java、C#)的运行环境都提供了自动垃圾回收技术,因此,如果实例化的共享对象长时间不被利用,系统会认为它是垃圾,会自动销毁并回收资源,下次利用时又将重新实例化,这将导致共享的单例对象状态的丢失。

3.适用场景在以下情况下可以考虑使用单例模式:

(1)系统只需要一个实例对象。例如,系统要求提供一个唯一的序列号生成器或资源管理器,或者需要考虑资源消耗太大而只允许创建一个对象。
(2)客户调用类的单个实例只允许使用一个公共访问点。除了该公共访问点,不能通过其他途径访问该实例。

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

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

相关文章

Unity之四元数

欧拉角 万向节死锁 四元数是什么 Unity中四元数的初始化 四元数和欧拉角的互相转换 补充 四元数相乘代表旋转四元数

Linux设备驱动开发学习笔记(等待队列,锁,字符驱动程序,设备树,i2C...)

1. 内核工具和辅助函数 1.1宏container_of container_of函数可以通过结构体的成员变量检索出整个结构体 函数原型: /* pointer 指向结构体字段的指针 container_type 结构体类型 container_field 结构体字段名称 返回值是一个指针 */ container_of(pointer, con…

ARP相关

ARP报文格式: 目的以太网地址,48bit,发送ARP请求时,目的以太网地址为广播MAC地址,即0xFF.FF.FF.FF.FF.FF。 源以太网地址,48bit。 帧类型,对于ARP请求或者应答,该字段的值都为0x08…

浅谈情绪的分类合集

什么是情绪分类 情绪分类,是指区分或者对比一种情绪与另一种情绪的方法,目前在情绪研究(emotion research)与情感科学(affective science)是具有争议的问题。有两个讨论情绪分类的基本观点: 情…

架构篇07-复杂度来源:低成本、安全、规模

文章目录 低成本安全规模小结 关于复杂度来源,前面的专栏已经讲了高性能、高可用和可扩展性,今天我们来聊聊复杂度另外三个来源低成本、安全和规模。 低成本 当我们的架构方案只涉及几台或者十几台服务器时,一般情况下成本并不是我们重点关…

vue使用科大讯飞的语音识别(语音听写)

使用的是封装好的插件:voice-input-button2 真的很好使很好使 1、先使用npm下载插件 npm i voice-input-button2 -save -dev 2、在main.js中引入 import voiceInputButton from voice-input-button2 3、全局引入 Vue.use(voiceInputButton, { appId: xxx, // …

(二十)Flask之上下文管理第一篇(粗糙缕一遍源码)

每篇前言: 🏆🏆作者介绍:【孤寒者】—CSDN全栈领域优质创作者、HDZ核心组成员、华为云享专家Python全栈领域博主、CSDN原力计划作者 🔥🔥本文已收录于Flask框架从入门到实战专栏:《Flask框架从入…

RT Thread Stdio生成STM32L431RCT6工程后如何修改外部时钟

一、简介 RT Thread Stdio生成STM32L431RCT6工程后默认为内部时钟,如何修改为外部时钟呢? 二、修改时钟步骤 本方案修改外部时钟为直接修改代码,不通过STM32CubeMX 进行配置(使用这个软件会编译出错) (…

C#用Convert.ToString(Int32, Int32)和Convert.Tolnt64(String, Int32)进行数值转换

目录 一、Convert.ToString(Int32, Int32) 方法 1.定义 2. 示例 二、Convert.ToInt64(String, Int32) 1.定义 2.实例 三、用Convert.ToString(Int32, Int32)和Convert.Tolnt64(String, Int32)进行数值转换 1.Main() 2.类库 3.生成效果 使用Convert.ToString(Int32…

GPT应用程序上线注意的问题

在将GPT应用程序上线之前,有一些重要的问题需要注意,以确保应用程序的成功运行、用户满意度和合规性。以下是一些建议,希望对大家有所帮助。北京木奇移动技术有限公司,专业的软件外包开发公司,欢迎交流合作。 合规性和…

docker里Java服务执行ping命令模拟流式输出

文章目录 业务场景处理解决实现ping功能并实时返回输出实现长ping和中断请求docker容器找不到ping命令处理 业务场景 我们某市的客户,一直使用CS版本的信控平台,直接安装客户Windows server服务器上,主要对信号机设备进行在线管理、方案配时…

Windows下安装alipay-sdk-python时,pycrypto安装报错问题处理

1、安装alipay-sdk-python 时,保存内容如下。 Building wheels for collected packages: pycryptoBuilding wheel for pycrypto (setup.py) ... error error: subprocess-exited-with-error python setup.py bdist_wheel did not run successfully.│ exit c…

fastJson和jackson的日期数据处理

目录 1.jackson 2.fastjson 3.总结 1.jackson jackson是spring mvc默认的JSON解析方法,前端的数据序列化处理之后,后端经过反序列化处理可以直接使用实体对象进行接收。后端接口返回实体对象,经过序列化处理后前端可以接收并进行处理。 …

深度学习:探索人工智能的前沿

1. 引言 1.1 人工智能的演进 人工智能(Artificial Intelligence,简称AI)是一门研究如何使计算机能够执行通常需要人类智能的任务的领域。从早期的符号推理到现代的深度学习,人工智能经历了漫长的发展过程。 20世纪50年代&#xff…

Golang通过Gorm操作Mysql时遇到的datetime时区问题

情景描述 golang使用Gorm操作MySQL,MySQL中数据类型是datetime,Golang中用的是time.now。 但是会导致存储的时间与北京时间有8h误差, 显然是没有初始化时区导致。 问题修复 初始化设置时区 参考我自己之前写过的一篇总结——Mysql中多种日…

Qt应用开发(安卓篇)——Hello Qt On Android

一、前言 这一篇从实际出发,讲述如何创建、编译和部署Qt On Android项目。 二、ADB调试 ADB的全称为Android Debug Bridge,就是起到调试桥的作用,主要用于连接计算机与Android 设备,以便进行调试和数据传输。ADB 可以实现以下主要…

工作中使用Redis10种场景

前言 Redis作为一种优秀的基于key/value的缓存,有非常不错的性能和稳定性,无论是在工作中,还是面试中,都经常会出现。 今天这篇文章就跟大家一起聊聊,我在实际工作中使用Redis的10种场景,希望对你会有所帮…

快速傅里叶变化检测轻微划痕

像这种轻微划痕,普通算法鲁棒性差,通用性也不是很好,通过一些特殊处理,基本上可以满足客户需求. 图像处理,检测无非这个几个步骤. 预处理----分割----筛选—满足设定条件NG read_image (Image, ‘轻微划痕.bmp’) dev_close_window() get_image_size(Image, Width, Height) dev…

HTTPS:如何确保您的网站数据传输安全?

目录 博客前言 一.HTTPS 1.1 HTTPS简介 1.2 HTTP和HTTPS区别 1.3 TLS/SSL协议工作原理 1.3.1 TLS/SSL协议结构 1.3.2 SSL/TLS握手协议建立连接过程 1.2.3 SSL/TLS报文分析 博客前言 以下是一个关于HTTPS协议的博客前言示例: 欢迎来到我的博客,今…

vue 解决el-table 表体数据发生变化时,未重新渲染问题

效果图父组件中数量改变后总数重新计算 子组件完整代码 <template><el-tableshow-summaryref"multipleTable"v-bind"$props"selection-change"handleSelectionChange"row-click"handleRowClick":summary-method"getSum…