返回顶部
首页 > 资讯 > 精选 >Java单例模式与破坏单例模式的概念是什么
  • 208
分享到

Java单例模式与破坏单例模式的概念是什么

2023-07-05 04:07:39 208人浏览 泡泡鱼
摘要

本文小编为大家详细介绍“Java单例模式与破坏单例模式的概念是什么”,内容详细,步骤清晰,细节处理妥当,希望这篇“Java单例模式与破坏单例模式的概念是什么”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。什么是单例

本文小编为大家详细介绍“Java单例模式与破坏单例模式的概念是什么”,内容详细,步骤清晰,细节处理妥当,希望这篇“Java单例模式与破坏单例模式的概念是什么”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。

什么是单例模式

经典设计模式又分23种,也就是GoF 23 总体分为三大类:

  • 创建型模式

  • 结构性模式

  • 行为型模式

Java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例、饿汉式单例、登记式单例。

单例模式有以下特点:

  • 单例类只能有一个实例。

  • 单例类必须自己创建自己的唯一实例。

  • 单例类必须给所有其他对象提供这一实例。

  单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。在计算机系统中,线程池缓存日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。这些应用都或多或少具有资源管理器的功能。每台计算机可以有若干个打印机,但只能有一个Printer Spooler,以避免两个打印作业同时输出到打印机中。每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。总之,选择单例模式就是为了避免不一致状态。

饿汉式(预加载)

饿汉式单例: 在类加载时,就会创建好将会使用的对象,可能会造成内存的浪费

示例:

public class Hungry {    // 创建唯一实例    private final static Hungry HUNGRY = new Hungry();    private Hungry(){}// 全局访问点 ---> 拿到HUNGRY实例    public static Hungry getIntance(){        return HUNGRY;    }}

而预加载就是先一步加载,我们没有使用该单例对象但是已经将其加载到内存中,那么就会造成内存的浪费

懒汉式(懒加载)

懒汉式改善了饿汉式浪费内存的问题,等到需要用到实例的时候再去加载到内存中

懒汉式写法( 线程安全 ):

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

但是在不进行任何同步干预的情况下,懒汉式不是线程安全的单例模式,经典的解决方案就是利用双重检验保证程序的原子性和有序性,如下示例:

public class LazyMan {    private LazyMan(){}    // 懒汉当中的双重检验锁 --> 可以保证线程安全    public volatile static LazyMan lazyMan;    // volatile 保证了new实例时不会发生指令重排    public static LazyMan getInstance(){        if (lazyMan==null){            synchronized (LazyMan.class){                // 此处上锁  以保证原子操作                if (lazyMan == null){                    lazyMan = new LazyMan();                }            }        }        return lazyMan;    }}

反射破坏单例模式

反射是一种动态获取类资源的一种途径,我们让然可以通过反射来获取单例模式中的更多实例:

public class LazyMan {    // 空参构造器    private LazyMan(){}    // 懒汉当中的双重检验锁 --> 可以保证线程安全    public volatile static LazyMan lazyMan;    // volatile 保证了new实例时不会发生指令重排    public static LazyMan getInstance(){        if (lazyMan==null){            synchronized (LazyMan.class){                // 此处上锁  以保证原子操作                if (lazyMan == null){                    lazyMan = new LazyMan();// 不是原子操作                }            }        }        return lazyMan;    }    public static void main(String[] args) throws Exception{        // 获取无参构造器        Constructor<LazyMan> constructor = LazyMan.class.getDeclaredConstructor(null);        constructor.setAccessible(true);// 无视私有        // 获取实例        LazyMan instance2 = constructor.newInstance();        LazyMan instance3 = constructor.newInstance();        LazyMan instance4 = constructor.newInstance();        // 懒汉式单例 获取唯一实例        LazyMan instance = LazyMan.getInstance();        System.out.println("getIntance获取实例(1)hashCode:"+instance.hashCode());        System.out.println("反射构造器newIntance获取实例(2)hashCode:"+instance2.hashCode());        System.out.println("反射构造器newIntance获取实例(3)hashCode:"+instance3.hashCode());        System.out.println("反射构造器newIntance获取实例(4)hashCode:"+instance4.hashCode());    }}

上述程序输出结果如下:

修复方式1:

// 对空参构造器进行上锁 并对唯一实例lazyman判断是否已经初始化 private LazyMan(){    if (lazyMan != null){throw new RuntimeException("不要试图破坏单例模式");    } }

但是这种修复方式仍然会被破坏,我们首先是利用了反射来获取LazyMan的空参构造器,并利用其构造器进行初始化获取实例,但是如果我们一直不调用getIntance方法来初始化lazyman实例而一直用反射获取,那么这种方式就形同虚设

因此,得出下一个修复方式。我们依然对空参构造器进行上锁,然后利用标志位保证我们的空参构造器只能使用一次,也就是最多只能为一个实例进行初始化。

修复方式2:

// 解决2.  对空参构造器进行上锁  利用标志位保证空参构造器只能初始化一次实例  但是标志位字段仍可以通过其他途径被拿到  并且修改    private static boolean flag = false;private LazyMan(){        synchronized(LazyMan.class){            if (flag == false){                flag = true;            }else {                throw new RuntimeException("不要试图破坏单例模式");            }        }    }

上述代码所示,利用flag作为标志位来保证空参构造器只能对最多一个实例执行初始化操作。但是,同时我们所设置的标志位flag同样存在被通过各种渠道拿到的风险,比如反编译。拿到flag标志后就可以对其修改,示例:

public static void main(String[] args) throws Exception{        // 获取无参构造器        Constructor<LazyMan> constructor = LazyMan.class.getDeclaredConstructor(null);        constructor.setAccessible(true);// 无视私有        // 懒汉式单例 获取唯一实例        LazyMan instance = LazyMan.getInstance();  // (1)        // 获取标志位字段并进行修改        Field flag1 = LazyMan.class.getDeclaredField("flag");        // (1) 处已经调用了空参构造器  flag变为true  此处修改为false 可以继续创建实例        flag1.set(instance,false);        LazyMan instance2 = constructor.newInstance();        // 与上述同理        flag1.set(instance2,false);        LazyMan instance3 = constructor.newInstance();        System.out.println("getIntance获取实例(1)hashCode:"+instance.hashCode());        System.out.println("反射构造器newIntance获取实例(2)hashCode:"+instance2.hashCode());        System.out.println("反射构造器newIntance获取实例(3)hashCode:"+instance3.hashCode());    }

那么既然如此,是不是单例程序无论如何设计最终都会被反射破坏呢?

事实并非如此,我们打开反射得到的构造器.newInstance方法源码查看:

// 我们只看如下两行if ((clazz.getModifiers() & Modifier.ENUM) != 0)            throw new IllegalArgumentException("Cannot reflectively create enum objects");

如上述代码所示,Java给出的解释为:

如果实际参数和形式参数的数量不同;如果原始参数的展开转换失败;或者如果在可能展开之后,参数值不能通过方法调用转换转换为相应的形式参数类型;如果此构造函数属于枚举类型。符合上述任一情况将会抛出IllegalArgumentException("Cannot reflectively create enum objects")非法参数异常

也就是说,枚举类型是可以避免单例模式被破坏的

public enum enumSingle {    INSTANCE;    public enumSingle getInstance() {        return INSTANCE;    }}class TestEnumSingle{    public static void main(String[] args) throws Exception {        // 下面我们尝试用反射来破坏枚举类        // 枚举类的构造器实际上带有两个参数 String和int        Constructor<enumSingle> declaredConstructor = enumSingle.class.getDeclaredConstructor(String.class,int.class);        declaredConstructor.setAccessible(true);        // 直接获取实例        enumSingle instance = enumSingle.INSTANCE;        // 反射获取实例        enumSingle enumSingle1 = declaredConstructor.newInstance();        System.out.println("类名直接访问获取实例hashCode:"+instance.hashCode());        System.out.println("反射实例hashCode:"+enumSingle1.hashCode());    }}// 最终抛出  java.lang.IllegalArgumentException: Cannot reflectively create enum objects

除了反射会打破单例之外,序列化Serializable也同样会破坏单例模式,具体体现是物品们同一对象在序列化前和反序列化之后不是同一对象。

读到这里,这篇“Java单例模式与破坏单例模式的概念是什么”文章已经介绍完毕,想要掌握这篇文章的知识点还需要大家自己动手实践使用过才能领会,如果想了解更多相关内容的文章,欢迎关注编程网精选频道。

--结束END--

本文标题: Java单例模式与破坏单例模式的概念是什么

本文链接: https://lsjlt.com/news/349551.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Java单例模式与破坏单例模式的概念是什么
    本文小编为大家详细介绍“Java单例模式与破坏单例模式的概念是什么”,内容详细,步骤清晰,细节处理妥当,希望这篇“Java单例模式与破坏单例模式的概念是什么”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。什么是单例...
    99+
    2023-07-05
  • Java单例模式与破坏单例模式概念原理深入讲解
    目录什么是单例模式饿汉式(预加载)懒汉式(懒加载)反射破坏单例模式什么是单例模式 经典设计模式又分23种,也就是GoF 23 总体分为三大类: 创建型模式结构性模式行为型模式 Jav...
    99+
    2023-02-21
    Java单例模式 Java破坏单例模式
  • java单例设计模式的概念
    这篇文章主要讲解了“java单例设计模式的概念”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“java单例设计模式的概念”吧!单例设计模式概述:     &nbs...
    99+
    2023-06-02
  • Java单例模式的创建,破坏和防破坏详解
    目录前言单例模式单例模式的几种实现方式懒汉式,线程不安全懒汉式,线程安全饿汉式双检锁/双重校验锁登记式/静态内部类枚举破坏单例模式未破坏的情况破坏后的情况单例模式的防破坏总结前言 ...
    99+
    2024-04-02
  • Java的单例模式是什么
    单例(Singleton)模式是Java中最简单的设计模式之一。这种类型的设计模式属于创建模式,因为此模式提供了创建对象的最佳方法之一。 (推荐学习:java课程)这种模式涉及一个类,它负责创建一个对象,同时确保只...
    99+
    2015-11-05
    java教程 Java
  • Java中怎么破坏单例方式
    本篇内容介绍了“Java中怎么破坏单例方式”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!单例模式(Singleton Pattern)是 J...
    99+
    2023-06-16
  • java中单例模式与Singleton
    前言这一篇来源我的公众号,如果你没看过,正好直接看看,如果看过了也可以再看看,我稍微修改了一些内容,今天讲解的内容如下:一、什么是单例模式【单例模式】,英文名称:Singleton Pattern,这个模式很简单,一个类型只需要一个实例,他...
    99+
    2021-08-10
    java教程 单例模式 Singleton
  • Java设计模式-单例模式
    单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供...
    99+
    2023-06-05
  • 【Java】设计模式之单例模式与工厂模式
    ✅作者简介:热爱后端语言的大学生,CSDN内容合伙人 ✨精品专栏:C++面向对象 🔥系列专栏:JavaSE精品总结 文章目录   前言1、设计模式概念及分类2、单例模式2...
    99+
    2023-10-04
    java 单例模式 设计模式
  • Java设计模式之单例模式
    目录什么是设计模式?单例模式是什么?单例模式设计的原则是什么?Java实现单例模式的5种方式?懒汉饿汉静态内部类双重校验锁DCL(Double Check Lock)枚举(num)总...
    99+
    2024-04-02
  • java 单例模式(懒汉式与饿汉式)
    java 单例模式单例模式是一种常用的软件设计模式。在它的可信结构中只包含一个被实例化单例的特殊类。通过单例设计模式可以把整系统中的一个类只有一个实例。单例设计模式又分为两种方式,懒汉式和饿汉式。 (1)懒汉式,就是只有当调用getInst...
    99+
    2023-05-31
    java 单例模式 ava
  • Java设计模式的单例模式实例分析
    本文小编为大家详细介绍“Java设计模式的单例模式实例分析”,内容详细,步骤清晰,细节处理妥当,希望这篇“Java设计模式的单例模式实例分析”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。什么是单例模式单例模式(S...
    99+
    2023-06-29
  • java单例模式的应用场景是什么
    Java单例模式的应用场景是在需要保证系统中只有一个实例对象存在的情况下使用。以下是几个常见的应用场景:1. 数据库连接对象:在一个...
    99+
    2023-10-11
    java
  • java设计模式中的单例模式简单介绍
    这篇文章主要介绍“java设计模式中的单例模式简单介绍”,在日常操作中,相信很多人在java设计模式中的单例模式简单介绍问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”java设计模式中的单例模式简单介绍”的疑...
    99+
    2023-06-02
  • Java单例模式怎么写
    本篇内容主要讲解“Java单例模式怎么写”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java单例模式怎么写”吧!这个模式是很有意思,而且比较简单,但是我还是要说因为它使用的是如此的广泛,如此的...
    99+
    2023-06-19
  • Java单例模式简单介绍
    一、概念单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例类的特殊类。通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。如果希望在系统中某个类的对象只能存在一个,...
    99+
    2023-05-31
    java 单例模式 ava
  • Java单例模式分析
    目录单例模式为什么要用单例单例的关键点几种写法懒汉式饿汉式静态内部类写法枚举单例容器实现单例参考总结单例模式 为什么要用单例 确保某个类只有一个对象,常用于访问数据库操作,服务的配置...
    99+
    2024-04-02
  • Java 实例解析单例模式
    目录单例模式的介绍优点缺点SynchronizedSynchronized示例Synchronized与非SynchronizedSingleton第一个示例第二个示例第三个示例第四...
    99+
    2024-04-02
  • Python实现单例模式的方式是什么
    本篇内容介绍了“Python实现单例模式的方式是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!单例模式(Singleton Patter...
    99+
    2023-07-04
  • JAVA的单例模式实例分析
    这篇文章主要介绍“JAVA的单例模式实例分析”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“JAVA的单例模式实例分析”文章能帮助大家解决问题。一、单例模式是什么?单例(Singleton)模式的定义...
    99+
    2023-06-29
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作