单例模式与反射创建对象

单例模式

饿汉式单例模式

单例模式,就是自己先把自己创建了,整个程序都只有这一个实例,别人都没有办法创建实例,因为他的构造方法是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,一经查实,立即删除!

相关文章

接口压力测试 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…

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

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

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修复等原因&…

函数的创建和调用及删除

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;应该是属于物理层数据…

学习ArkTS -- 状态管理

装饰器 State 在声明式UI中&#xff0c;是以状态驱动试图更新&#xff1a; 状态&#xff08;State&#xff09;&#xff1a;指驱动视图更新的数据&#xff08;被装饰器标记的变量&#xff09; 视图&#xff08;View&#xff09;&#xff1a;基于UI描述渲染得到用户界面 说明…

病理验证mIF和TMA路线(自学)

目录 技术 使用配对病理切片 mIF验证 单基因使用TMA验证 技术 多重荧光免疫组化技术 (Multiplex immunohistochemical&#xff0c;mIHC) 也称作酪氨酸信号放大 (Tyramide dignal amplification&#xff0c;TSA) 技术&#xff0c;是一类利用辣根过氧化酶 (Horseradish Pero…

weblogic反序列化漏洞(CVE-2017-10271)复现

直接用vuluhub搭建现成的靶场做 访问靶场 打开是这样表示成功 想反弹shell 就先开启kali1的nc监听&#xff0c;这就监听2233端口吧 linux&#xff1a;nc -l -p 2233 抓包修改为攻击数据包 ip和端口可以任意修改 反弹的shell 还可以写入文件shell 只需要把提供的poc POS…

修复Windows搜索不工作的几种方法,总有一种适合你

序言 Windows搜索是Windows 10中一个非常有用的功能,它允许你搜索特定的程序、应用程序、文档、图片、文件、设置等,以便快速访问它们。但有时Windows搜索找不到我们预期的结果,甚至没有响应,这会给Windows用户带来很多不便。如果Windows 10中的搜索栏不工作,该怎么办?你…

【MySQL】SQL优化

SQL优化 插入数据 insert 一次插入数据和批量插入数据 insert into tb_test (id, name) values (1,Tom); insert into tb_test (id, name) values (1,Tom),(2,Jack),(3,Jerry);优化方案&#xff1a; 手动控制事务&#xff0c;且按主键顺序插入。start transaction; insert …

机器人实验室LAAS-CNRS介绍

一、LAAS-CNRS介绍 1、缩写介绍 同样的&#xff0c;给出英文缩写的全称&#xff0c;以便理解。这里的LAAS&#xff08;Laboratory for Analysis and Architecture of Systems&#xff09;指法国的系统分析与架构实验室&#xff0c;CNRS&#xff08;Centre National de la Rec…

OpenHarmony实战开发-提升应用响应速度。

应用对用户的输入需要快速反馈&#xff0c;以提升交互体验&#xff0c;因此本文提供了以下方法来提升应用响应速度。 避免主线程被非UI任务阻塞减少组件刷新的数量 避免主线程被非UI任务阻塞 在应用响应用户输入期间&#xff0c;应用主线程应尽可能只执行UI任务&#xff08;…