创建型设计模式-1.单例设计模式

创建型设计模式-1.单例设计模式

创建型设计模式:核心目的就是给我们提供了一系列全新的创建对象的方式方法

一、简介

1.简述

单例设计模式(Singleton Design Pattern),一个类只允许创建一个对象(或实例),那这个类就是一个单例类,这种设计模式就叫作单例设计模式,简称单例模式。

  • 当一个类被设计为单例类时,它只有一个全局访问点,通过该访问点可以获取该类的唯一实例。
  • 使用单例模式的主要目的是在整个应用程序中共享一个共享资源或控制对唯一实例的访问。
  • 通过限制对象的创建,单例模式确保系统中只有一个实例存在,并提供对该实例的全局访问,从而避免了重复创建对象的开销。

2.使用场景

单例模式适用于以下场景:

  • 当只需要一个实例来协调某个操作时,如线程池、日志记录器或缓存管理器。
  • 当需要控制资源使用并确保全局访问时,如数据库连接池或文件管理器。
  • 当一个对象需要被频繁地访问,但创建和销毁实例的开销较大时。

3.优点和缺点

单例模式的优点包括:

  • 提供全局访问点,方便对实例进行统一的管理和控制。
  • 减少了重复创建对象的开销,提高了系统的性能。
  • 确保在整个应用程序中只有一个实例存在,避免了资源的浪费和冲突。

然而,单例模式也有一些潜在的缺点:

  • 单例模式可能会引入全局状态,增加了程序的复杂性和耦合度。
  • 单例模式的扩展性有限,如果需要修改单例类的实现,可能需要修改大量的代码。
  • 单例模式在多线程环境下需要注意线程安全性的问题,需要确保实例的创建和访问是线程安全的。

总之,单例模式是一种强大的设计模式,适用于需要共享资源或控制实例访问的情况。通过合理地使用单例模式,可以提高应用程序的性能、可维护性和可扩展性。

4.使用步骤

单例设计模式是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来访问该实例。以下是创建一个单例的一般步骤:

  1. 私有化构造函数:将类的构造函数私有化,这样其他类就无法直接实例化该类的对象。

  2. 创建静态私有实例变量:在类的内部创建一个静态私有变量,用于存储类的唯一实例。

  3. 创建静态公有方法:提供一个公有的静态方法,用于获取类的实例。这个方法会在内部检查是否已经存在实例,如果存在则直接返回实例,如果不存在则创建一个实例并返回。

下面是一个简单的例子来说明单例设计模式的步骤:

public class Singleton {private static Singleton instance;  // 静态私有实例变量private Singleton() {// 私有化构造函数}public static Singleton getInstance() {if (instance == null) {  // 检查是否已经存在实例instance = new Singleton();  // 创建实例}return instance;  // 返回实例}
}

在上述代码中,构造函数被声明为私有的,确保其他类无法直接实例化Singleton类的对象。instance变量是一个静态私有变量,用于存储类的唯一实例。getInstance()方法是一个静态公有方法,用于获取Singleton类的实例。在该方法中,首先检查instance是否为null,如果为null则创建一个新的实例,否则直接返回已有的实例。

使用单例模式时,其他类可以通过调用Singleton.getInstance()来获取Singleton类的实例,并且每次调用都会返回同一个实例。这样可以确保在整个程序中只有一个Singleton对象存在。

二、案例

1.饿汉式单例

单例设计模式-饿汉式:

饿汉式是一种简单直接的单例模式实现方式。**在类加载的时候就创建了唯一的实例对象,并在整个生命周期中保持不变。**因此,它也被称为“急切”创建实例的方式。

public class HgySingleton {private static final HgySingleton INSTANCE = new HgySingleton();private HgySingleton() {}public static HgySingleton getInstance() {return INSTANCE;}
}

2.懒汉式单例

单例设计模式-懒汉式:

懒汉式是一种延迟实例化的方式,在第一次访问获取实例的时候才会创建对象。这种方式在实例创建之前没有额外的开销,但需要考虑多线程环境下的线程安全问题

public class LazySingleton {private static LazySingleton instance = null;private LazySingleton() {}public static LazySingleton getInstance() {if (instance == null) {instance = new LazySingleton();}return instance;}
}

3.双重检查锁单例

单例设计模式-双重检查锁:

双重检查锁是对懒汉式的改进,通过在获取实例时进行双重检查,确保只在实例未创建时才进行同步操作。这样在多线程环境下可以保持高性能并且线程安全。

public class DclSingleton {private static volatile DclSingleton INSTANCE;private DclSingleton() {}public static DclSingleton getInstance() {if (INSTANCE == null) {synchronized (DclSingleton.class) {if (INSTANCE == null) {INSTANCE = new DclSingleton();}}}return INSTANCE;}
}

4.内部类单例

单例设计模式-静态内部类:

静态内部类方式是一种延迟加载实例的方式,它利用了Java类加载机制中的静态内部类不会在外部类加载时被加载的特性。当第一次获取实例时,静态内部类才会被加载并创建实例。

public class InnerSingleton {private InnerSingleton() {}public static InnerSingleton getInstance() {return SingletonHolder.INSTANCE;}private static class SingletonHolder {private static final InnerSingleton INSTANCE = new InnerSingleton();}
}

5.枚举类单例

单例设计模式-枚举类:

枚举类方式是Java中最简洁、高效且线程安全的单例模式实现方式。枚举类的实例是在枚举常量被第一次访问时创建的,而且它天生就是线程安全的。

public class EnumSingleton {private EnumSingleton() {// 私有构造函数}public static EnumSingleton getInstance() {return SingletonEnum.INSTANCE.getInstance();}private static enum SingletonEnum {INSTANCE;private EnumSingleton instant;// 在枚举常量中初始化单例实例private SingletonEnum() {instant = new EnumSingleton();}public EnumSingleton getInstance() {return instant;}}
}

枚举可以抵挡常规的反射攻击是因为Java语言规范对枚举类进行了特殊处理。当使用反射尝试访问枚举类的私有构造函数时,会抛出IllegalAccessException异常,阻止了对枚举类进行实例化的尝试。

这种反射抵御是通过Java语言规范的设计来实现的。在枚举类中,构造函数被默认为私有的,不允许外部代码直接访问。当使用反射时,如果尝试通过Constructor.newInstance()方法来调用枚举类的私有构造函数,Java会在内部检查是否为枚举类,并抛出异常。

此外,枚举常量在枚举类被加载时就被实例化,并且是在枚举类初始化阶段完成的。这意味着在第一次访问枚举类时,所有的枚举常量都会被创建,而且无法再通过反射来创建新的枚举常量实例。

综上所述,由于枚举类的特殊处理和枚举常量的提前实例化,枚举可以有效地抵挡常规的反射攻击,确保枚举类的单例特性和唯一性。这也是为什么使用枚举实现单例模式是一种安全可靠的方式。然而,需要注意的是,反射仍然可以访问枚举类的其他成员变量和方法,只是无法通过反射创建新的枚举常量实例。

单例设计模式-反射和序列化:

使用常规的单例模式实现可能会受到反射和序列化的影响而产生多个实例。为了防止这种情况,需要在单例类中做特殊处理,确保在使用反射和序列化时依然保持单例特性。

public class ReflectSerializableSingleton implements Serializable {private static volatile ReflectSerializableSingleton INSTANCE;private ReflectSerializableSingleton() {if (INSTANCE != null) {throw new RuntimeException("实例:【" + this.getClass().getName() + "】已经存在,该实例只允许实例化一次");}}public static ReflectSerializableSingleton getInstance() {if (INSTANCE == null) {synchronized (ReflectSerializableSingleton.class) {if (INSTANCE == null) {INSTANCE = new ReflectSerializableSingleton();}}}return INSTANCE;}// readResolve()方法可以用于替换从流中读取的对象,在进行反序列化时,会尝试执行readResolve方法,并将返回值作为反序列化的结果,而不会克隆一个新的实例,保证jvm中仅仅有一个实例存在public Object readResolve(){return singleton;}
}

三、总结

1.应用

单例模式的应用:

  1. jdk中有一个类的实现是一个标准单例模式->Runtime类,该类封装了运行时的环境。每个 Java 应用程序都有一个 Runtime 类实例,使应用程序能够与其运行的环境相连接。 一般不能实例化一个Runtime对象,应用程序也不能创建自己的 Runtime 类实例,但可以通过 getRuntime 方法获取当前Runtime运行时对象的引用。
  2. Mybaits中的org.apache.ibatis.io.VFS使用到了单例模式。VFS就是Virtual File System的意思,mybatis通过VFS来查找指定路径下的资源。查看VFS以及它的实现 类,不难发现,VFS的角色就是对更“底层”的查找指定资源的方法的封装,将复杂的 “底层”操作封装到易于使用的高层模块中,方便使用者使用。

2.存在问题

单例存在的问题:

  1. 无法支持面向对象编程:单例模式的构造方法被私有化,这导致无法将单例类作为其他类的父类,限制了继承和多态的特性。这意味着在面对未来的需求变化时,扩展性受到限制。如果需要创建类似但略有差异的单例,就需要重新创建一个相似且大部分功能相同的单例类,造成代码冗余。

  2. 极难的横向扩展:单例模式只允许存在一个实例对象,如果未来需要创建更多实例以满足不同需求,就必须修改源代码。这违反了开闭原则,增加新的实例需要对现有代码进行修改,导致扩展困难。例如,在数据库连接池中,可能从一个连接变成需要多个连接。

  3. 不同作用范围的单例

    1. 线程级别的单例:每个线程都拥有自己的单例实例,线程之间互不干扰。
    2. 容器范围的单例:在容器中管理单例实例,容器可以管理多个单例对象并控制其生命周期。
    3. 日志中的多例:在日志记录中,可能需要根据不同的上下文创建多个实例来记录不同的日志信息。

请注意,尽管单例模式存在一些缺点,但在某些情况下仍然是有用的设计模式。在使用单例模式时,需要仔细考虑其适用性和潜在的问题,并根据具体情况做出权衡决策。

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

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

相关文章

nginx+lua+redis环境搭建(文末赋上脚本)

目录 需求背景 环境搭建后nginx和redis版本 系统环境 搭建步骤 配置服务器DNS 安装ntpdate同步一下系统时间 安装网络工具、编译工具及依赖库 创建软件包下载目录、nginx和redis安装目录 下载配置安装lua解释器LuaJIT 下载nginx NDK(ngx_devel_kit&#xff09…

ceph安装部署

Ceph 简介 存储基础 单机存储设备 单机存储的问题 分布式存储的类型 分布式存储(软件定义的存储 SDS) Ceph 架构 Ceph 核心组件 ​编辑 Pool中数据保存方式支持两种类型 OSD 存储后端 Ceph 数据的存储过程 Ceph 集群部署 基于 ceph-deploy …

网络运维能转型到系统运维吗?

很多网工处于刚起步的初级阶段,各大公司有此专职,但重视或重要程度不高,可替代性强;小公司更多是由其它岗位来兼顾做这一块工作,没有专职,也不可能做得深入。 现在开始学习入门会有一些困难,不…

Hyperledger Fabric测试网络运行官方Java链码[简约版]

文章目录 启动测试网络使用peer CLI测试链码调用链码 启动测试网络 cd fabric-samples/test-networknetwork.sh的脚本语法是&#xff1a;network.sh <mode> [flag] ./network.sh up./network.sh createChannel在java源码路径下 chmod 744 gradlew vim gradlew :set ffu…

[SSM]GoF之工厂模式

目录 六、GoF之工厂模式 6.1工厂模式的三种形态 6.2简单工厂模式 6.3工厂方法模式 6.4抽象工厂模式&#xff08;了解&#xff09; 六、GoF之工厂模式 设计模式&#xff1a;一种可以被重复利用的解决方案 GoF&#xff08;Gang of Four)&#xff0c;中文名——四人组。 该书…

阿里云服务器 用docker部署mysql

阿里云服务器上使用Docker部署MySQL 当您在阿里云服务器上使用Docker部署MySQL时&#xff0c;步骤如下&#xff1a; 登录到阿里云服务器&#xff1a;使用SSH工具登录到您的阿里云服务器。您可以使用命令行工具&#xff08;如OpenSSH&#xff09;或可视化工具&#xff08;如PuT…

银河麒麟高级服务器操作系统V10安装mysql数据库

一、安装前 1.检查是否已经安装mysql rpm -qa | grep mysql2.将查询出的包卸载掉 rpm -e --nodeps 文件名3.将/usr/lib64/libLLVM-7.so删除 rm -rf /usr/lib64/libLLVM-7.so4.检查删除结果 rpm -qa | grep mysql5.搜索残余文件 whereis mysql6.删除残余文件 rm -rf /usr/b…

利用JavaScript判断页面宽度的响应式布局方法

首先前端中&#xff0c;样式尺寸单位全部用rem&#xff0c;比如&#xff1a; .content{ width: 8rem; border-radius: 0.15rem; font-size: 0.95rem; letter-spacing: 0.15rem; &#xff5d; 接着页面中的html给个默认的font-size样式&#xff0c;比如&#xff1a; <html …

MYSQL表操作(DML,DDL)

建表并插入数据&#xff1a; mysql> create table worker(-> dept_id int(11) not null,-> emp_id int (11) not null,-> work_time date not null,-> salary float(8,2) not null,-> poli_face varchar(10) not null default 群众,-> name varchar(20) …

一种对不同类型齐格勒-尼科尔斯 P-I-D 控制器调谐算法研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

MySQL数据库 【增删改查】

目录 一、新增 指定列插入 一次插入多个数据 二、查询 1、全列查询 2、指定列查询 3、查询字段为表达式 4、查询的时候给列名/表达式 指定别名 5、查询时去重 6、排序查询 7、条件查询 8、模糊查询 9、空值查询 10、分页查询 三、修改 四、删除 SQL 最核心…

11、JSON.parse 数据不完整

一、问题描述 使用 JSON.parse 反序列化&#xff0c;出现数据丢失现象。 字符串json数据&#xff1a; {"varImageList": [{"variationValue": "Black ","imageList": [{"variationValue": "Black ","imag…

JS-26 认识防抖和节流函数;自定义防抖、节流函数;自定义深拷贝、事件总线函数

目录 1_防抖和节流1.1_认识防抖和节流函数1.2_认识防抖debounce函数1.3_防抖函数的案例1.4_认识节流throttle函数 2_Underscore实现防抖和节流2.1_Underscore实现防抖和节流2.2_自定义防抖函数2.3_自定义节流函数 3_自定义深拷贝函数4_自定义事件总线 1_防抖和节流 1.1_认识防…

你是不是一个好的测试工程师?

如何评价一个程序员是否优秀一直是一个很有争议的话题。 先说一个真实事件&#xff0c;国际化项目&#xff0c;最开始都是由产品经理在excel中管理翻译&#xff0c;迭代过程中如有增删改&#xff0c;就把增删改的部分标记出来&#xff0c;提供给开发&#xff0c;开发再对应更新…

关于gateway中lb失效

在通过gateway将请求发送到对应的服务模块时&#xff0c;出现了503的报错&#xff0c;也就是gateway时可以正常启动&#xff0c;但是页面上在发送请求获取数据的时候&#xff0c;却不是相应的请求地址。 解决方法&#xff1a; 1.首先你得保证前端项目里面访问网关地址都是正确…

【Netty】NIO基础(三大组件、文件编程)

文章目录 三大组件Channel & BufferSelector ByteBufferByteBuffer 正确使用姿势ByteBuffer 内部结构ByteBuffer 常见方法分配空间向 buffer 写入数据从 buffer 读取数据mark 和 reset 字符串与 ByteBuffer 互转Scattering ReadsGathering Writes粘包、半包分析 文件编程Fi…

vue 当新增样式无法生效的情况下如何处理

使用scoped属性时&#xff0c;会遇到样式问题。需要使用样式穿透解决 <style lang"scss" scoped> </style> 可以使用以下方法 &#xff1a;deep css 使用 >>> less 使用 /deep/ scss 使用 ::v-deep 代码写法如下: .a :deep(.b) { } .…

v-show和v-if的区别以及显示隐藏不生效的奇怪现象以及点击索引错位问题的解释

基本概念没什么好讲的。有时候会遇到莫名其妙不显示的问题&#xff0c;这都是因为对这两个概念理解不透彻造成的。 v-show的本质 v-show的本质就是通过调用css的display:none来实现的&#xff0c;这点非常重要&#xff0c;出问题可以在浏览器调试页面手动设置display:none来验…

Mac的docker安装redis

Mac的docker安装redis 1、docker search redis NAME DESCRIPTION STARS OFFICIAL AUTOMATED redis Redis is an open source key-value store that… 12205 …

mysql、redis 、RabbitMQ只能本机访问,怎么改?

如果只能本机访问&#xff0c;怎么改? 一、mysql - 改my.ini 刷脚本 bind-address0.0.0.0 然后重启一下mysql服务 任务管理器-关掉mysql 搜索 计算机管理-重启mysql服务 然后 打开查询&#xff0c;并选择mysql数据&#xff0c;输入这个sql语句&#xff0c;点击运行 sele…