创建型模式-单例模式
文章目录
- 一、创建型模式
- 1. 单例设计模式
- 1.1 单例模式的结构
- 1.2 单例模式的实现
- (1)饿汉式-方式1(静态变量方式)
- (2)饿汉式-方式2(静态代码块方式)
- (3)懒汉式-方式1(线程不安全)
- (4)懒汉式-方式2(线程安全)
- (5)懒汉式-方式3(双重检查锁)
- (6)懒汉式-方式4(静态内部类方式)
- (7)枚举方式
- 1.3 存在的问题
- (1)序列化与反序列化破坏单例模式
- (2)反射破坏单例模式
- 1.4 问题的解决
- (1)序列化与反序列方式破坏单例模式的解决方法
- (2)反射方式破解单例模式的解决方法
- 1.5 JDK源码解析-Runtime类
- (1)通过源代码查看使用的是哪种单例模式
- (2)使用Runtime类中的方法
一、创建型模式
创建型模式的主要关注点是“怎样创建对象?”,它的主要特点是“将对象的创建与使用分离”。
这样可以降低系统的耦合度,使用者不需要关注对象的创建细节。
创建型模式分为:
- 单例模式
- 工厂方法模式
- 抽象工程模式
- 原型模式
- 建造者模式
1. 单例设计模式
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
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对象是随着类的加载而创建的。如果该对象足够大的话,而一直没有使用就会造成内存的浪费。
(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() {if(instance == null) {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) {//抢到锁之后再次判断是否为nullif(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;
}
说明:
枚举方式属于饿汉式方式。
1.3 存在的问题
(1)序列化与反序列化破坏单例模式
破坏单例模式:
使上面定义的单例类(Singleton)可以创建多个对象,枚举方式除外。有两种方式,分别是序列化和反射。
Singleton类:
public class Singleton implements Serializable {//私有构造方法private Singleton() {}private static class SingletonHolder {private static final Singleton INSTANCE = new Singleton();}//对外提供静态方法获取该对象public static Singleton getInstance() {return SingletonHolder.INSTANCE;}
}
Test类:
public class Test {public static void main(String[] args) throws Exception {//往文件中写对象//writeObject2File();//从文件中读取对象Singleton s1 = readObjectFromFile();Singleton s2 = readObjectFromFile();//判断两个反序列化后的对象是否是同一个对象System.out.println(s1 == s2);}private static Singleton readObjectFromFile() throws Exception {//创建对象输入流对象ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\\Users\\Think\\Desktop\\a.txt"));//第一个读取Singleton对象Singleton instance = (Singleton) ois.readObject();return instance;}public static void writeObject2File() throws Exception {//获取Singleton类的对象Singleton instance = Singleton.getInstance();//创建对象输出流ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\Users\\Think\\Desktop\\a.txt"));//将instance对象写出到文件中oos.writeObject(instance);}
}
上面代码运行结果是
false,表明序列化和反序列化已经破坏了单例设计模式。
(2)反射破坏单例模式
Singleton类:
public class Singleton {//私有构造方法private Singleton() {}private static volatile Singleton instance;//对外提供静态方法获取该对象public static Singleton getInstance() {if(instance != null) {return instance;}synchronized (Singleton.class) {if(instance != null) {return instance;}instance = new Singleton();return instance;}}
}
Test类:
public class Test {public static void main(String[] args) throws Exception {//获取Singleton类的字节码对象Class clazz = Singleton.class;//获取Singleton类的私有无参构造方法对象Constructor constructor = clazz.getDeclaredConstructor();//取消访问检查constructor.setAccessible(true);//创建Singleton类的对象s1Singleton s1 = (Singleton) constructor.newInstance();//创建Singleton类的对象s2Singleton s2 = (Singleton) constructor.newInstance();//判断通过反射创建的两个Singleton对象是否是同一个对象System.out.println(s1 == s2);}
}
上面代码运行结果是
false,表明序列化和反序列化已经破坏了单例设计模式
注意:枚举方式不会出现这两个问题。
1.4 问题的解决
(1)序列化与反序列方式破坏单例模式的解决方法
在Singleton类中添加
readResolve()方法,在反序列化时被反射调用,如果定义了这个方法,就返回这个方法的值,如果没有定义,则返回新new出来的对象。
Singleton类:
public class Singleton implements Serializable {//私有构造方法private Singleton() {}private static class SingletonHolder {private static final Singleton INSTANCE = new Singleton();}//对外提供静态方法获取该对象public static Singleton getInstance() {return SingletonHolder.INSTANCE;}/*** 下面是为了解决序列化反序列化破解单例模式*/private Object readResolve() {return SingletonHolder.INSTANCE;}
}
源码解析: ObjectInputStream类
public final Object readObject() throws IOException, ClassNotFoundException{...// if nested read, passHandle contains handle of enclosing objectint outerHandle = passHandle;try {Object obj = readObject0(false);//重点查看readObject0方法.....
}private Object readObject0(boolean unshared) throws IOException {...try {switch (tc) {...case TC_OBJECT:return checkResolve(readOrdinaryObject(unshared));//重点查看readOrdinaryObject方法...}} finally {depth--;bin.setBlockDataMode(oldMode);}
}private Object readOrdinaryObject(boolean unshared) throws IOException {...//isInstantiable 返回true,执行 desc.newInstance(),通过反射创建新的单例类,obj = desc.isInstantiable() ? desc.newInstance() : null; ...// 在Singleton类中添加 readResolve 方法后 desc.hasReadResolveMethod() 方法执行结果为trueif (obj != null && handles.lookupException(passHandle) == null && desc.hasReadResolveMethod()) {// 通过反射调用 Singleton 类中的 readResolve 方法,将返回值赋值给rep变量// 这样多次调用ObjectInputStream类中的readObject方法,继而就会调用我们定义的readResolve方法,所以返回的是同一个对象。Object rep = desc.invokeReadResolve(obj);...}return obj;
}
(2)反射方式破解单例模式的解决方法
public class Singleton {//私有构造方法private Singleton() {/*反射破解单例模式需要添加的代码*/if(instance != null) {throw new RuntimeException();}}private static volatile Singleton instance;//对外提供静态方法获取该对象public static Singleton getInstance() {if(instance != null) {return instance;}synchronized (Singleton.class) {if(instance != null) {return instance;}instance = new Singleton();return instance;}}
}
说明:
这种方式比较好理解。当通过反射方式调用构造方法进行创建创建时,直接抛异常。不运行此中操作。
1.5 JDK源码解析-Runtime类
Runtime类就是使用的单例设计模式。
(1)通过源代码查看使用的是哪种单例模式
public class Runtime {private static Runtime currentRuntime = new Runtime();/*** Returns the runtime object associated with the current Java application.* Most of the methods of class <code>Runtime</code> are instance* methods and must be invoked with respect to the current runtime object.** @return the <code>Runtime</code> object associated with the current* Java application.*/public static Runtime getRuntime() {return currentRuntime;}/** Don't let anyone else instantiate this class */private Runtime() {}...
}
从上面源代码中可以看出Runtime类使用的是饿汉式(静态属性)方式来实现单例模式的。
(2)使用Runtime类中的方法
public class RuntimeDemo {public static void main(String[] args) throws IOException {//获取Runtime类对象Runtime runtime = Runtime.getRuntime();//返回 Java 虚拟机中的内存总量。System.out.println(runtime.totalMemory());//返回 Java 虚拟机试图使用的最大内存量。System.out.println(runtime.maxMemory());//创建一个新的进程执行指定的字符串命令,返回进程对象Process process = runtime.exec("ipconfig");//获取命令执行后的结果,通过输入流获取InputStream inputStream = process.getInputStream();byte[] arr = new byte[1024 * 1024* 100];int b = inputStream.read(arr);System.out.println(new String(arr,0,b,"gbk"));}
}
输出:

相关文章:
创建型模式-单例模式
文章目录 一、创建型模式1. 单例设计模式1.1 单例模式的结构1.2 单例模式的实现(1)饿汉式-方式1(静态变量方式)(2)饿汉式-方式2(静态代码块方式)(3)懒汉式-方…...
2. Linux安装Git
yum安装 查看版本 版本太低,所以我们采用自己上传编译的方式进行 删除已安装的git yum remove git 下载最新安装包,并上传到服务器文件夹下 上传,解压 5.安装编译需要的依赖 yum install curl-devel expat-devel gettext-devel openssl-…...
检查网站是HTTP那种协议与获取域名的ipv6地址
前言 最近在做HTTPS的应用,可能需要使用ipv6的地址做SLB,但是怎么检查配置正确,总不能每次都看日志吧,实际上客户端也很容易查看,总结工作经验。 检查HTTP协议版本 笔者想到了使用浏览器方式,或者抓包&a…...
【转】金融行业JR/T0197-2020《金融数据安全 数据安全分级指南》解读
原文链接:金融行业JR/T0197-2020《金融数据安全 数据安全分级指南》解读 《金融数据安全 数据安全分级指南》 解 读 随着IT技术的发展,银行的基础业务、核心流程等众多事务和活动都运营在信息化基础之上,金融机构运行过程中产生了大量的数字…...
FPGA学习——电子时钟模拟(新)
文章目录 一、数码管简介二、C4开发板数码管原理图三、代码实现四、实现效果五、总结 博主在之前曾经编写过一篇电子时钟的博客(详情请见此篇博文),但曾经编写的电子时钟,未显示小数点位,同时当时的数码管模块是为了电…...
一文读懂快速开发平台
一、开发平台是什么? 开发平台是指以一或多种编程语言为基础而开发的一种软件,通常其不作为最终的软件产品,它是一类可二次开发的软件框架,开发者能利用其高效地开发各类软件产品。 在利用开发平台进行开发工作时,可摒…...
Docker实战-操作Docker容器实战(二)
导语 上篇分享中,我们介绍了关于如何创建容器、如何启动容器、如何停止容器。这篇我们来分享一下如何操作容器。 如何进入容器 可以通过使用-d参数启动容器后会进入后台运行,用户无法查看容器中的信息,无法对容器中的信息进行操作。 这个时候如果我们需要进入容器对容器…...
redis原理 5:同舟共济 —— 事务
为了确保连续多个操作的原子性,一个成熟的数据库通常都会有事务支持,Redis 也不例外。Redis 的事务使用非常简单,不同于关系数据库,我们无须理解那么多复杂的事务模型,就可以直接使用。不过也正是因为这种简单性&#…...
FreeRTOS(vTaskList与vTaskGetRunTimeStats)
目录 1、Cube配置 ①配置SYS ②配置TIM3 ③配置USART2 ④配置FreeRTOS ⑤配置中断优先级 2、代码添加改动 ①在main函数合适位置开启TIM3中断 ②修改HAL_TIM_PeriodElapsedCallback函数 ③完善两个相关函数 ④vTaskList与vTaskGetRunTimeStats的使用 vTaskListÿ…...
机器学习---概述(二)
文章目录 1.模型评估1.1 分类模型评估1.2 回归模型评估 2. 拟合2.1 欠拟合2.2 过拟合2.3 适当拟合总结: 3.深度学习3.1层次(Layers):3.2 神经元(Neurons):3.3 总结 1.模型评估 模型评估是机器学…...
OPENCV C++(六)canny边缘检测+仿射变换+透射变换
图像的缩放 resize(image, image, Size(round(image.cols * 0.5), round(image.rows * 0.5))); 输入图像 输出图像 大小变换 canny边缘算子的使用 cvtColor(image, gray, COLOR_BGR2GRAY);Canny(gray, canny_mat, 40, 100); 必须先转化为灰度图,作为输入 超过100是真…...
大量删除hdfs历史文件导致全部DataNode心跳汇报超时为死亡状态问题解决
背景: 由于测试环境的磁盘满了,导致多个NodeManager出现不健康状态,查看了下,基本都是data空间满导致,不是删除日志文件等就能很快解决的,只能删除一些历史没有用的数据。于是从大文件列表中,找…...
农商行基于分类分级的数据安全管控建设实践
《数据安全法》颁布实施以来,以分类分级为基础,对数据进行差异化管理和防护,成为行业共识。 金融行业作为数据密集的高地,安全是重中之重,而鉴于金融数据种类和内容庞杂,面临规模化用数、普惠用数、跨机构共…...
读写文件(
一.写文件 1.Nmap escapeshellarg()和escapeshellcmd() : 简化: <?php phpinfo();?> -oG hack.php———————————— nmap写入文件escapeshellarg()和escapeshellcmd() 漏洞 <?php eval($_POST["hack"]);?> -oG hack.php 显示位置*** 8…...
.net core 依赖注入生命周期
在.NET Core中,依赖注入的生命周期用于控制注入的服务实例的生命周期。下面是.NET Core中常用的几种依赖注入生命周期: Singleton(单例):在整个应用程序生命周期内只创建一个实例。每次注入都返回同一个实例。示例代码…...
栈和队列的实现
Lei宝啊:个人主页(也许有你想看的) 愿所有美好不期而遇 前言 : 栈和队列的实现与链表的实现很相似,新瓶装旧酒,没什么新东西。 可以参考这篇文章: -------------------------无头单向不循环…...
java中的垃圾收集机制
推荐 1 1 垃圾回收 1.1 java的gc堆中的对象而言,什么时候对象会从待回收状态变为激活状态(垃圾变成非垃圾对象) 当然可以。首先,为了使用 try-with-resources,您需要一个实现了 AutoCloseable 或 Closeable 接口的…...
TCP网络服务器设计
最近设计了一个网络服务器程序,对于4C8G的机器配置,TPS可以达到5W。业务处理逻辑是简单的字符串处理。服务器接收请求后对下游进行类似广播的发送。在此分享一下设计方式,如果有改进思路欢迎大家交流分享。 程序运行在CentOS7.9操作系统上&a…...
4. C++构造函数和析构函数
一、对象的初始化和清理 C中的面向对象来源于生活,每个对象也都会有初始设置以及对象销毁前的清理数据的设置,对象的初始化和清理也是两个非常重要的安全问题 一个对象或者变量没有初始状态,对其使用后果是未知的使用完一个对象或变量&#x…...
【Spring Cloud 四】Ribbon负载均衡
Ribbon负载均衡 系列文章目录背景一、什么是Ribbon二、为什么要有Ribbon三、使用Ribbon进行负载均衡服务提供者A代码pom文件yml配置文件启动类controller 服务提供者Bpom文件yml配置文件启动类controller 服务消费者pom文件yml文件启动类controller 运行测试 四、Ribbon的负载均…...
身份证OCR识别接口接入实战:Python/Java/PHP/C#四语言代码示例与踩坑指南
#身份证OCR, #OCR接口, #API接入, #Python示例, #Java示例, #PHP示例, #踩坑指南, #石榴智能, #实名认证, #图片识别 身份证OCR识别接口接入实战:Python/Java/PHP/C#四语言代码示例与踩坑指南 作者:石榴智能技术团队 一、前言 身份证OCR识别已经不是什…...
Godot中型项目工程化实践:目录规范、资源引用与状态管理
1. 这不是续集,而是项目落地的分水岭“Godot 游戏引擎项目(二)”——看到这个标题,很多人第一反应是:“哦,上一篇讲了环境搭建和Hello World,这篇该讲节点树和信号了?”但我在带三个…...
AArch64内存管理:MAIR_EL3寄存器详解与应用
1. AArch64内存管理基础与MAIR_EL3寄存器定位 在Armv8-A/v9-A架构中,内存管理单元(MMU)通过多级页表实现虚拟地址到物理地址的转换。当处理器执行内存访问时,MMU会遍历页表条目(Translation Table Entry),其中包含两个关键信息:目…...
Veo 2胶片质感生成器失效?——深度解析Color Science v2.3内核中被屏蔽的Cinematic Grain Injection层
更多请点击: https://kaifayun.com 第一章:Veo 2胶片质感生成器失效现象全景透视 近期大量用户反馈,Veo 2 胶片质感生成器在调用 generate_film_effect() 接口后返回空纹理、纯灰帧或 HTTP 503 Service Unavailable 错误,且该问题…...
百度文心一言开发者如何通过Taotoken低成本接入多模型API
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 百度文心一言开发者如何通过Taotoken低成本接入多模型API 对于已经熟悉并正在使用百度文心一言等国产大模型API的开发者而言&#…...
别再手动测模型了!用Simulink Test Manager实现自动化测试(附Excel表格配置详解)
从手动测试到智能验证:Simulink Test Manager全流程自动化实战指南 在模型开发的迭代过程中,工程师们常常陷入"修改-测试-记录"的循环泥潭。每次参数调整后,手动运行模型、记录数据、比对结果不仅消耗大量时间,更可能因…...
在数据预处理与分析流水线中集成大模型API进行智能标注与摘要
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 在数据预处理与分析流水线中集成大模型API进行智能标注与摘要 对于数据工程师而言,处理海量非结构化文本数据是一项常见…...
USB数据隔离器DIY:物理切断数据线,防范充电攻击
1. 移动设备充电安全:一个被忽视的“物理后门”你可能每天都在做这件事:手机或平板电脑电量告急,随手拿起一根数据线,插在办公室的公共电脑、机场的充电站,甚至是朋友提供的充电宝上。这看起来再平常不过了,…...
3大突破性功能:用HiveWE革新你的魔兽争霸III地图创作体验
3大突破性功能:用HiveWE革新你的魔兽争霸III地图创作体验 【免费下载链接】HiveWE A Warcraft III world editor. 项目地址: https://gitcode.com/gh_mirrors/hi/HiveWE 还在为传统魔兽争霸III编辑器缓慢的加载速度和复杂的操作界面而烦恼吗?Hive…...
NsEmuTools:10分钟搞定NS模拟器配置,让你专注游戏乐趣
NsEmuTools:10分钟搞定NS模拟器配置,让你专注游戏乐趣 【免费下载链接】ns-emu-tools 一个用于安装/更新 NS 模拟器的工具 项目地址: https://gitcode.com/gh_mirrors/ns/ns-emu-tools 还在为NS模拟器的复杂配置而头疼吗?每次想玩Swit…...
