单例模式详解:概念与实用技巧

news2025/1/18 20:23:14

目录

  • 单例模式
    • 单例模式结构
    • 单例模式适用场景
    • 单例模式优缺点
    • 练手题目
      • 题目描述
      • 输入描述
      • 输出描述
      • 输入示例
      • 输出示例
      • 提示信息
      • 题解

单例模式

单例模式是一种创建型设计模式, 让你能够保证一个类只有一个实例, 并提供一个访问该实例的全局节点。

  • 只有一个实例的意思是,在整个应用程序中,只存在该类的一个实例对象,而不是创建多个相同类型的对象。

  • 全局访问点的意思是,为了让其他类能够获取到这个唯一实例,该类提供了一个全局访问点(通常是一个静态方法),通过这个方法就能获得实例。

单例模式结构

  • 私有的构造函数:防止外部代码直接创建类的实例

  • 私有的静态实例变量:保存该类的唯一实例

  • 公有的静态方法:通过公有的静态方法来获取类的实例

在这里插入图片描述

单例模式通用代码

单例模式的实现方式有多种,包括懒汉式、饿汉式等。

  • 饿汉式指的是在类加载时就已经完成了实例的创建,不管后面创建的实例有没有使用,先创建再说,所以叫做 “饿汉”。

  • 而懒汉式指的是只有在请求实例时才会创建,如果在首次请求时还没有创建,就创建一个新的实例,如果已经创建,就返回已有的实例,意思就是需要使用了再创建,所以称为“懒汉”。

饿汉模式

//单例类通过'getSingleton(获取实例)'方法进行定义,让客户端全局获得该对象实例
public class Singleton{

// 保存单例实例成员变量必须被声明为静态类型
private static final Singleton singleton=new Singleton();

// 单例的构造函数必须永远是私有类型,以防止使用`new`运算符直接调用构造方法。限制产生多个对象
private Singleton(){
	}
	
//通过该方法获得实例对象
public static Singleton getSingleton(){
return singleton;
	}
//类中其他方法,尽量是static
public static void doSomething(){
	}
}

懒汉模式

public class Singleton {
    private static Singleton instance;
    
    private Singleton() {
        // 私有构造方法,防止外部实例化
    }
    // 使用了同步关键字来确保线程安全, 可能会影响性能
    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

在懒汉模式的基础上,可以使用双重检查锁来提高性能。

public class Singleton {
    private static volatile Singleton instance;
    
    private Singleton() {
        // 私有构造方法,防止外部实例化
    }
    
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

有上限的多例模式,它是单例模式的一种扩展,采用有上限的多例模式,我们可以在设计时决定在内存中有多少个实例,方便系统进行扩展,修正单例可能存在的性能问题,提供系统的响应速度。

public class Singleton{
	
	private static int maxNum=3;//最多产生3个实例数量
	
	private static ArrayList<Singleton> List=new ArrayList<Singleton>();
	
	private static int count=0;//当前皇帝序列号
	
	static{
	for(int i=0;i<maxNum;i++){
		List.add(new Singleton());
		}
	}
	
	private Singleton(){
	
	}
	
	private static Singleton getInstance(){
	Random random=new Random();
	count=random.nextInt(maxNum);
	return List.get(count);
	}
	

}

单例模式适用场景

  1. 如果程序中的某个类对于所有客户端只有一个可用的实例, 可以使用单例模式。

    单例模式禁止通过除特殊构建方法以外的任何方式来创建自身类的对象。 该方法可以创建一个新对象, 但如果该对象已经被创建, 则返回已有的对象。

  2. 如果你需要更加严格地控制全局变量, 可以使用单例模式。

    单例模式与全局变量不同, 它保证类只存在一个实例。 除了单例类自己以外, 无法通过任何方式替换缓存的实例。

在这里插入图片描述

识别方法: 单例可以通过返回相同缓存对象的静态构建方法来识别。

单例模式优缺点

单例模式优点:

  • 你可以保证一个类只有一个实例。

  • 你获得了一个指向该实例的全局访问节点。

  • 仅在首次请求单例对象时对其进行初始化。

单例模式缺点:

  • 违反了单一职责原则。 该模式同时解决了两个问题。

  • 单例模式一般没有接口,扩展很困难,若要扩展,除了修改代码基本上没有第二种途径可以实现。

  • 单例模式可能掩盖不良设计, 比如程序各组件之间相互了解过多等。

  • 该模式在多线程环境下需要进行特殊处理, 避免多个线程多次创建单例对象。

  • 单例的客户端代码单元测试可能会比较困难, 因为许多测试框架以基于继承的方式创建模拟对象。 由于单例类的构造函数是私有的, 而且绝大部分语言无法重写静态方法, 所以你需要想出仔细考虑模拟单例的方法。 要么干脆不编写测试代码, 或者不使用单例模式。

练手题目

题目描述

小明去了一家大型商场,拿到了一个购物车,并开始购物。请你设计一个购物车管理器,记录商品添加到购物车的信息(商品名称和购买数量),并在购买结束后打印出商品清单。(在整个购物过程中,小明只有一个购物车实例存在)。

输入描述

输入包含若干行,每行包含两部分信息,分别是商品名称和购买数量。商品名称和购买数量之间用空格隔开。

输出描述

输出包含小明购物车中的所有商品及其购买数量。每行输出一种商品的信息,格式为 “商品名称 购买数量”。

输入示例

Apple 3 Banana 2 Orange 5

输出示例

Apple 3 Banana 2 Orange 5

提示信息

本道题目请使用单例设计模式, 使用私有静态变量来保存购物车实例。 使用私有构造函数防止外部直接实例化。

题解

1.简单单例模式实现。

import java.util.Scanner;
import java.util.ArrayList;

class ShoppingCart {
    
    private static ShoppingCart instance = new ShoppingCart();
    private static ArrayList<String> productNames = new ArrayList<>();
    private static ArrayList<Integer> productQuantities = new ArrayList<>();
    
    private ShoppingCart() {
        
    }
    
    public static ShoppingCart getInstance() {
		    
        return instance;
    }
    
    public void Add(String name, int quantity) {
        productNames.add(name);
        productQuantities.add(quantity);
        System.out.println(name + " " + quantity);
    }
}

public class Main {
    public static void main(String[] args) {
        ShoppingCart cart = ShoppingCart.getInstance();
        Scanner scanner = new Scanner(System.in);
        
        String inputLine;
        while (scanner.hasNextLine()) {
            inputLine = scanner.nextLine();
            
            if ("exit".equalsIgnoreCase(inputLine)) {
                break;
            }

            String[] parts = inputLine.split(" ");
            
            if (parts.length == 2) {
                String name = parts[0];
                int quantity;
                
                try {
                    quantity = Integer.parseInt(parts[1]);
                    cart.Add(name, quantity);
                } catch (NumberFormatException e) {
                    System.out.println("输入错误,请重新输入");
                }
            } else {
                System.out.println("输入错误,请重新输入");
            }
        }
        
        scanner.close();
    }
}

2.在懒汉模式的基础上,可以使用双重检查锁来提高性能。

import java.util.Scanner;  
import java.util.ArrayList;  

class ShoppingCart {
    // 购物车类的单例实例变量,使用volatile关键字确保线程安全
    private static volatile ShoppingCart instance;
    // 存储商品名称
    private static ArrayList<String> productNames = new ArrayList<>();
    // 存储商品数量
    private static ArrayList<Integer> productQuantities = new ArrayList<>();
    
    // 私有构造函数,防止外部直接创建ShoppingCart对象
    private ShoppingCart() {
        
    }
    
    // 获取购物车单例实例的方法,确保线程安全
    public static ShoppingCart getInstance() {
        if (instance == null) {
            synchronized (ShoppingCart.class) {
                if (instance == null) {
                    instance = new ShoppingCart();
                }
            }
        }
        return instance;
    }
    
    // 添加商品到购物车的方法
    public void Add(String name, int quantity) {
        productNames.add(name); 
        productQuantities.add(quantity); 
        System.out.println(name + " " + quantity);
    }
}

public class Main {
    public static void main(String[] args) {
        ShoppingCart cart = ShoppingCart.getInstance(); 
        Scanner scanner = new Scanner(System.in);  
        
        String inputLine;
        // 循环读取用户输入,直到用户输入"exit"
        while (scanner.hasNextLine()) {
            inputLine = scanner.nextLine();
            
            if ("exit".equalsIgnoreCase(inputLine)) {
                break;
            }
            
            // 使用空格分割输入的字符串,获取商品名称和数量
            String[] parts = inputLine.split(" ");
            
            // 确保输入格式正确,即包含两个部分:商品名称和数量
            if (parts.length == 2) {
                // 商品名称
                String name = parts[0];  
                // 商品数量
                int quantity; 
                
                try {
                    // 将第二部分转换为整数
                    quantity = Integer.parseInt(parts[1]);
                    cart.Add(name, quantity);  
                } catch (NumberFormatException e) {
                    // 如果转换失败,输出错误信息
                    System.out.println("转换失败,请重新输入");
                }
            } else {
                // 如果输入格式不正确,输出错误信息
                System.out.println("如果输入格式不正确,请重新输入");
            }
        }
        
        scanner.close();
    }
}

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

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

相关文章

LLM - 词表示和语言模型

一. 词的相似度表示 (1): 用一系列与该词相关的词来表示 (2): 把每个词表示一个独立的符号(one hot) (3): 利用该词上下文的词来表示该词 (3): 建立一个低维度的向量空间&#xff0c;用深度学习方法将该词映射到这个空间里(Word Embedding) 二&#xff1a;语言模型 (1): 根…

jsqlparse工具拦截sql处理和拓展

前置知识 访问者模式 &#xff08;Visitor Pattern&#xff09;是一种行为设计模式&#xff0c;它允许你定义在不改变被访问元素的类的前提下&#xff0c;扩展其功能。通过将操作&#xff08;操作或算法&#xff09;从对象结构中提取出来&#xff0c;可以在不修改这些对象的前…

MCU中如何利用串口通信,增加AT指令框架

第一步&#xff0c;通过串口与PC端建立通信第二步&#xff0c;根据PC端发来的AT指令&#xff0c;MCU执行相应代码 主要是解析PC端发来的字符串&#xff0c;也就是获取字符串、处理字符串、以及分析字符串。 1. 串口通信 用到的是DMA串口通信&#xff0c;收发字符串数据时&…

AGI系列(7)Reflection 在 AI agent 中的应用实例

斯坦福大学教授吴恩达一直非常推崇AI Agent,之前他提出过AI Agent的四种工作模式,分别是Reflection(反思)、Tool use(工具使用)、Planning(规划)和Multi-agent collaboration(多智能体协同)。 近日,他又开源了一个翻译 AI Agent, 他认为 AI 智能体机器翻译对改进传…

spring6框架解析(by尚硅谷)

文章目录 spring61. 一些基本的概念、优势2. 入门案例实现maven聚合工程创建步骤分析实现过程 3. IoC&#xff08;Inversion of Control&#xff09;基于xml的bean环境搭建获取bean获取接口创建实现类依赖注入 setter注入 和 构造器注入原生方式的setter注入原生方式的构造器注…

electron-vue自定义标题

1.在主进程background.js或者main.js中主窗口配置frame: false async function createWindow() {Menu.setApplicationMenu(null);// Create the browser window.const win new BrowserWindow({width: 1000,height: 600,resizable: false,frame: false,webPreferences: {nodeI…

Python基础语法(与C++对比)(持续更新ing)

代码块 Python在统一缩进体系内&#xff0c;为同一代码块C{...}内部的为同一代码块 注释 Python 单行注释&#xff1a;#... 多行注释&#xff1a;... C 单行注释&#xff1a;//... 多行注释: /*...*/ 数据类型 1. Python数据类型 Python中支持数字之间使用下划线 _ 分割…

docker容器技术、k8s的原理和常见命令、用k8s部署应用步骤

容器技术 容器借鉴了集装箱的概念&#xff0c;集装箱解决了什么问题呢&#xff1f;无论形状各异的货物&#xff0c;都可以装入集装箱&#xff0c;集装箱与集装箱之间不会互相影响。由于集装箱是标准化的&#xff0c;就可以把集装箱整齐摆放起来&#xff0c;装在一艘大船把他们…

昇思学习打卡-5-基于Mindspore实现BERT对话情绪识别

本章节学习一个基本实践–基于Mindspore实现BERT对话情绪识别 自然语言处理任务的应用很广泛&#xff0c;如预训练语言模型例如问答、自然语言推理、命名实体识别与文本分类、搜索引擎优化、机器翻译、语音识别与合成、情感分析、聊天机器人与虚拟助手、文本摘要与生成、信息抽…

通过 Power Automate 以提升的权限运行 Power Apps 连接

使用Power Apps在Sharepoint列表中新建或编辑项比较简单&#xff0c;就是创建窗体&#xff0c;连接Sharepoint列表&#xff0c;添加个按钮&#xff0c;触发条件为Submit(form)。 填写信息&#xff0c;点击按钮即可新建项 但使用过程中&#xff0c;发现运行此应用的用户&#xf…

朗新天霁eHR GetFunc_code.asmx SQL注入致RCE漏洞复现

0x01 产品简介 朗新天霁人力资源管理系统(LongShine eHR)是一款由北京朗新天霁软件技术有限公司研发的人力资源管理系统,该产品融合了国外先进的人力资源管理理念和国内大量人力资源管理实践经验,是国内功能较为全面、性价比较高的人力资源管理系统之一,系统凭借其集成化…

如何通过IP地址查询地理位置及运营商信息

在数字时代&#xff0c;IP地址&#xff08;Internet Protocol Address&#xff0c;互联网协议地址&#xff09;已经成为我们日常网络活动的重要组成部分。每台连接到互联网的设备都被分配了一个唯一的IP地址&#xff0c;它不仅可以识别设备&#xff0c;还可以揭示设备的地理位置…

以太网协议介绍——UDP

注&#xff1a;需要先了解一些以太网的背景知识&#xff0c;方便更好理解UDP协议、 以太网基础知识一 以太网基础知识二 UDP协议 UDP即用户数据报协议&#xff0c;是一种面向无连接的传输层协议&#xff0c;属于 TCP/IP 协议簇的一种。UDP具有消耗资源少、通信效率高等优点&a…

MySQL 9.0 GA 来了!

2024 年 7 月 2 日&#xff0c;MySQL 9.0 GA 版本正式发布。还记得 MySQL 8.0 版本正式发布于 2018 年 4 月 19 日&#xff0c;中间经过了 6 年之久&#xff0c;MySQL 官方终于发布了大版本号变更得 9.0 版本&#xff0c;接下来由我给大家介绍 MySQL 在 9.0 版本中有哪些新的变…

经典低功耗四通道运算放大器LM324

前言&#xff1a; SOP14封装LM324 这个LM324运放有几十年的历史了吧&#xff1f;很普通&#xff0c;很常用&#xff0c;搞电路的避免不了接触运放&#xff0c;怎么选择运放&#xff0c;是工程师关心的问题吧&#xff1f; 从本文开始&#xff0c;将陆续发一些常用的运放&#xf…

2024 AI工程师世界博览会

6月24日至6月27日在旧金山举行的 AI 工程师世界博览会是AI 从业者和爱好者的首要活动之一。本次年度会议展示了人工智能技术的最新进展&#xff0c;并提供了对行业趋势的宝贵见解。 模型不是壁垒 大型语言模型&#xff08;LLMs&#xff09;的快速发展是会议的中心主题。OpenAI…

单片机软件架构连载(4)-结构体

枚举、指针、结构体&#xff0c;我愿称为C语言"三板斧"。 用人话来讲&#xff0c;几乎所有c语言高阶编程&#xff0c;都离不开这这3个知识点的应用。 今天站在实际产品常用的角度&#xff0c;给大家讲一下结构体。 1.结构体概念 结构体可以用来构建更复杂的数据结…

【病毒分析】假冒游戏陷阱:揭秘MBRlock勒索病毒及其修复方法

1.背景 在公众号文章中看到一篇名为《敲竹杠木马分析&#xff1a;虚假的植物大战僵尸杂交版》的文章&#xff0c;样本来源于某吧&#xff0c;对此我们对样本进行了提取分析。 文章链接&#xff1a;https://mp.weixin.qq.com/s/Up9u4DZtHnVNMiGBIHZzHw 2.恶意文件基础信息 文…

鸿蒙开发设备管理:【@ohos.update (升级)】

升级 说明&#xff1a; 本模块首批接口从API version 6开始支持。后续版本的新增接口&#xff0c;采用上角标单独标记接口的起始版本。开发前请熟悉鸿蒙开发指导文档&#xff1a;gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md点击或者复制转到。 升级范围&…

Build a Large Language Model (From Scratch)附录B(gpt-4o翻译版)

来源&#xff1a;https://github.com/rasbt/LLMs-from-scratch?tabreadme-ov-file https://www.manning.com/books/build-a-large-language-model-from-scratch