金三银四面试题(二十):单例模式知多少?

设计模式也是面试中的热门考题,基本这个部分都是问问你知不知道XXX设计模式,有什么用,优缺点,然后再现场手写一个demo。很多时候是和spring一起考的,问问你知不知道spring框架用了哪些设计模式。今天我们来先看看单例模式。

什么是单例模式

单例模式是一种设计模式,用于确保类在应用程序中只有一个实例,并提供一个全局访问点来访问该实例。单例模式通常用于那些需要全局状态或共享资源的情况,以确保整个应用程序中只有一个实例存在,从而避免不必要的资源消耗和冲突。例子,一个应用的日志记录器(Logger)。全局一个日志器记录即可,不需要多个。

单例模式的特点包括:

  1. 私有构造函数:单例类的构造函数被设为私有,以防止外部直接创建对象实例。

  2. 静态方法或静态变量:提供一个静态方法或静态变量来访问该类的唯一实例。

  3. 延迟实例化:有时单例对象不会在应用程序启动时立即创建,而是在第一次被请求时才进行实例化。

  4. 线程安全性:在多线程环境中,需要考虑单例对象的线程安全性,确保在并发情况下也能正确地返回唯一实例。

使用单例模式的优点包括:

  • 节省资源:由于只有一个实例存在,可以避免创建多个对象所带来的资源浪费。
  • 提供全局访问点:可以通过单例对象的全局访问点方便地获取到该实例,使得全局状态或共享资源的管理更加简单。
  • 确保一致性:由于只有一个实例存在,可以确保整个应用程序中对该实例的状态保持一致。

然而,使用单例模式也可能带来一些缺点,如增加了代码的耦合性、对单例对象的依赖性过强等。因此,在使用单例模式时需要权衡利弊,并根据实际情况慎重考虑。

手写单例

可能这会需要你手写一个单例模式,单例模式有很多种写法,懒汉模式,饿汉模式,双重检查模式等。

懒汉模式

懒汉模式的懒就在于就是用的时候再去创建对象,否则什么都不做

public class LazySingleton {// 私有静态变量,用于保存唯一的实例private static LazySingleton instance;// 私有构造函数,防止外部直接创建对象实例private LazySingleton() {// 初始化操作}// 公共静态方法,用于获取唯一的实例public static LazySingleton getInstance() {// 延迟实例化,只有在第一次调用时才创建实例if (instance == null) {instance = new LazySingleton();}return instance;}// 其他方法public void doSomething() {// 执行其他操作}
}

懒汉式单例模式的写法由于new和赋值操作的非原子性所以该写法非线程安全.

饿汉模式

饿汉模式就是提前就已经加载好的静态static 对象

public class EagerSingleton {// 私有静态变量,用于保存唯一的实例,并在类加载时就进行初始化private static final EagerSingleton instance = new EagerSingleton();// 私有构造函数,防止外部直接创建对象实例private EagerSingleton() {// 初始化操作}// 公共静态方法,用于获取唯一的实例public static EagerSingleton getInstance() {return instance;}// 其他方法public void doSomething() {// 执行其他操作}
}

饿汉式单例模式的写法:线程安全,但饿汉模式的主要缺点是如果该单例对象在应用程序中没有被使用到,那么可能会造成资源的浪费。因为在类加载时就创建了实例,即使在后续没有被使用到,该实例也会一直存在于内存中。

双重检查模式

双重检查模式就是两次检查避免多线程造成创建了多个对象。也是一种在懒汉模式的基础上改进的线程安全的单例模式。它通过双重检查锁定来确保在多线程环境下只创建一个实例。

public class DoubleCheckedSingleton {// 使用 volatile 关键字确保 instance 变量的可见性private static volatile DoubleCheckedSingleton instance;// 私有构造函数,防止外部直接创建对象实例private DoubleCheckedSingleton() {// 初始化操作}// 公共静态方法,用于获取唯一的实例public static DoubleCheckedSingleton getInstance() {// 双重检查锁定,确保在多线程环境下只有一个实例被创建if (instance == null) {synchronized (DoubleCheckedSingleton.class) {if (instance == null) {instance = new DoubleCheckedSingleton();}}}return instance;}// 其他方法public void doSomething() {// 执行其他操作}
}

这里面试官可能问你,可不可以去掉这个volatile关键字,答案是不可以,volatile 关键字的作用是确保变量的可见性和禁止指令重排序,否则可能会出现线程安全问题。
所以,双检锁单例模式的写法:线程安全。

这就结束了吗?

等等,加了volatile的双重检查看似没问题,难道这就一定可靠吗?使用 Java 的反射机制可以破坏传统的单例模式实现。通过反射,可以访问类的私有构造函数,并强制创建多个对象实例,从而违反了单例模式的原则。

import java.lang.reflect.Constructor;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 static void main(String[] args) {Singleton singleton1 = Singleton.getInstance();Singleton singleton2 = null;try {// 使用反射获取私有构造函数Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();// 设置可访问私有构造函数constructor.setAccessible(true);// 强制创建多个实例singleton2 = constructor.newInstance();} catch (Exception e) {e.printStackTrace();}System.out.println("singleton1: " + singleton1.hashCode());System.out.println("singleton2: " + singleton2.hashCode());System.out.println("Are they the same instance? " + (singleton1 == singleton2));}
}

那要怎么办? 《Effective Java》中曾经提到过,枚举单例是一种线程安全且简洁的单例模式实现方式,它基于枚举类型的特性,在Java中保证了单例实例的唯一性。枚举类型的每个枚举常量都是单例对象,且在枚举类型被加载时就被初始化。

public enum EnumSingleton {INSTANCE; // 唯一的枚举常量// 可以添加其他成员变量和方法private int data;public int getData() {return data;}public void setData(int data) {this.data = data;}// 可以在枚举类中添加构造函数,但必须是私有的private EnumSingleton() {this.data = 0;}
}

在上面的示例中,EnumSingleton 是一个枚举类型,其中只有一个枚举常量 INSTANCE。由于枚举类型的特性,在类加载时,INSTANCE 常量就会被初始化为单例对象,因此无需担心多线程下的并发问题。

通过调用 EnumSingleton.INSTANCE 就可以获取到该单例对象,例如:

EnumSingleton singleton = EnumSingleton.INSTANCE;

这样就可以确保在整个应用程序中只存在一个 EnumSingleton 实例。

枚举单例的优点包括:

  • 线程安全:枚举类的实例在类加载时就被创建,保证了线程安全性。
  • 简洁:使用枚举类型实现单例模式非常简洁,不需要手动编写单例模式的代码。

因此,如果在Java中实现单例模式,推荐使用枚举类型来实现。

总结

以上就是单例模式的全部内容了,希望能帮助到大家。

在这里插入图片描述

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

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

相关文章

代码+视频,R语言对数据进行多重插补后回归分析

我们在临床做回顾性研究分析中经常要面对数据缺失的问题&#xff0c;如果数据缺失量大就会对我们的研究结果产生影响&#xff0c;近年来&#xff0c;对数据进行多重插补广泛应用于SCI论文中。我们在之前的文章中已经演示了使用SPSS对数据进行多重插补并分析。今天&#xff0c;我…

贪吃蛇的简单实现(c语言)

前言&#xff1a;学完了C语言的基础语法&#xff0c;和一点数据结构的知识&#xff0c;拿贪吃蛇来练练手&#xff0c;并熟悉以前的知识。写完之后&#xff0c;有一种成就感&#xff0c;为以后的学习饱满激情。 注意这里的讲解是由部分到整体的思路。 目录 控制台不能是终端&am…

ubuntu环境下使用g++把c++编译成汇编语言(暂时)

1. 引言 为了深入理解c&#xff0c;决定学习一些简单的汇编语言。使用ubuntu系统下g很容易将一个c的文件编译成汇编语言。本文使用此方法&#xff0c;对一个简单的c文件编译成汇编语言进行理解。 2.示例 文件名&#xff1a;reorder_demo.cpp #include<stdio.h>typede…

逻辑回归+分类的评估方式

一&#xff1a;什么是逻辑回归 解决二分类问题 二&#xff1a;损失及优化 三&#xff1a;逻辑回归API 四&#xff1a;案例 五&#xff1a;分类的评估方式 评估公式 分类评估API ROC与AUC&#xff08;介绍API&#xff09;衡量不平衡样本 ROC曲线的绘制 分类中解决类别不平衡

HackmyVM-----Boxing靶机

文章目录 正常打靶流程1.获取靶机IP地址2.获取靶机端口服务3.访问网页4.添加域名WindowsLinux 5.访问域名6.nc反弹shell 7.结束 正常打靶流程 1.获取靶机IP地址 ┌──(root㉿kali)-[/home/kali] └─# arp-scan -l Interface: eth0, type: EN10MB, MAC: 00:0c:29:10:3c:9b, …

Drive Scope for Mac:硬盘健康监测分析工具

Drive Scope for Mac是一款专为Mac用户设计的硬盘健康监测与分析工具&#xff0c;致力于保障用户的数据安全。这款软件功能强大且操作简便&#xff0c;能够实时检测硬盘的各项指标&#xff0c;帮助用户及时发现并解决潜在问题。 Drive Scope for Mac 1.2.23注册激活版下载 Driv…

linux 驱动-匹配2 (amba_bustype)

目录 1.实例分析 a. 设备树实例 b. 驱动实例 2. amba匹配流程 a. 创建amba_device b. 确定总线以及总线的匹配函数 c. 分析总线的匹配函数 1.实例分析 a. 设备树实例 serial7e201000 { compatible "brcm,bcm2835-pl011\0arm,pl011\0arm,primecell"; //创建am…

用Python自动化操作PPT,看完这篇文章就够了!

1.PPT自动化能干什么&#xff1f;有什么优势&#xff1f; 它可以代替你自动制作PPT它可以减少你调整用于调整PPT格式的时间它可以让数据报告风格一致总之就是&#xff1a;它能提高你的工作效率&#xff01;让你有更多时间去做其他事情&#xff01; 2.使用win32com操作ppt 官…

【探索Linux】P.32(自定义协议)

阅读导航 引言一、自定义协议概念二、自定义协议需要注意的事项三、自定义协议示例(跨网络计算器协议)✅协议代码&#xff08;Protocol.hpp&#xff09;1. 计算器协议简单介绍2. 序列化部分3. 反序列化部分4. 请求和响应数据结构5. 使用自定义协议 四、总结温馨提示 引言 在上…

Rust Tracing 入门

Tracing 是一个强大的工具&#xff0c;开发人员可以使用它来了解代码的行为、识别性能瓶颈和调试问题。 Rust 是一种以其性能和安全保证而闻名的语言&#xff0c;在它的世界中&#xff0c;跟踪在确保应用程序平稳高效运行方面发挥着至关重要的作用。 在本文中探讨Tracing 的概…

C++ 初识模板

目录 0.前言 1.泛型编程 2.函数模板 2.1概念 2.2格式 2.3原理 2.4函数模板的实例化 2.4.1隐式实例化 2.4.2显式实例化 2.5模板参数的匹配原则 3.类模板 3.1类模板的定义格式 3.2类模板的实例化 4.结语 &#xff08;图像由AI生成&#xff09; 0.前言 在 C 中&a…

Unity3D 爆火的休闲益智游戏工程源码/3D资源 大合集

Unity3D休闲益智游戏工程源码大合集 一、关卡类游戏工程源码二、跑酷类游戏工程源码三、消除合成类游戏工程源码四、棋牌类游戏工程源码五、RPG(角色扮演)类游戏工程源码六、FPS&#xff08;射击&#xff09;类游戏工程源码十、Unity3D工艺仿真六、Unity游戏资源1、Unity3D 吃鸡…

Redis数据类型——String

Redis官网指令文档&#xff1a;Commands | Docs 前言 此处的String类型是针对Redis的Value的&#xff0c;因为Key的形式都是String&#xff0c;而Value则有哈性、列表、集合等形式。 众所周知&#xff0c;由于不同编码&#xff0c;经常会出现乱码的问题&#xff0c;但在Redi…

打造稳定安全的亚马逊测评环境:关键步骤与要点一览

亚马逊测评环境的搭建是一项既复杂又需要深入细致考虑的工作&#xff0c;它涉及多方面的技术配置和资源准备。以下是一些关键步骤和要点&#xff0c;帮助您更高效地构建测评环境。 一、资源筹备 1. 养号系统&#xff1a;选择稳定、高效的养号系统&#xff0c;确保能够模拟真实…

Linux系统-进程和计划任务管理

一.程序和进程 1.程序 保持在硬盘、光盘等介质中的可执行代码和数据文件中静态保存的代码 2.进程 在CPU及内存中运行的程序代码动态执行的代码父、子进程每个程序可以创建一个或多个进程 3.进程特征 动态性&#xff1a;进程是程序的一次执行过程&#xff0c;是临时的&…

决策树分类任务实战(python 代码详解)

目录 一、导入库、数据集、并划分训练集和测试集 二、参数调优 (一)第一种调参方法&#xff1a;for循环 (1)单参数优化 ①单参数优化(无K折交叉验证) ②单参数K折交叉验证 优化 (2)多参数优化 ①多参数优化(无K折交叉验证) 参数介绍&#xff1a; ②多参数K折交叉验证…

vulfocus靶场名称: apache-cve_2021_41773/apache-cve_2021_42013

Apache HTTP Server 2.4.49、2.4.50版本对路径规范化所做的更改中存在一个路径穿越漏洞&#xff0c;攻击者可利用该漏洞读取到Web目录外的其他文件&#xff0c;如系统配置文件、网站源码等&#xff0c;甚至在特定情况下&#xff0c;攻击者可构造恶意请求执行命令&#xff0c;控…

记录一下hive启动metestore服务时报错

【背景说明】 之前hadoop有问题&#xff0c;把hadoop和MySQL删了重装&#xff0c;hive没有动&#xff0c;然后启hive的metastore服务的时候&#xff0c;显示找不到metastore数据库 【报错】 Caused by: java.lang.reflect.InvocationTargetExceptionat sun.reflect.Generated…

【Java框架】SpringMVC(一)——基本的环境搭建及基本结构体系

目录 MVC模式视图(View)控制器(Controller)模型(Model)JSP Model1JSP Model2MVC的优点MVC的缺点 Spring MVC架构介绍特点 SpringMVC环境搭建(在前面Spring整合Mybatis的基础上)1.创建控制器Controller2.创建springmvc配置文件&#xff0c;并添加Controller的Bean3.web.xml中配置…

# 从浅入深 学习 SpringCloud 微服务架构(二)模拟微服务环境(1)

从浅入深 学习 SpringCloud 微服务架构&#xff08;二&#xff09;模拟微服务环境&#xff08;1&#xff09; 段子手168 1、打开 idea 创建父工程 创建 artifactId 名为 spring_cloud_demo 的 maven 工程。 --> idea --> File --> New --> Project --> Ma…