Java设计模式-单例模式

2023-11-18

单例模式

  在有些系统中,为了节省内存资源、保证数据内容的一致性,对某些类要求只能创建一个实例,这就是所谓的单例模式。

单例模式的定义与特点

  单例(Singleton)模式的定义:指一个类只有一个实例,且该类能自行创建这个实例的一种模式。例如,Windows 中只能打开一个任务管理器,这样可以避免因打开多个任务管理器窗口而造成内存资源的浪费,或出现各个窗口显示内容的不一致等错误。

  在计算机系统中,还有 Windows 的回收站、操作系统中的文件系统、多线程中的线程池、显卡的驱动程序对象、打印机的后台处理服务、应用程序的日志对象、数据库的连接池、网站的计数器、Web 应用的配置对象、应用程序中的对话框、系统中的缓存等常常被设计成单例。

  单例模式有 3 个特点:

  1. 单例类只有一个实例对象;
  2. 该单例对象必须由单例类自行创建;
  3. 单例类对外提供一个访问该单例的全局访问点;

单例模式的结构与实现

  单例模式是设计模式中最简单的模式之一。通常,普通类的构造函数是公有的,外部类可以通过“new 构造函数()”来生成多个实例。但是,如果将类的构造函数设为私有的,外部类就无法调用该构造函数,也就无法生成多个实例。这时该类自身必须定义一个静态私有实例,并向外提供一个静态的公有函数用于创建或获取该静态私有实例。

下面来分析其基本结构和实现方法。
1. 单例模式的结构

  单例模式的主要角色如下。
  单例类:包含一个实例且能自行创建这个实例的类。
  访问类:使用单例的类。
  其结构如图所示。
单例模式的结构图

2. 单例模式的结构

  Singleton 模式通常有两种实现形式。
  第 1 种:懒汉式单例
  该模式的特点是类加载时没有生成单例,只有当第一次调用 getlnstance 方法时才去创建这个单例。代码如下:

package designMode;
public class LazySingleton {
    private static volatile LazySingleton instance=null;    //保证 instance 在所有线程中同步
    private LazySingleton(){}    //private 避免类在外部被实例化
    public static synchronized LazySingleton getInstance(){
        //getInstance 方法前加同步
        if(instance==null)
        {
            instance=new LazySingleton();
        }
        return instance;
    }
}

  注意:如果编写的是多线程程序,则不要删除上例代码中的关键字 volatile 和 synchronized,否则将存在线程非安全的问题。如果不删除这两个关键字就能保证线程安全,但是每次访问时都要同步,会影响性能,且消耗更多的资源,这是懒汉式单例的缺点。
  第 2 种:饿汉式单例
  该模式的特点是类一旦加载就创建一个单例,保证在调用 getInstance 方法之前单例已经存在了。

package designMode;
public class HungrySingleton {
    private static final HungrySingleton instance=new HungrySingleton();
    private HungrySingleton(){}
    public static HungrySingleton getInstance(){
        return instance;
    }
}

  饿汉式单例在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以是线程安全的,可以直接用于多线程而不会出现问题。

  饱汉模式(懒汉模式)–双重加锁检查DCL(Double Check Lock)

public class Singleton {
    /**
     * 对保存实例的变量添加volatile的修饰
     */
    private volatile static Singleton instance = null;
    private Singleton(){
    }
    public static Singleton getInstance(){
//先检查实例是否存在,如果不存在才进入下面的同步块
        if(instance == null){
//同步块,线程安全的创建实例
            synchronized(Singleton.class){
//再次检查实例是否存在,如果不存在才真的创建实例
                if(instance == null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

  值得注意的是:上述单例实现形式由于构造函数是私有的,因此该类不能被继承。

  单例类的特殊实现 - 单例注册表

package designMode;

import java.util.HashMap;
public class RegSingleton{
    private static  HashMap registry=new HashMap();
    //静态块,在类被加载时自动执行
    static {
        RegSingleton rs=new RegSingleton();
        registry.put(rs.getClass().getName(),rs);
    }
    //受保护的默认构造函数,如果为继承关系,则可以调用,克服了单例类不能为继承的缺点
    protected RegSingleton(){}
    //静态工厂方法,返回此类的唯一实例
    public static RegSingleton getInstance(String name){
        if(name==null){
            name="RegSingleton";
        }
        if(registry.get(name)==null){
            try{
                registry.put(name,Class.forName(name).newInstance());
            }catch(Exception ex){ex.printStackTrace();}
        }
        return (RegSingleton)registry.get(name);
    }
}
Spring对单例的底层实现

  Spring框架对单例的支持是采用单例注册表的方式进行实现的,源码如下:


public abstract class AbstractBeanFactory implements ConfigurableBeanFactory{  
       /** 
        * 充当了Bean实例的缓存,实现方式和单例注册表相同 
        */  
       private final Map singletonCache=new HashMap();  
       public Object getBean(String name)throws BeansException{  
           return getBean(name,null,null);  
       }  
    ...  
       public Object getBean(String name,Class requiredType,Object[] args)throws BeansException{  
          //对传入的Bean name稍做处理,防止传入的Bean name名有非法字符(或则做转码)  
          String beanName=transformedBeanName(name);  
          Object bean=null;  
          //手工检测单例注册表  
          Object sharedInstance=null;  
          //使用了代码锁定同步块,原理和同步方法相似,但是这种写法效率更高  
          synchronized(this.singletonCache){  
             sharedInstance=this.singletonCache.get(beanName);  
           }  
          if(sharedInstance!=null){  
             ...  
             //返回合适的缓存Bean实例  
             bean=getObjectForSharedInstance(name,sharedInstance);  
          }else{  
            ...  
            //取得Bean的定义  
            RootBeanDefinition mergedBeanDefinition=getMergedBeanDefinition(beanName,false);  
             ...  
            //根据Bean定义判断,此判断依据通常来自于组件配置文件的单例属性开关  
            //<bean id="date" class="java.util.Date" scope="singleton"/>  
            //如果是单例,做如下处理  
            if(mergedBeanDefinition.isSingleton()){  
               synchronized(this.singletonCache){  
                //再次检测单例注册表  
                 sharedInstance=this.singletonCache.get(beanName);  
                 if(sharedInstance==null){  
                    ...  
                   try {  
                      //真正创建Bean实例  
                      sharedInstance=createBean(beanName,mergedBeanDefinition,args);  
                      //向单例注册表注册Bean实例  
                       addSingleton(beanName,sharedInstance);  
                   }catch (Exception ex) {  
                      ...  
                   }finally{  
                      ...  
                  }  
                 }  
               }  
              bean=getObjectForSharedInstance(name,sharedInstance);  
            }  
           //如果是非单例,即prototpye,每次都要新创建一个Bean实例  
           //<bean id="date" class="java.util.Date" scope="prototype"/>  
           else{  
              bean=createBean(beanName,mergedBeanDefinition,args);  
           }  
    }  
    ...  
       return bean;  
    }  
    }

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java设计模式-单例模式 的相关文章

  • Logback 上下文选择器的实际使用

    Logback 的文档测井分离 http logback qos ch manual loggingSeparation html表明我可以使用上下文选择器 http logback qos ch manual contextSelecto
  • OpenJDK 11 问题 - 客户端在上次 UNWRAP 之前完成握手

    我正在将代码库从 Oracle Java 1 8 0 131 迁移到 OpenJDK 11 0 1 我们有实现 nio ssl 套接字通道的代码 在 Java 8 中 客户端 服务器握手工作正常 在 Java 11 中 客户端在解包来自服务
  • 通过 WebStart 运行时 Java 7 更新 55 JacORB 错误

    自从更新到 Java 7 update 55 以来 我无法运行我的 WebStart java 应用程序 当通过 WebStart 启动时 该应用程序在 Java 7 update 51 下运行良好 当在 WebStart 之外启动时 它还
  • Eclipse 调试“未找到源”

    我刚刚开始使用 Eclipse 所以慢慢来吧 但是 当尝试调试 JUnit 测试用例时 我会收到一个对话框 指出当我在测试方法中的代码中找到此行时 未找到源代码 Assert assertEquals 1 contents size 我知道
  • 无法解析 Java 中的方法

    我有一个Question具有 4 个的对象Answer里面的物体 在 Question java 我有一个方法是 public Answer getA return a 在另一种方法中我有 if questions get randomNu
  • Java中单击和双击的区别

    我搜索论坛并看到以下代码 public void mouseClicked MouseEvent e if e getClickCount 2 System out println and it s a double click wasDo
  • Tomcat:Java 静态变量作用域、应用程序范围还是会话范围?

    java 静态变量是否在使用相同 web 应用程序的所有会话之间共享 或者每个会话都有自己的静态变量版本吗 换句话说 Tomcat 是为每个会话创建一组新的类 还是为整个 Web 应用程序创建一组新的类 Tomcat 创建一个ClassLo
  • 多个罐子、单个持久单元解决方案?

    包括我在内的一些人一直在努力将不同模块 jar 中的实体合并到单个持久性单元中 尤其是JavaSE 例如这里JPA 2 0 自动从不同的 jar 添加实体类到 PersistenceUnit https stackoverflow com
  • Android 上 WebRTC 的自定义视频源

    Overview 我想使用自定义视频源通过 WebRTC Android 实现来直播视频 如果我理解正确的话 现有的实现仅支持 Android 手机上的前置和后置摄像头 以下类与此场景相关 Camera1Enumerator java ht
  • 如何在android中播放内部和外部SD卡中的mp3文件?

    我正在开发一个 mp3 播放器应用程序 它可以播放内部 SD 卡内任何位置的 mp3 文件 我使用以下代码来获取内部存储中存在的 mp3 文件 ArrayList
  • 使用 Jquery Ajax 将数据从 jsp 发送到 struts2 操作类

    我需要使用 jquery Ajax 将表单数据从 jsp 传递到 struts2 并从 Struts2 操作类接收回 JSON 数据 我已经给出了下面的代码 当我传递 AJAX 数据时 url search action searchTex
  • 动态更新 LookAndFeel 值

    我希望能够动态更新 Swing GUI 的 LookAndFeel 属性 在本例中 我有一个简单的 Swing Awt 游戏 运行最初为 Nimbus 的游戏LookAndFeel 在启动后的各个时刻 我只想更改 比方说 一个细节 应用程序
  • AWS Lambda 和 S3 - 上传的 pdf 文件为空/损坏

    我有一个 Spring 应用程序 在 AWS Lambda 上运行 它获取文件并将其上传到 AWS S3 Spring控制器发送一个MultipartFile到我的方法 使用 Amazon API Gateway 将其上传到 AWS S3
  • 优化两个三位数乘积的最大回文数?

    我正在研究一个面试问题 我被问到这个问题 我应该编写一个程序 从两个三位数的乘积中找到最大的回文数 这里是question https projecteuler net problem 4 我想出了这种从底部开始的蛮力方法 public c
  • Hibernate - 如何通过 Hibernate 将 java.net.URL 存储到数据库中

    我有一块田地URL countryURL in a Country班级 我想将其数据存储到COUNTRY通过 Hibernate 将表存储在数据库中 哪个休眠type我应该在休眠映射文件中使用
  • Java基于参数的同步(名为互斥锁/锁)

    我正在寻找一种根据接收到的参数来同步方法的方法 如下所示 public synchronized void doSomething name some code 我想要方法doSomething同步基于name参数如下 线程 1 doSom
  • JPA中如何连接多个数据库?

    我有一个 Spring Boot 应用程序 当前使用 JPA 连接到单个数据库 application properties 文件中的连接详细信息 spring datasource url jdbc oracle thin localho
  • 运行外部进程的非阻塞线程

    我创建了一个 Java GUI 应用程序 它充当许多低级外部进程的包装器 该实用程序按原样运行 但迫切需要一项重大改进 我希望我的外部进程以非阻塞方式运行 这将允许我并行服务其他请求 简而言之 我希望能够在生成数据时处理来自外部进程的数据
  • 与 System.in.read() 一起使用的文件结尾/流键盘组合是什么

    如果这个小问题已经得到解答 我深表歉意 我无法在SO找到它 使用以下 Java 简单代码从 IDE 控制台读取行 Windows 7 和 Eclipse Kepler int v try while v System in read 1 S
  • 切换按钮形状不变

    我正在尝试制作一个带有绿色背景的圆形切换按钮 我用了

随机推荐

  • Android5——活动的生命周期方法

    1 活动的生命周期方法 onCreate 会在活动第一次被创建的时候使用 onStart 会在活动由不可见变为可见的时候调用 OnResume 会在活动准备好和用户进行交互的时候调用 此时的活动一定位于返回栈的栈顶 并且处于运行状态 onP
  • 简单两步解决Python的NLTK库DATA安装问题

    前言 本人使用Anaconda集成开发环境 已经安装有NLTK库 在使用NLTK最简单的分词功能时报错 查询错误 得知是没有安装NLTK的数据包 系统推荐使用以下命令 import nltk nltk download 用了之后弹出窗口 出
  • ROS 第四天 ROS中的关键组件

    1 Launch文件 通过XML文件实现多节点的配置和启动 可自动启动ROS Master
  • word文档中如何添加目录

    如果word文档的内容较多 结构复杂 那么在文档首页设置一个目录链接是非常有必要的 本文将介绍如何添加此目录 1 设置标题 在文档中选择将在目录中显示的内容 将之设置为 标题1 标题2 或 标题3 等 其中 标题1 代表一级标题 应为顶级目
  • 如何在 Java 中调用 MATLAB 代码

    文章目录 测评 完整源代码 运行环境 MATLAB R2022a Java 8 1 8 0 311 IntelliJ IDEA 2022 2 1 Ultimate Edition Maven 3 8 3 Windows 10 教育版 64位
  • ios 删除无用证书

    1 前往文件夹 Library MobileDevice Provisioning Profiles 然后可以删除里面的所有文件 然后就可以根据自己的需求重新下载了
  • STM32之_keil 编译内存大小解析

    Program Size Code 28784 RO data 6480 RW data 60 ZI data 3900 的含义 1 Code 程序所占用的FLASH大小 存储在FLASH 2 RO data Read only data
  • 简单的c++ UDP类 + 多线程 win32编程

    UdpClient h include Thread h class IUdpRecvCallback public virtual void OnRecv const char buf USHORT len const char from
  • 基恩士PLC KV8000+XH16EC总线控制,全ST程序实例

    基恩士PLC KV8000 XH16EC总线控制 全ST程序实例 本人自己开发全程序无加密 公司级框架 功能齐全 提供项目源码框架FB源码 触摸屏源码 需要一定ST基础才能看懂 重在分享编程思想 没用过该控制器的请慎拍 请使用11 10版本
  • 图像的二值化分割,otsu类间方差法

    二值化图像指图像中的每个像素只取两个离散的值之一 用数学公式表示为 公式中 f x y 表示一幅数字图像 X Y表示该图像中某像素的坐标值 T为 二值化的阈值 表示经过阈值运算后的二值化图像 这里0和1仅仅是一个抽象表示 并非实际像素值 它
  • python 实现百度关键字自动爬虫

    coding utf 8 In 3 import requests from lxml import etree import re from sqlalchemy import create engine engine create en
  • IDEA中快捷键大全

    Alt 回车 导入包 自动修正 Ctrl N 查找类 Ctrl Shift N 查找文件 Ctrl Alt L 格式化代码 Ctrl Alt O 优化导入的类和包 Alt Insert 生成代码 如get set方法 构造函数等 Ctrl
  • 【Linux】Linux下的自动化构建工具——make/makefile

    需要云服务器等云产品来学习Linux的同学可以移步 gt 腾讯云 lt gt 阿里云 lt gt 华为云 lt 官网 轻量型云服务器低至112元 年 新用户首次下单享超低折扣 目录 一 make和makefile的区别 二 makefile
  • 3. 形状和转换

    文章目录 形状和转换 改变形状 np expand dims np squeeze np reshape arr reshape 反序 转置 arr T np transpose 形状和转换 array 大多数情况下都是以多维的形式出现的
  • combotree 只能选中叶子节点

    combotree 只能选中叶子节点 一 Aphorism 人之所恶在好为人师 慎言之 二 summary 应该分为两种情况 1 第一种情况 单选 multiple false tt combotree nultiple false onB
  • edge浏览器打开出现ref A ref B refC

    打开edge浏览器 设置 或地址栏输入 edge settings privacy 进入 隐私 搜索和服务 选择 cleanbrowsing 重启浏览器 活清理浏览器缓存 第5步里 我是将所有历史都清除了 如果历史或cookie有重要的东西
  • IBM AppScan使用随想

    公司的一个客户让我对他的一个网站进行安全性测试 该网站刚刚完成一期开发 从他给我的别的网站的测试报告中 我发现了IBM AppScan 最后安装了8 0版并成功破解 What s IBM AppScan It s a famous test
  • 第二十章 Chisel基础——生成Verilog与基本测试

    经过前三章的内容 读者已经了解了如何使用Chisel构建一个基本的模块 本章的内容就是在此基础上 把一个Chisel模块编译成Verilog代码 并进一步使用Verilator做一些简单的测试 一 生成Verilog 前面介绍Scala的内
  • 云服务器 宝塔部署SpringBoot前后端分离项目

    博主介绍 小黄鸭技术 擅长领域 Java 实用工具 运维 系列专栏 开发工具 Java之路 八股文之路 如果文章写作时有错误的地方 请各位大佬指正 一起进步 欢迎大家点赞 收藏 评论 支持博主 目录 前言 环境 部署 查看面板地址和用户名以
  • Java设计模式-单例模式

    单例模式 在有些系统中 为了节省内存资源 保证数据内容的一致性 对某些类要求只能创建一个实例 这就是所谓的单例模式 单例模式的定义与特点 单例 Singleton 模式的定义 指一个类只有一个实例 且该类能自行创建这个实例的一种模式 例如