JAVA - 单例设计模式

JAVA - 单例设计模式

    • 一. 简介
    • 二. 单例模式的原则
    • 三. 单例模式的实现
      • 1.1 饿汉式
        • 1.1.1 静态变量初始化方式
        • 1.1.2 静态代码块初始化方式
        • 1.1.3 枚举方式
      • 1.2 懒汉式
        • 1.2.1 懒加载初始化方法 (线程不安全)
        • 1.2.2 懒加载初始化方法 (线程安全)
        • 1.2.3 双重检查锁
        • 1.2.4 静态内部类方式

前言

这是我在这个网站整理的笔记,有错误的地方请指出,关注我,接下来还会持续更新。 作者:神的孩子都在歌唱

一. 简介

单例模式(Singleton Pattern的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

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

二. 单例模式的原则

  • 单例模式限制类的实例化,并确保Java虚拟机中只存在该类的一个实例。
  • 单例类必须提供一个全局访问点来获取该类的实例。
  • 单例模式用于日志记录、驱动对象、缓存和线程池
  • 单例设计模式还用于其他设计模式,如抽象工厂等。
  • 单例设计模式也用在核心 Java 类中(例如,java.lang.Runtimejava.awt.Desktop)。

三. 单例模式的实现

单例设计模式分类两种:

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

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

​ 简单理解:在自己身体里面创建自己,给外部调用

1.1 饿汉式

1.1.1 静态变量初始化方式

在系统初始化时候,单例类的实例是在类加载时创建的。静态变量方式初始化的缺点是,即使客户端应用程序可能没有使用该方法,也会创建该方法。这是静态初始化单例类的实现:

/*** @author: 那就叫小智吧* @date: 2022/3/3 15:41* @Description:  静态变量创建类对象* 	该方式在成员位置声明Singleton类型的静态变量,并创建Singleton类的对象instance。instance对象是随着类的加载而创建的。如果该对象足够大的话,而一直没有使用就会造成内存的浪费。*/
public class Singleton1 {// 私有构造方法private Singleton1(){System.out.println("通过静态变量创建类对象");}// 在成员位置创建该类的对象private static Singleton1 instance= new Singleton1();// 对外提供静态方法获取该对象public static Singleton1 getInstance(){return instance;}
}

如果您的单例类没有使用大量资源,则可以使用这种方法。但在大多数情况下,单例类是为文件系统数据库连接等资源创建的。除非客户端调用该getInstance方法,否则我们应该避免实例化。此外,此方法不提供任何异常处理选项。

1.1.2 静态代码块初始化方式

静态代码块方式实现与静态变量初始化方式类似,不同之处在于类的实例是在提供异常处理选项的静态块中创建的。

/*** @author: 那就叫小智吧* @date: 2022/3/3 15:49* @Description: 在静态代码块中创建该类对象* 该方式在成员位置声明Singleton类型的静态变量,而对象的创建是在静态代码块中,也是对着类的加载而创建。当然该方式也存在内存浪费问题。*/
public class Singleton2 {// 私有构造方法private Singleton2(){System.out.println("在静态代码块中创建该类对象");};// 在成员位置声明静态变量private static Singleton2 instance;static {try {instance = new Singleton2();} catch (Exception e) {throw new RuntimeException("创建单例实例时发生异常");}}// 对外提供静态方法获取该对象public static Singleton2 getInstance(){return instance;}}

静态代码块方式实现与静态变量初始化方式 都会在使用实例之前创建实例,但这不是最佳实践。

1.1.3 枚举方式
/*** @author: 那就叫小智吧* @date: 2022/3/3 16:36* @Description: 恶汉式* 枚举类实现单例模式是极力推荐的单例实现模式,因为枚举类型是线程安全的,并且只会装载一次,设计者充分的利用了枚举的这个特性来实现单例模式,枚举的写法非常简单,而且枚举类型是所用单例实现中唯一一种不会被破坏的单例实现模式。*/
public enum Singleton {INSTANCE;
}

使用enumJava 来实现单例设计模式,以确保任何enum值在 Java 程序中仅实例化一次。由于Java 枚举值是全局可访问的,因此单例也是如此。缺点是enum类型有些不灵活(例如,它不允许延迟初始化)。

1.2 懒汉式

1.2.1 懒加载初始化方法 (线程不安全)
/*** @author: 那就叫小智吧* @date: 2022/3/3 15:59* @Description: 懒汉式 :线程不安全* 从下面面代码我们可以看出该方式在成员位置声明Singleton类型的静态变量,并没有进行对象的赋值操作,* 那么什么时候赋值的呢?当调用getInstance()方法获取Singleton类的对象的时候才创建Singleton类的对象,这样就实现了懒加载的效果。但是,如果是多线程环境,会出现线程安全问题。*/
public class Singleton1 {// 构造私有方法private Singleton1(){System.out.println("懒汉式:线程不安全");}// 在成员位置声明静态变量private static Singleton1 instance;// 对外提供静态方法获取改对象public static Singleton1 getInstance(){if (instance == null){instance = new Singleton1();}return  instance;}
}

这种实现在单线程环境中工作得很好,但是当涉及到多线程系统时,如果多个线程if同时处于该条件内,则可能会导致问题。它将破坏单例模式,并且两个线程将获得单例类的不同实例。

1.2.2 懒加载初始化方法 (线程安全)

创建线程安全单例类的一种简单方法是使全局访问方法同步,以便一次只有一个线程可以执行该方法。以下是此方法的一般实现:

/*** @author: 那就叫小智吧* @date: 2022/3/3 16:04* @Description: 懒汉式 : 线程安全* 	该方式也实现了懒加载效果,同时又解决了线程安全问题。但是在getInstance()方法上添加了synchronized关键字,导致该方法的执行效果特别低。从上面代码我们可以看出,其实就是在初始化instance的时候才会出现线程安全问题,一旦初始化完成就不存在了。*/
public class Singleton2 {// 私有构造方法private Singleton2(){};// 在成员位置声明静态变量private static Singleton2 instance;// 对外提供静态方法获取对象public static synchronized Singleton2 getInstance(){if (instance != null) {instance = new Singleton2();}return  instance;}
}
1.2.3 双重检查锁

前面的实现,能够正常运行并且提供了线程安全性,但是由于与同步方法相关的成本,它降低了性能,尽管我们只需要它用于可能创建单独实例的前几个线程。为了避免每次都产生额外的开销,使用了双重检查锁定原则

/*** @author: 那就叫小智吧* @date: 2022/3/3 16:08* @Description: 懒汉式 :双重检查方式* 对于 `getInstance()` 方法来说,绝大部分的操作都是读操作,读操作是线程安全的,所以我们没必让每个线程必须持有锁才能调用该方法,我们需要调整加锁的时机。由此也产生了一种新的实现模式:双重检查锁模式*/
public class Singleton3 {// 私有构造方法private Singleton3() {System.out.println("懒汉式 : 双重检查方式");}// 在成员位置声明静态变量private static Singleton3 instance;// 对外提供静态方法获取该对象public static Singleton3 getInstance() {// 第一次判断,如果instance不为null,不进入枪锁阶段,直接返回实例if (instance == null) {synchronized (Singleton3.class) {// 抢到锁之后再次判断是否为nullif (instance == null) {instance = new Singleton3();}}}return instance;}
}

检查锁模式带来空指针异常的问题

/*** @author: 那就叫小智吧* @date: 2022/3/3 16:25* @Description: 懒汉式: 双重检查方式* 双重检查锁模式是一种非常好的单例实现模式,解决了单例、性能、线程安全问题,上面的双重检测锁模式看上去完美无缺,其实是存在问题,在多线程的情况下,可能会出现空指针问题,出现问题的原因是JVM在实例化对象的时候会进行优化和指令重排序操作。** 要解决双重检查锁模式带来空指针异常的问题,只需要使用 `volatile` 关键字, `volatile` 关键字可以保证可见性和有序性。*/
public class Singleton4 {// 私有构造方法private Singleton4() {}private static volatile Singleton4 instance;// 对外提供静态方法获取该对象public static Singleton4 getInstance() {//第一次判断,如果instance不为null,不进入抢锁阶段,直接返回实际if(instance == null) {synchronized (Singleton4.class) {//抢到锁之后再次判断是否为空if(instance == null) {instance = new Singleton4();}}}return instance;}}

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

1.2.4 静态内部类方式
/*** @author: 那就叫小智吧* @date: 2022/3/3 16:29* @Description: 懒汉式 : 静态内部类方式* 静态内部类单例模式中实例由内部类创建,由于 JVM 在加载外部类的过程中, 是不会加载静态内部类的, 只有内部类的属性/方法被调用时才会被加载,并初始化其静态属性。静态属性由于被 `static` 修饰,保证只被实例化一次,并且严格保证实例化顺序。*/
public class Singleton5 {// 私有构造方法private Singleton5() {}// 创建静态内部类private static class SingletonHolder {private static final Singleton5 instance = new Singleton5();}//对外提供静态方法获取该对象public static Singleton5 getInstance() {return SingletonHolder.instance;}}
/**
第一次加载Singleton类时不会去初始化INSTANCE,只有第一次调用getInstance,虚拟机加载SingletonHolder并初始化INSTANCE,这样不仅能确保线程安全,也能保证 Singleton 类的唯一性。*/

文章参考地址

作者:神的孩子都在歌唱
本人博客:https://blog.csdn.net/weixin_46654114
转载说明:务必注明来源,附带本人博客连接。

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

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

相关文章

http请求报文

关于urlURL 是大型标识符 URI(统一资源标识)的一部分。这个超集是建立在已有的命名惯例基础上的。一个 URL 是一个简单的 URI,使用已存在的协议或规划(也就是 http,ftp 等)作为地址的一部分。为了进一步描绘这些,我们将会Edit By Vheavens Edit By Vheavens 引入 non-URL 的 UR…

operanbsp;内容拦截

结束我大opera的一个好用功能 内容拦截 博客的主要很清爽,也可以自定义.非常好, 但打开博文,左边的特别的精彩图文,瞄到就不高兴.好的 开始 右键-内容拦截 shift 点那个精彩xx,最右上角,广告, 然后点完成.ok了 貌似我大遨游的广告拦截更好用 转载于:https://www.cnblogs.com/wa…

Solr-5.3.1安装配置

Solr-5.3.1安装配置官方网站:http://lucene.apache.org/solr/http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.htmlhttp://lucene.apache.org/solr/quickstart.html环境:CentOS6 x64Solr-5.3.1从Solr5.0开始,Solr不再发布为在任何…

EF for Firebird

今天用了Firebird,记录下怎么用,不然下次给忘记了 1.官网下载包 1.DDEXProvider-3.0.1.0.msi 2.FirebirdSql.Data.FirebirdClient-4.6.2.0.msi 备注: 1.发现DDEXProvider 与 FirebirdSql.Data.FirebirdClient安装有先后顺序 1.如果中间出现了…

php几个函数

1. dirname (PHP 3, PHP 4, PHP 5) dirname -- 返回路径中的目录部分说明 string dirname ( string path )给出一个包含有指向一个文件的全路径的字符串,本函数返回去掉文件名后的目录名。 在 Windows 中,斜线(/)和反斜线&#xf…

Struts2 自定义拦截器(easy example)

要自定义拦截器需要实现com.opensymphony.xwork2.interceptor.Interceptor接口: 新建一个MyInterceptpackage com.action;import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.Interceptor;public class MyInterceptor i…

img标签在div里上下居中

方法一&#xff1a;图片尺寸未知&#xff0c;IE8-不支持 CSS部分&#xff1a; <style> .content{width:500px;height:500px;border:1px solid black;position:relative; } </style>DOM部分&#xff1a; <div class"content"> <img src"htt…

(接口)银联证书上传被修改的问题和读取证书的绝对路径问题

最近在做对接银联的支付接口。相对于支付宝、微信支付&#xff0c;银联支付接口应该是做的最完美的了。银联支付平台提供了足够详细的接口文档&#xff0c;接口demo&#xff0c;常见问题解决方案。 更人性化的是&#xff0c;个人可以免费注册一个商家账号&#xff0c;提供给你免…

控制器对视图声明周期的控制

init&#xff0d;初始化程序 viewDidLoad&#xff0d;加载视图 viewWillAppear&#xff0d;UIViewController对象的视图即将加入窗口时调用&#xff1b; viewDidApper&#xff0d;UIViewController对象的视图已经加入到窗口时调用&#xff1b; viewWillDisappear&#xff0d;UI…

MST:Bad Cowtractors(POJ 2377)

坏的牛圈建筑 题目大意&#xff1a;就是现在农夫又要牛修建牛栏了&#xff0c;但是农夫想不给钱&#xff0c;于是牛就想设计一个最大的花费的牛圈给他&#xff0c;牛圈的修理费用主要是用在连接牛圈上 这一题很简单了&#xff0c;就是找最大生成树&#xff0c;把Kruskal算法改一…

有关进程的一些基本概念

对进程的初步描述 一.和进程有关的一些概念 ①一个进程就是一个正在执行程序的实例&#xff0c;包括程序计数器&#xff0c;寄存器和变量的当前值。 从概念上说&#xff0c;每个进程拥有它自己的虚拟CPU&#xff0c;当然真实的CPU在各个进程之间来回切换。 ②在某一瞬间&…

第4章 maven依赖特性

第4章 maven依赖特性 本章详细介绍maven的依赖特性和依赖范围&#xff0c;如何排除依赖。 4.1 什么是依赖传递 举个例子 在非maven项目中&#xff0c;你需要使用spring功能&#xff0c;你会想到导入spring的jar包&#xff0c;那么&#xff0c;srping又需要其他依赖jar包支持&a…

lintcode :Integer to Roman 整数转罗马数字

题目 整数转罗马数字 给定一个整数&#xff0c;将其转换成罗马数字。 返回的结果要求在1-3999的范围内。 样例 4 -> IV 12 -> XII 21 -> XXI 99 -> XCIX 更多案例&#xff0c;请戳 http://literacy.kent.edu/Minigrants/Cinci/romanchart.htm 说明 什么是 罗马数字…

Win32ASm学习[1]:RadASm下测试Debug

okay 正文开始下面的代码如果不能编译 请把你的RadAsm下的Masm32这个文件夹复制到任意一个磁盘的根目录下&#xff0c;在进行编译就可以了 或者安装Masm32 SDK到任意磁盘根目录下 .386.model flat,stdcalloption casemap:none;>>>>>>>>>>&g…

关于javascript闭包

1.闭包的概念 闭包就是能够读取其他函数内部变量的函数。 由于在Javascript语言中&#xff0c;只有函数内部的子函数才能读取局部变量&#xff0c;因此可以把闭包简单理解成“定义在一个函数内部的函数”。 所以&#xff0c;在本质上&#xff0c;闭包就是将函数内部和函数外部连…

Win32ASM学习[2]:运算符

算术运算符 名称 优先级 () 圆括号 1 ,- 正、负 2 *,/ 乘、除 3 MOD 取模 3 ,- 加、减 4 ------------------------------------------------------------------------------------------------------------------------------------------ .386 .mo…

正式入住了

从13年开始从事iOS开发工作,就准备写一些东西,记录这一路学习工作之旅,但是总是想着坚持不下来,也就慢慢的放弃了,开始用一些记笔记的软件,印象笔记用过,个人体验比较差,后来又用了OneNote,这个APP还是比较舒服,但是由于公司的老版mac-pro,无法使用,也就突然想起了,也是时候重出…

VRRP协议具体解释

转帖&#xff1a;http://blog.chinaunix.net/space.php?uid11654074&doblog&id2857384 Contents Page 文件夹 入木三分学网络…

Win32ASM学习[3]:局部变量

.386 .modelflat, stdcall include windows.inc include kernel32.inc include masm32.inc include debug.inc includelib kernel32.lib includelib masm32.lib includelib debug.lib .code main proc ;局部变量中的类型不能使用缩写 LOCAL v1: dword …