设计模式之单例模式

devtools/2024/9/18 12:41:59/ 标签: 设计模式, 单例模式, java

五种实现方式:

方式一:饿汉式
//饿汉式
public class Singleton1 implements Serializable {private static final Singleton1 SINGLETON_TEST=new Singleton1();//构造私有private Singleton1() {System.out.println("私有构造方法");}public static Singleton1 getInstance(){return SINGLETON_TEST;}public static void otherMotherd(){System.out.println("其他方法");}
}

测试:
 


public class SingleTest {public static void main(String[] args) {Singleton1.otherMotherd();//检测是饿汉式//私有构造方法//其他方法System.out.println(Singleton1.getInstance());//Singleton1@4d7e1886System.out.println(Singleton1.getInstance());//Singleton1@4d7e1886//以上两个实例为同一个对象}
}

结果:

 单例被破坏的情况: 
  1. 序列化和反序列化:

当单例类实现了Serializable接口时,对象被序列化后再反序列化时会创建新的实例。为了避免这种情况,可以通过重写readResolve()方法来返回单例实例。

测试反序列化后的对象和原始对象是否一致:

/*** 实现序列化接口后可能破坏单例模式*/@Testvoid test5() throws Exception {//将对象写入文件中ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("single1.ser"));oos.writeObject(Singleton1.getInstance());oos.close();//从文件中读取对象ObjectInputStream ois=new ObjectInputStream(new FileInputStream("single1.ser"));Singleton1 single = (Singleton1) ois.readObject();ois.close();System.out.println("original:"+Singleton1.getInstance().hashCode());System.out.println("DeSerializable:"+single.hashCode());}

结果:

预防:

public class Singleton implements Serializable {private static final Singleton instance = new Singleton();private Singleton() {}public static Singleton getInstance() {return instance;}protected Object readResolve() {return instance;}
}
  1. 反射机制:

通过反射可以调用类的私有构造方法,从而创建多个实例。为了防止通过反射破坏单例模式,可以在构造方法中添加逻辑判断,确保只创建一个实例。

反射破坏单例:

@Testvoid test() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {Singleton1 instance = Singleton1.getInstance();Constructor<? extends Singleton1> constructor = instance.getClass().getDeclaredConstructor();constructor.setAccessible(true);Singleton1 singleton1 = constructor.newInstance();System.out.println(instance==singleton1);}

 结果:

预防:在构造方法处进行判断


//饿汉式
public class Singleton2 implements Serializable {private static final Singleton2 SINGLETON_TEST=new Singleton2();//构造私有private Singleton2() {if (SINGLETON_TEST!=null){throw new IllegalStateException("对象已经创建");}System.out.println("私有构造方法");}public static Singleton2 getInstance(){return SINGLETON_TEST;}public static void otherMotherd(){System.out.println("其他方法");}
}

 以上防止反射破坏单例测试:

/***防止单例被破坏*/@Testvoid test3() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {Singleton2 instance = Singleton2.getInstance();Constructor<? extends Singleton2> constructor = instance.getClass().getDeclaredConstructor();constructor.setAccessible(true);Singleton2 singleton2 = constructor.newInstance();System.out.println(instance==singleton2);}

结果:

  1. 类加载器:

如果使用不同的类加载器加载同一个类,也可能导致创建多个实例。为了解决这个问题,可以在获取实例时指定类加载器,确保只有一个实例被创建。

测试使用不同的类加载器导致同一个类创建多个实例的情况:
 

 public static void main(String[] args) throws Exception {CurstomLoader loader1=new CurstomLoader();CurstomLoader loader2=new CurstomLoader();Class<?> single1 = loader1.loadClass("com.example.singletonmodle.single.Singleton1");Class<?> single2 = loader2.loadClass("com.example.singletonmodle.single.Singleton1");Singleton1 instance1 = (Singleton1) single1.newInstance();Singleton1 instance2= (Singleton1) single2.newInstance();System.out.println("loader1:"+instance1.hashCode());System.out.println("loader2:"+instance2.hashCode());}

 

public class Singleton {private static Singleton instance = new Singleton();private Singleton() {}public static Singleton getInstance(ClassLoader classLoader) {synchronized (Singleton.class) {if (instance == null) {try {Class<?> clazz = classLoader.loadClass(Singleton.class.getName());instance = (Singleton) clazz.newInstance();} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {e.printStackTrace();}}}return instance;}
}
  1. 通过unsafe破坏单例:目前没有预防的方法。

 总结:多线程破坏单例使用DCL+voliatle预防,反射破坏单例使用在构造方法处检查对象是否存在进行预防,实现序列化接口破坏单例使用实现readResolve方法,返回已经创建的单例对象。unsafe目前还没有预防的方式。

方式二:枚举类


public enum Singleton3 {INSTANCE;private Singleton3(){System.out.println("枚举类的私有构造方法");}@Overridepublic String toString() {//getClass().getName()返回该对象的类名,Integer.toHexString(hashCode())返回该对象的哈希码的十六进制表示。return getClass().getName()+"@"+Integer.toHexString(hashCode());}public Singleton3 getInstance(){return INSTANCE;}public void otherMethod(){System.out.println("其他方法");}}

测试:

 枚举实现的单例可以预防反序列化破坏单例

枚举也可以预防反射破坏单例

枚举类的构造方法不是无参构造,有两个参数。

但是枚举无法预防unsafe破坏单例模式

方式三:懒汉式
 
//懒汉式
public class Singleton4 {private static  Singleton4 SINGLETON_TEST;//构造私有private Singleton4() {System.out.println("私有构造方法");}public static Singleton4 getInstance(){
if (SINGLETON_TEST==null){SINGLETON_TEST=new Singleton4();}return SINGLETON_TEST;}public static void otherMotherd(){System.out.println("其他方法");}
}

测试:

以上单例模式在多线程下存在单例被破坏的可能。

  1. 多线程环境下未处理好并发访问:

如果在多线程环境下,多个线程同时尝试获取单例实例,可能会导致创建多个实例的情况。可以使用DCL(双重检测来确保只有一个实例被创建,DCL需要配合volatile使用,确保可见性)。懒汉式

public class Singleton {private static final Singleton instance = new Singleton();private Singleton() {}public static Singleton getInstance() {return instance;}
}public class Main {public static void main(String[] args) {// 创建多个线程同时获取单例实例Thread thread1 = new Thread(() -> {Singleton singleton = Singleton.getInstance();System.out.println("Thread 1: " + singleton.hashCode());});Thread thread2 = new Thread(() -> {Singleton singleton = Singleton.getInstance();System.out.println("Thread 2: " + singleton.hashCode());});thread1.start();thread2.start();}
}

预防:DCL+voliatle ,也就是第四种实现方式。

方式四:DCL+voliatle
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 Singleton6 {private static  Singleton6 SINGLETON_TEST=null;//构造私有private Singleton6() {System.out.println("私有构造方法");}private static class Holder{static Singleton6 INSTANCE=new Singleton6();}public static Singleton6 getInstance(){return Holder.INSTANCE;}public static void otherMotherd() {System.out.println("其他方法");}
}

JDK中单例的体现方式:
单例模式一般在jdk的一些库中见到,自己不要乱用单例模式,很容易用错。


http://www.ppmy.cn/devtools/6956.html

相关文章

数据库服务的运行与登录

打开数据库服务 数据库服务: SQL Server(MSSQLServer) 运行在服务器端的应用程序, 提供数据的存储 / 处理和事务等在使用DBMS的客户端之前必须首先打开该服务 客户端连接到服务器 关于客户端 / 服务器端的说明 客户端 : 数据库管理系统(DBMS), 应用程序服务器端 : 安装的数据…

论文笔记:UrbanGPT: Spatio-Temporal Large Language Models

1 intro 时空预测的目标是预测并洞察城市环境随时间和空间不断变化的动态。其目的是预见城市生活多个方面的未来模式、趋势和事件&#xff0c;包括交通、人口流动和犯罪率。虽然已有许多努力致力于开发神经网络技术&#xff0c;以准确预测时空数据&#xff0c;但重要的是要注意…

EF6(Entity Framework 6)基础知识

一、Entity Framework 6 概述 Entity Framework (EF) 是 Microsoft 提供的一个对象关系映射器 (ORM)&#xff0c;它使得 .NET 开发人员能够使用 .NET 对象来处理数据库&#xff0c;从而无需经常编写大部分数据访问代码。EF 提供了许多功能&#xff0c;包括更改跟踪、查询构建、…

Arrays

Arrays&#xff1a;用来操作数组的一个工具类 Arrays类提供的常见方法&#xff1a; 方法名说明public static String toString&#xff08;类型[ ] arr&#xff09;返回数组的内容public static int[] copyOfRange(类型[ ] arr,起始索引&#xff0c;结束索引)拷贝数组&…

6.MMD ray渲染 材质的添加及打光方法

材质 前置准备 先准备好模型和场景 将ray控制器拖入进去 添加完默认的材质以后的效果 打开插入材质页面 打开MaterialMap栏 将流萤的模型展开 自发光 现在给领带添加一个自发光效果 在自发光Emissive里&#xff0c;打开x1&#xff0c;选择albedo&#xff0c;白光 现在…

HDMI to TYPE-C芯片|HDMI2.0转TYPE-C转接器方案|CS5802设计方案|ASL CS5802

CS5802输入端可以是1080P、4K30、4K60HZ这三种规格,输出的接口可以是TYPE-C信号接口,或者是TYPE-C信号接口,输入端HDMI由4路信号组成&#xff0c;支持1.62Gbps、2.7Gbps、5.4Gbps链路速率。内置可选SSC功能可降低EMI的干扰状况。 ASL CS5802芯片概述&#xff1a; 符合HDMI规范…

蓝桥杯备考随手记: practise08

问题描述: 我们知道第一个质数是 2、第二个质数是 3、第三个质数是 5…… 请你计算第 2019 个质数是多少&#xff1f; 思路分析: 质数是指除了1和自身以外没有其他因数的正整数。因此可以通过检查一个数是否有除1和自身以外的因数来判断它是否为质数。 可以从2开始逐个检查…

【声呐仿真】学习记录1-配置dave、uuv_simulator

【声呐仿真】学习记录1-配置dave、uuv_simulator 1.介绍2.配置3.一些场景 1.介绍 家|DAVE项目 — Home | Project DAVE 2.配置 参考官方教程安装|DAVE项目 — Installation | Project DAVE mkdir -p ~/uuv_ws/src cd ~/uuv_ws/src git clone https://github.com/Field-Robot…

如何实现在 Windows 上运行 Linux 程序?

在Windows 上运行Linux程序是可以通过以下几种方法实现: 1.使用 Windows Subsystem for Linux (WSL): WSL是微软提供的功能&#xff0c;可以在Windows 10上运行一个完整的Linux系统。用户可以在Microsoft Store中安装所需的 在开始前我有一些资料&#xff0c;是我根据网友给的…

UE5 在骨骼动画模型上绘制贴图

参考&#xff1a;Unreal 5.1 - How to paint damage textures and other effects on skeletal meshes 针对模型&#xff0c;在运行状态下通过射线指定一定范围&#xff0c;添加材质效果。 核心思路 通过射线获取命中点&#xff0c;作为材质参数材质中&#xff0c;命中的世界…

HTML5+CSS3小实例:菜单按钮的三种切换动画

实例:菜单按钮的三种切换动画 技术栈:HTML+CSS 效果: 源码: 【HTML】 <!DOCTYPE html> <html lang="zh-CN"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initia…

pytorch中模型训练的学习率动态调整

pytorch动态调整学习率 背景手动设置自动衰减的学习率pytorch中的torch.optim.lr_schedulertorch.optim.lr_scheduler.ExponentialLRtorch.optim.lr_scheduler.StepLRtorch.optim.lr_scheduler.MultiStepLRtorch.optim.lr_scheduler.ReduceLROnPlateau 背景 在神经网络模型的训…

Arrow, 一个六边形的 Python 时间库

文章目录 Arrow, 一个六边形的 Python 时间库第一部分&#xff1a;背景介绍第二部分&#xff1a;库是什么&#xff1f;第三部分&#xff1a;如何安装这个库&#xff1f;第四部分&#xff1a;库函数使用方法第五部分&#xff1a;场景应用第六部分&#xff1a;常见Bug及解决方案第…

ICV:《中美量子产业融资比较分析》

近日&#xff0c;全球前沿科技咨询公司ICV发布了A Comparative Analysis of Quantum Industry Financing in the U.S and China&#xff08;美国和中国量子产业融资比较分析&#xff09;报告。该报告旨在对中美两国在量子技术领域的投融资情况进行比较分析&#xff0c;探讨其差…

企业数据分析的维度一般有哪些?

​在很多场景下&#xff0c;都会进行企业的一个分析&#xff0c;来反应我们的问题。常见的需要分析企业数据的场景有&#xff1a;业务优化&#xff08;月度季度&#xff09;&#xff0c;需要做投资决策时&#xff0c;有融资需求&#xff0c;或者战略上出现了改变时&#xff0c;…

Canvas图形编辑器-数据结构与History(undo/redo)

Canvas图形编辑器-数据结构与History(undo/redo) 这是作为 社区老给我推Canvas&#xff0c;于是我也学习Canvas做了个简历编辑器 的后续内容&#xff0c;主要是介绍了对数据结构的设计以及History能力的实现。 在线编辑: https://windrunnermax.github.io/CanvasEditor开源地…

TypeScript-官方基础模板创建的小程序,如何创建js文件

如何创建JS文件&#xff0c;不需要寻找“js”文件类型&#xff0c;只需要创建一个新的“文件”即可。 第一步:先删除 ts文件;如 index.ts 第二步:右键点击项目&#xff0c;选择“新建”&#xff0c;然后选择“文件”。 第三步:在弹出的界面中&#xff0c;在“文件名”中输入“…

Python 网络与并发编程(二)

文章目录 线程Thread创建方式join()守护线程全局锁GIL问题线程同步和互斥锁死锁信号量事件(Event)生产者和消费者模式 线程Thread 创建方式 Python的标准库提供了两个模块&#xff1a; _thread 和threading &#xff0c; _thread 是低级模块&#xff0c; threading 是高级模块…

在 Linux 中复制文件和目录

目录 ⛳️推荐 前言 在 Linux 命令行中复制文件 将文件复制到另一个目录 复制文件但重命名 将多个文件复制到另一个位置 复制时处理重复文件 交互式复制文件 在 Linux 命令行中复制目录 仅复制目录的内容&#xff08;不是目录&#xff09; 复制多个目录 测试你的知…

Django项目无法安装python-ldap依赖解决方案

最近工作中安排了一个Python web项目&#xff0c;使用Pycharm从git拉取代码后&#xff0c;配置号Python的解释器和pip后&#xff0c;Pycharm自动下载安装项目所需的依赖&#xff0c;但是有一个依赖django-auth-ldap4.1.0安装始终失败&#xff0c;最初的异常信息提示是&#xff…