单例模式与反射创建对象

单例模式

饿汉式单例模式

单例模式,就是自己先把自己创建了,整个程序都只有这一个实例,别人都没有办法创建实例,因为他的构造方法是private的

  • 一次性把全部都创建了
public class HungryMan {private static int [][] s = new int[5][5];private static int [][] s1 = new int[5][5];private static int [][] s2 = new int[5][5];private static HungryMan hungryMan = new HungryMan();private HungryMan() {}private static HungryMan getInstance() {return hungryMan;}
}
  • 这样就会存在很多浪费空间

懒汉式单例模式

为了解决这种浪费,出现了懒汉式单例模式

  • 就是我什么都不创建,需要使用的时候再去创建

  • public class LazyMan {private static LazyMan lazyMan;private LazyMan() {}private static LazyMan getLazyMan(){if(lazyMan == null){lazyMan = new LazyMan();}return lazyMan;}
    }
    

单例模式的问题

  • 在单线程下,创建是没有问题的,但是在多线程下创建就会有问题
public class LazyMan {private static LazyMan lazyMan;private LazyMan() {System.out.println(Thread.currentThread().getName() );}private static LazyMan getLazyMan(){if(lazyMan == null){lazyMan = new LazyMan();}return lazyMan;}public static void main(String[] args) {for (int i = 0; i < 10; i++) {new Thread(()->{LazyMan.getLazyMan();}).start();}}
}
  • 这里注意几个点

    • 需要在构造方法中输出

    • 而不是在

      • new Thread(()->{System.out.println(LazyMan.getLazyMan());
        }).start();
        
      • 因为sout是加锁的,所以拿到都是同一个对象

    • 这样才能测试出结果

解决问题

  • 双重检测锁【DCL懒汉式】
    • 为什么需要二次判断
    • 因为第一个判断是在同步代码块外的,所以很多线程都会去判断
    • 如果没有第二个判断,可能存在有线程已经创建了实例,所以会创建出多个实例
    • 当然你直接将整个代码块全部锁上也是可以的
    • 这个代码也是不安全的
    • 因为创建对象也不是原子性操作
      • 分配空间
      • 执行构造方法,初始化对象
      • 把对象指向这个空间
    • 所以也是不安全的,会出现重排,导致不安全
    • 需要给对象加上volatile,防治重排
//双重检测锁,解决这个问题
private static LazyMan getLazyMan(){if(lazyMan == null){synchronized (LazyMan.class) {if(lazyMan == null){lazyMan = new LazyMan();}}}return lazyMan;
}
  • 双重检测锁的问题

    • public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {//通过构建实例,构建对象LazyMan lazyMan = LazyMan.getLazyMan();//getClass拿到反射对象,通过反射对象获取到构造方法,因为是无参,所以是nullConstructor<? extends LazyMan> constructor = lazyMan.getClass().getDeclaredConstructor(null);//将私有变量变为公有变量constructor.setAccessible(true);//创建实例LazyMan lazyMan1 = constructor.newInstance();//或者LazyMan lazyMan2 = new LazyMan();System.out.println(lazyMan);System.out.println(lazyMan1);System.out.println(lazyMan2);
      }
      
    • 通过反射将其破坏了

  • 解决方式

    • 创建对象的时候,添加判断
private LazyMan() {synchronized (LazyMan.class){if(lazyMan != null){throw new RuntimeException("实例已经被创建");}}
}
  • 出现新的问题
    • 之前通过实例.getClass(),拿到反射对象,现在直接LazyMan.Class拿到对象,也不new对象了,直接newInstance(),获得到对象,这样单例模式还是被破坏了
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {//通过构建实例,构建对象//LazyMan lazyMan = LazyMan.getLazyMan();//getClass拿到反射对象,通过反射对象获取到构造方法,因为是无参,所以是nullConstructor<? extends LazyMan> constructor = LazyMan.class.getDeclaredConstructor(null);//将私有变量变为公有变量constructor.setAccessible(true);//创建实例LazyMan lazyMan1 = constructor.newInstance();LazyMan lazyMan2 = constructor.newInstance();System.out.println(lazyMan1);System.out.println(lazyMan2);
}
  • 枚举类

    • 枚举类是没有不可以通过反射办法破环单例模式的

    • 在这里插入图片描述

    • 可以看出如果获取枚举类的instance是会抛出异常的

    • 尝试破坏枚举类的单例模式

    • public enum LazyEnum {INSTANCE;}
      class Test{public static void main(String[] args) throws InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException {LazyEnum instance = LazyEnum.INSTANCE;System.out.println(instance);Constructor<LazyEnum> declaredConstructor = LazyEnum.class.getDeclaredConstructor(String.class, int.class);declaredConstructor.setAccessible(true);LazyEnum lazyEnum = declaredConstructor.newInstance();System.out.println(lazyEnum);}
      }
      
    • 关于这个为什么是一个有参数的构造

      • 如何知道枚举类是有参构造还是无参构造

      • 在这里插入图片描述

      • 这里显示了是一个无参构造

      • 在这里插入图片描述

      • 对于字节码文件的查看,显示的也是无参构造

    • 如果是无参构造就会抛出NoSuchMethod的错误,表示没有这个方法

    • 需要使用别的方法去查看到底是有参数还是无参构造

      • 使用的软件是jad.exe
      • 将LazyEnum.class文件反编译成Lazy.java文件,再进去查看,这样我们可以得到一个含有有参构造的构造函数,参数分别是Sring 和 int(注意不是Integer)

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

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

相关文章

Debezium分享系列之:部署Debezium采集Oracle数据库的详细步骤

Debezium分享系列之:部署Debezium采集Oracle数据库的详细步骤 一、部署Debezium Oracle连接器二、Debezium Oracle 连接器配置三、添加连接器配置四、可插拔数据库与不可插拔数据库一、部署Debezium Oracle连接器 部署的详细步骤可以参考博主这篇技术文章: Debezium系列之:…

接口压力测试 jmeter--增强篇(二)

前期准备 1. JMeter的插件的安装 下载Jmeter Plugins Manager对插件进行管理 &#xff08;1&#xff09;下载地址&#xff1a;https://jmeter-plugins.org/install/Install/ &#xff08;2&#xff09;下载后&#xff0c;将jar包放到jmeter包目录下/lib/ext目录下 &#xff0…

CUDA编程---线程束洗牌指令

从Kepler系列的GPU&#xff08;计算能力为3.0或更高&#xff09;开始&#xff0c;洗牌指令&#xff08;shuffle instruction&#xff09;作为一种机制被加入其中&#xff0c;只要两个线程在相同的线程束中&#xff0c;那么就允许这两个线程直接读取另一个线程的寄存器。 洗牌指…

清华大学:序列推荐模型稳定性飙升,STDP框架惊艳登场

获取本文论文原文PDF&#xff0c;请公众号留言&#xff1a;论文解读 引言&#xff1a;在线平台推荐系统的挑战与机遇 在线平台已成为我们日常生活中不可或缺的一部分&#xff0c;它们提供了丰富多样的商品和服务。然而&#xff0c;如何为用户推荐感兴趣的项目仍然是一个挑战。…

【笔记】Telephony SIM SPN及运营商名称显示数据来源介绍

来源介绍 网络名称显示 来源及优先级&#xff08;高到低&#xff09; SourceCommentEnhanced Operator Name String(Eons) 名称信息存放&#xff1a; EF_PNN(PLMN Network Name, fid: 6FC5) &#xff1a;LAC和EF_PNN中的Record Identifier EF_OPL(Operator PLMN List, fid: 6FC…

rst文件是什么?如何阅读rst文件

左手编程&#xff0c;右手年华。大家好&#xff0c;我是一点&#xff0c;关注我&#xff0c;带你走入编程的世界。 公众号&#xff1a;一点sir&#xff0c;关注领取编程资料 如果有阅读过Python源码的同学一定知道&#xff0c;Python社区的相关的帮助文件是用rst结尾的文档格式…

67条tips实战案例渗透测试大佬的技巧总结

67条tips实战案例渗透测试大佬的技巧总结。 Tips 1. 手动端口探测 nmap的-sV可以探测出服务版本&#xff0c;但有些情况下必须手动探测去验证 使用Wireshark获取响应包未免大材小用&#xff0c;可通过nc简单判断 eg. 对于8001端口&#xff0c;nc连接上去&#xff0c;随便输…

python面向对象的使用(2)

题目 面向对象模拟电影院自动售票系统实现自动选择电影、场次、座位。 思路 通过类定义电影的相关信息&#xff0c;输出输入相关电影信息&#xff0c;对座位进行顺序取 代码解释 class Movie:def __init__(self, title, duration):self.title titleself.duration durati…

GlobalFilter全局过滤器

这个跟跟刚才那个GatewatFilert默认全局配置的效果是一样的&#xff0c;但是那个是配置&#xff0c;只能使用已有的进行配置&#xff0c;GlobalFilter全局过滤器是通过类实现的 可以自己用代码实现拦截后要处理的逻辑。 定义方式&#xff1a; 先实现GlobalFilter接口&#xf…

深入C语言,发现多样的数据之枚举和联合体

一、枚举 枚举 是列出某些有穷序列集的所有成员的程序&#xff0c;或者是一种特定类型对象的计数。这两种类型经常&#xff08;但不总是&#xff09;重叠。是一个被命名的整型常数的集合。简单来说就将某种特定类型的对象一一进行列举&#xff0c;一一列举特定类型可能的取值。…

探索RadSystems:低代码开发的新选择(二)

系列文章目录 探索RadSystems&#xff1a;低代码开发的新选择&#xff08;一&#xff09;&#x1f6aa; 文章目录 系列文章目录前言一、RadSystems Studio是什么&#xff1f;二、用户认证三、系统角色许可四、用户记录管理五、时间戳记录总结 前言 在数字化时代&#xff0c;低…

【做一名健康的CSDNer】程序员哪几种行为最伤肾(程序员必看)

虽然没有专门针对程序员这一职业群体特有的伤肾行为的研究报道&#xff0c;但根据一般人群的健康风险和生活习惯&#xff0c;程序员由于其特殊的工作模式和环境&#xff0c;可能更容易出现如下伤肾的行为&#xff1a; 熬夜加班&#xff1a; 程序员由于项目进度、bug修复等原因&…

LOD2-Unity中Shader LOD技术原理以及使用

Shader LOD&#xff08;Level of Detail&#xff09;是Unity中用于优化渲染性能的一种技术。它通过在不同的距离或屏幕空间中使用不同的着色器来控制模型的细节级别&#xff0c;从而减少GPU的工作量。 Shader LOD的原理是根据相机与物体之间的距离来选择合适的着色器。在远离相…

高斯锁表导致sql报错处理

构造锁等待场景&#xff1a; 1.打开一个新的连接会话&#xff0c;使用普通用户连接GaussDB(DWS)数据库&#xff0c;在test SCHEMA 下创建测试表test.ypg_test。 CREATE TABLE ypg_test (id int, name varchar(50)); 2.开启事务1&#xff0c;进行INSERT操作。 START TRANSACTI…

【操作系统】——调度算法——小题

单核处理器系统中&#xff0c;有3个作业J1,J2,J3同时到达&#xff0c;其运行时间分别为1h&#xff0c;4h&#xff0c;2h&#xff0c;以单道方式运行&#xff0c;下面的执行序列中周转时间最短的是&#xff08;&#xff09; A,j1,j2,j3 Bj3,j2,j1 Cj2,j1,j3 Dj1,j3,j2 在各种作业…

GPFL个性化联邦学习:同时学习全局和个性化特征信息

Global category embedding指的是将全局类别信息嵌入到模型中的过程。在机器学习和深度学习中&#xff0c;当处理具有多个类别的数据集时&#xff0c;可以使用全局类别嵌入来将类别信息编码到模型中&#xff0c;以帮助模型更好地理解和利用类别之间的关系。这可以帮助提高模型在…

函数的创建和调用及删除

Oracle从入门到总裁:​​​​​​https://blog.csdn.net/weixin_67859959/article/details/135209645 函数和存储过程非常类似&#xff0c;也是可以存储在 Oracle 数据库中的 PL/SQL代码块&#xff0c;但是有返回值。 可以把经常使用的功能定义为一个函数&#xff0c;就像系统…

数仓建模—逻辑数据模型

数仓建模—逻辑数据模型 数据模型是数据元素及其基于现实世界对象之间的关系的可视化表示。数据模型揭示并定义数据在业务流程中的连接方式,并支持创建高效的信息系统或应用程序。例如,在商业智能中,数据模型定义用户可以在其分析中使用哪种数据。 逻辑数据模型 (LDM Logi…

【C++ STL序列容器】array 数组

文章目录 【 1. 基本原理 】【 2. array 的创建 】2.1 不赋初值2.2 赋默认值2.3 赋指定值 【 3. array 的成员函数 】实例 【 1. 基本原理 】 array 是在 C 普通数组的基础上添加了一些成员函数和全局函数。在使用上&#xff0c;它 比普通数组更 安全&#xff0c;且效率并没…

以太网帧格式解析

以太网的正式标准是IEEE802.3&#xff0c;它规定了以太网传输的帧结构。 以太网帧格式如下图所示&#xff1a; 以太网传输数据时&#xff0c;是按照上图的格式&#xff0c;自左到右依次传输的。需要注意的是前导码和SFD不属于以太网协议的内容&#xff0c;应该是属于物理层数据…