iis服务器助手广告广告
返回顶部
首页 > 资讯 > 精选 >Java设计模式的单例模式如何实现
  • 364
分享到

Java设计模式的单例模式如何实现

2023-06-29 16:06:34 364人浏览 八月长安
摘要

这篇文章主要介绍了Java设计模式的单例模式如何实现的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Java设计模式的单例模式如何实现文章都会有所收获,下面我们一起来看看吧。单例模式单例模式顾名思义就是单一的实例

这篇文章主要介绍了Java设计模式的单例模式如何实现的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Java设计模式的单例模式如何实现文章都会有所收获,下面我们一起来看看吧。

单例模式

单例模式顾名思义就是单一的实例,涉及到一个单一的类,该类负责创建自己的对象,同时确保只有一个对象被创建,并且提供一种可以访问这个对象的方式,可以直接访问,不需要实例化该类的对象。

单例模式的特点:

单例类只能有一个实例

这个实例必须由单例类自己创建

单例类需要提供给外界访问这个实例

单例模式的作用:

单例模式主要为了保证在Java应用程序中,一个类只有一个实例存在。

1.单例模式的结构

单例模式主要有以下角色:

  • 单例类

只能创建一个实例的类

  • 访问类

测试类,就是使用单例类的类

2.单例模式的实现

2.1饿汉式

饿汉式:类加载时创建该单实例类对象

饿汉式-方式1 静态成员变量

创建 饿汉式静态成员变量 单例类

public class Demo1 {         private Demo1(){}        private static Demo1 instance = new Demo1();        public static Demo1 getInstance(){        return instance;    }}

创建 饿汉式静态成员变量 测试类(访问类)

public class Test1 {    public static void main(String[] args) {      //创建demo1类的对象 这个时候就无法通过new创建了,因为demo1的构造方法是私有的        Demo1 instance = Demo1.getInstance();        Demo1 instance1 = Demo1.getInstance();        //判断两个对象是否是同一个        System.out.println(instance == instance1);            }}

输出true 表明是同一个对象,指向同一块内存地址,这样我们就保证了Demo1单例类只有一个对象被创建

饿汉式-方式2 静态代码块

创建 饿汉式静态代码块 单例类

public class Demo2 {    //饿汉式单例类  静态代码块        private Demo2(){}        private static  Demo2 instance;        static {        instance = new Demo2();    }        public static Demo2  getInstance(){        return instance;    }}

创建 饿汉式静态代码块 测试类

public class Test2 {    public static void main(String[] args) {        Demo2 instance = Demo2.getInstance();        Demo2 instance1 = Demo2.getInstance();        System.out.println(instance == instance1);    }}

输出true 表明是同一个对象,指向同一块内存地址,这样我们就保证了Demo2单例类只有一个对象被创建

饿汉式-方式3(枚举方式)

枚举类实现单例模式是十分推荐的一种单例实现模式,由于枚举类型是线程安全的,并且只会加载一次,这是十分符合单例模式的特点的,枚举的写法很简单,而且枚举方式是所有单例实现中唯一一个不会被破环的单例实现模式

单例类

//枚举方式创建单例public enum Singleton {     INSTANCE;}

测试类

public class Test1 {    public static void main(String[] args) {    Singleton instance = Singleton.INSTANCE;    Singleton instance1 = Singleton.INSTANCE;        System.out.println(instance == instance1);        //输出 true            }}

注意:

由于枚举方式是饿汉式,因此根据饿汉式的特点,枚举方式也会造成内存浪费,但是在不考虑内存问题下,枚举方式是首选,毕竟实现最简单了

2.2懒汉式

懒汉式:类加载时不会创建该单实例对象,首次使用该对象时才会创建

懒汉式-方式1 (线程不安全)

public class Demo3 {        private Demo3(){}        private static  Demo3 instance;        public static Demo3 getInstance(){        //在首次使用该对象时创建,因此instance赋值也就是对象创建 就是在外界获取该单例类的方法getInstance()中创建        instance = new Demo3();        return instance;    }}
public class Test3 {    public static void main(String[] args) {        Demo3 instance = Demo3.getInstance();        Demo3 instance1 = Demo3.getInstance();        //判断两个对象是否是同一个        System.out.println(instance == instance1);    }}

输出结果为false,表明我们创建懒汉式单例失败了。是因为我们在调用getInstance()时每次调用都会new一个实例对象,那么也就必然不可能相等了。

   // 如果instance为null,表明还没有创建该类的对象,那么就进行创建        if(instance == null){          instance = new Demo3();        }        //如果instance不为null,表明已经创建过该类的对象,根据单例类只能创建一个对象的特点,因此         //我们直接返回instance        return instance;    }

注意:

我们在测试是只是单线程,但是在实际应用中必须要考虑到多线程的问题。我们假设一种情况,线程1进入if判断然后还没来得及创建instance,这个时候线程1失去了cpu的执行权变为阻塞状态,线程2获取cpu执行权,然后进行if判断此时instance还是null,因此线程2为instance赋值创建了该单例对象,那么等到线程1再次获取cpu执行权,也进行了instance赋值创建了该单例对象,单例模式被破坏。

懒汉式-方式2 (线程安全)

我们可以通过加synchronized同步的方式保证单例模式在多线程下依旧有效

 public static synchronized Demo3 getInstance(){        //在首次使用该对象时创建,因此instance赋值也就是对象创建 就是在外界获取该单例类的方法getInstance()中创建        // 如果instance为null,表明还没有创建该类的对象,那么就进行创建        if(instance == null){          instance = new Demo3();        }        //如果instance不为null,表明已经创建过该类的对象,根据单例类只能创建一个对象的特点,因此我们直接返回instance        return instance;    }

注意:

虽然保证了线程安全问题,但是在getInstance()方法上添加了synchronized关键字,导致该方法执行效率很低(这是加锁的一个常见问题)。其实我们可以很容易发现,我们只是在判断instance时需要解决多线程的安全问题,而没必要在getInstance()上加锁

懒汉式-方式3(双重检查锁)

对于getInstance()方法来说,绝大部分的操作都是读操作,读操作是线程安全的,没必要让每个线程必须持有锁才能调用该方法,我们可以调整加锁的时机。

public class Demo4 {        private Demo4(){}        private static Demo4 instance;        public static  Demo4 getInstance(){        // (第一次判断)如果instance为null,表明还没有创建该类的对象,那么就进行创建        if(instance == null){            synchronized (Demo4.class){                //第二次判断 如果instance不为null                if(instance == null){                    instance = new Demo4();                }            }        }        //如果instance不为null,表明已经创建过该单例类的对象,不需要抢占锁,直接返回        return instance;    }}

双重检查锁模式完美的解决了单例、性能、线程安全问题,但是只是这样还是有问题的…

JVM在创建对象时会进行优化和指令重排,在多线程下可能会发生空指针异常的问题,可以使用volatile关键字,volatile可以保证可见性和有序性。

 private static volatile Demo4  instance;

Java设计模式的单例模式如何实现

如果发生指令重排 2 和 3 的步骤颠倒,那么instance会指向一块虚无的内存(也有可能是有数据的一块内存)

完整代码

public class Demo4 {        private Demo4(){}        private static volatile Demo4  instance;            public static  Demo4 getInstance(){        // (第一次判断)如果instance为null,表明还没有创建该类的对象,那么就进行创建        if(instance == null){            synchronized (Demo4.class){                //第二次判断 如果instance不为null                if(instance == null){                    instance = new Demo4();                }            }        }        //如果instance不为null,表明已经创建过该单例类的对象,不需要抢占锁,直接返回        return instance;    }}

懒汉式-4 (静态内部类)

静态内部类单例模式中实例由内部类创建,由于JVM在加载外部类的过程中,是不会加载静态内部类的,只有内部类的属性/方法被调用时才会被加载,并初始化其静态属性。静态属性由于被final修饰,保证只被实例化一次,并且严格保证实例化顺序。

创建单例类

public class Singleton {    private Singleton(){}        private static  class SingletonHolder{        //在静态内部类中创建外部类的对象        private static final Singleton INSTANCE = new Singleton();    }    public static Singleton getInstance(){        return SingletonHolder.INSTANCE;    }}

创建测试类

public class Test4 {    public static void main(String[] args) {        Singleton instance = Singleton.getInstance();        Singleton instance1 = Singleton.getInstance();        //判断两个对象是否是同一个        System.out.println(instance == instance1);    }}

注意:

第一次加载Singleton类时不会去初始化INSTANCE,只有在调用getInstance()方法时,JVM加载SingletonHolder并初始化INSTANCE,这样可以保证线程安全,并且Singleton类的唯一性

静态内部类单例模式是一种开源项目比较常用的单例模式,在没有任何加锁的情况下保证多线程的安全,并且没有任何性能和空间上的浪费

3.单例模式的破坏

单例模式最重要的一个特点就是只能创建一个实例对象,那么如果能使单例类能创建多个就破坏了单例模式(除了枚举方式)破坏单例模式的方式有两种:

3.1序列化和反序列化

从以上创建单例模式的方式中任选一种(除枚举方式),例如静态内部类方式

//记得要实现Serializable序列化接口public class Singleton implements Serializable {    private Singleton(){}        private static  class SingletonHolder{        //在静态内部类中创建外部类的对象        private static final Singleton INSTANCE = new Singleton();    }    public static Singleton getInstance(){        return SingletonHolder.INSTANCE;    }}

测试类

public class Test1 {    public static void main(String[] args) throws ioException {              writeObjectToFile();    }        public static void writeObjectToFile() throws IOException {        //1.获取singleton对象        Singleton instance = Singleton.getInstance();        //2.创建对象输出流对象        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("d:\\1.txt"));        //3.写对象        oos.writeObject(instance);        //4.释放资源        oos.close();    }}

在d盘根目录下出现一个文件1.txt由于数据是序列化后的 咱也看不懂

然后我们从这个文件中读取instance对象

public static void main(String[] args) throws Exception {             // writeObjectToFile();        readObjectFromFile();        readObjectFromFile();    }        public static  void readObjectFromFile() throws Exception {        //1.创建对象输入流对象        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:\\1.txt"));        //2.读对象        Singleton instance = (Singleton) ois.readObject();        System.out.println(instance);        //3.释放资源        ois.close();    }

输出结果不相同,结论为:序列化破坏了单例模式,两次读的对象不一样了

com.xue.demo01.Singleton@2328c243
com.xue.demo01.Singleton@bebdb06

解决方案

在singleton中添加readResolve方法

      public Object readResolve(){        return SingletonHolder.INSTANCE;    }

重新进行写和读,发现两次读的结果是相同的,解决了序列化破坏单例模式的问题

为什么在singleton单例类中添加readResolve方法就可以解决序列化破坏单例的问题呢,我们在ObjectInputStream源码中在readOrdinaryObject方法中

 private Object readOrdinaryObject(boolean unshared)        throws IOException{//代码段   Object obj;        try {            //isInstantiable如果一个实现序列化的类在运行时被实例化就返回true            //desc.newInstance()会通过反射调用无参构造创建一个新的对象            obj = desc.isInstantiable() ? desc.newInstance() : null;        } catch (Exception ex) {            throw (IOException) new InvalidClassException(                desc.forClass().getName(),                "unable to create instance").initCause(ex);        }   //代码段   if (obj != null &&            handles.lookupException(passhandle) == null &&       //hasReadResolveMethod 如果实现序列化接口的类中定义了readResolve方法就返回true            desc.hasReadResolveMethod())        {       //通过反射的方式调用被反序列化类的readResolve方法            Object rep = desc.invokeReadResolve(obj);            if (unshared && rep.getClass().isArray()) {                rep = cloneArray(rep);            }           //代码段 }
3.2反射

从以上创建单例模式的方式中任选一种(除枚举方式),例如静态内部类方式

测试类

public class Test1 {    public static void main(String[] args) throws Exception {        //1.获取Singleton的字节码对象        Class<Singleton> singletonClass = Singleton.class;        //2.获取无参构造方法对象        Constructor cons = singletonClass.getDeclaredConstructor();        //3.取消访问检查        cons.setAccessible(true);        //4.反射创建对象        Singleton instance1 = (Singleton) cons.newInstance();        Singleton instance2 = (Singleton) cons.newInstance();        System.out.println(instance1 == instance2);        //输出false 说明反射破坏了单例模式    }}

解决方案:

public class Singleton  {    //static是为了都能访问    private static boolean flag = false;    private Singleton() {        //加上同步锁,防止多线程并发问题        synchronized (Singleton.class) {            //判断flag是否为true,如果为true说明不是第一次创建,抛异常            if (flag) {                throw new RuntimeException("不能创建多个对象");            }            //flag的值置为true            flag = true;        }    }        private static  class SingletonHolder{        //在静态内部类中创建外部类的对象        private static final Singleton INSTANCE = new Singleton();    }    public static Singleton getInstance(){        return SingletonHolder.INSTANCE;    }}

关于“Java设计模式的单例模式如何实现”这篇文章的内容就介绍到这里,感谢各位的阅读!相信大家对“Java设计模式的单例模式如何实现”知识都有一定的了解,大家如果还想学习更多知识,欢迎关注编程网精选频道。

--结束END--

本文标题: Java设计模式的单例模式如何实现

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

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

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Java设计模式的单例模式如何实现
    这篇文章主要介绍了Java设计模式的单例模式如何实现的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Java设计模式的单例模式如何实现文章都会有所收获,下面我们一起来看看吧。单例模式单例模式顾名思义就是单一的实例...
    99+
    2023-06-29
  • java设计模式中如何实现单例模式
    这篇文章将为大家详细讲解有关java设计模式中如何实现单例模式,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。下面是一个简单的小实例://简单懒汉式 public class ...
    99+
    2023-05-30
    java
  • Java设计模式-单例模式
    单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供...
    99+
    2023-06-05
  • Java设计模式的单例模式实例分析
    本文小编为大家详细介绍“Java设计模式的单例模式实例分析”,内容详细,步骤清晰,细节处理妥当,希望这篇“Java设计模式的单例模式实例分析”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。什么是单例模式单例模式(S...
    99+
    2023-06-29
  • Java设计模式之单例模式
    目录什么是设计模式?单例模式是什么?单例模式设计的原则是什么?Java实现单例模式的5种方式?懒汉饿汉静态内部类双重校验锁DCL(Double Check Lock)枚举(num)总...
    99+
    2022-11-12
  • java设计模式之怎么实现单例模式
    这篇文章主要介绍了java设计模式之怎么实现单例模式的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇java设计模式之怎么实现单例模式文章都会有所收获,下面我们一起来看看吧。单元素的枚举类型经常成为实现 Sing...
    99+
    2023-07-04
  • 如何理解Java设计模式的单例模式
    这篇文章主要讲解了“如何理解Java设计模式的单例模式”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“如何理解Java设计模式的单例模式”吧!一、什么是单例模式单例模式是一种常用的软件设计模式...
    99+
    2023-06-25
  • java设计模式-单例模式实现方法详解
    目录饿汉式静态变量静态代码块懒汉式线程不安全线程安全双重检查静态内部类总结单例模式,属于创建类型的一种常用的软件设计模式。通过单例模式的方法创建的类在当前进程中只有一个实例(根据需要...
    99+
    2022-11-12
  • 如何实现单例设计模式
    这篇文章主要讲解了“如何实现单例设计模式”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“如何实现单例设计模式”吧! 单例模式单例模式(Singleton Pattern)是 Java...
    99+
    2023-06-15
  • Android设计模式之单例模式实例
    目录一、概念1.1 单例类1.2 优缺点1.2.1 优点1.2.2 缺点二、创建单例模式的方法2.1 饿汉式2.2 懒汉式2.2.1 懒汉式(非线程安全)2.2.2 懒汉式(线程安全...
    99+
    2023-05-16
    Android单例模式 Android设计模式单例模式
  • java设计模式之单例模式解析
    单例模式是最简单但同时也是很重要的一种设计模式,优点有以下几个方面:当内存占用特别大的类需要频繁地创建销毁时,单例模式可以节省内存和提高性能,例如myBatis里面的sessionFactory当需要对文件做单一读写时,例如同一时间只能同时...
    99+
    2023-05-31
    java 设计模式 单例模式
  • Java设计模式之单例模式简介
    目录一、饿汉式(静态常量)二、饿汉式(静态代码块)三、懒汉式(线程不安全)四、懒汉式(线程安全,同步方法)五、懒汉式(线程不安全,同步代码块)六、双重检查( DCL )七、静态内部类...
    99+
    2022-11-12
  • JAVA设计模式之单例模式详解
    目录前言一、单例模式是什么?二、懒汉式单例三、饿汉式单例四、双重校验锁总结前言 在之前的文章里已经介绍了设计模式以及设计原则的概念,接下来我们从单例模式入手深入学习几种常用的JAVA...
    99+
    2022-11-13
  • 【Java】设计模式之单例模式与工厂模式
    ✅作者简介:热爱后端语言的大学生,CSDN内容合伙人 ✨精品专栏:C++面向对象 🔥系列专栏:JavaSE精品总结 文章目录   前言1、设计模式概念及分类2、单例模式2...
    99+
    2023-10-04
    java 单例模式 设计模式
  • 【python 设计模式】单例模式
    单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场。 比如,某个服务器程序的配置信息存放在一个文件中...
    99+
    2023-01-31
    模式 python
  • Python设计模式——单例模式
    一、单例模式理论单例模式:    保证某一个类只有一个实例,而且在全局只有一个访问点优点:    1、由于单例模式要求在全局内只有一个实例,因而可以节省比较多的内存空间    2、全局只有一个接入点,可以更好地进行数据同步控制,避免多重占用...
    99+
    2023-01-31
    模式 Python
  • java设计模式中的单例模式简单介绍
    这篇文章主要介绍“java设计模式中的单例模式简单介绍”,在日常操作中,相信很多人在java设计模式中的单例模式简单介绍问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”java设计模式中的单例模式简单介绍”的疑...
    99+
    2023-06-02
  • Java设计模式之单例模式示例详解
    目录0.概述1.饿汉式1.1 饿汉式单例实现1.2 破坏单例的几种情况1.3 预防单例的破坏2.枚举饿汉式2.1 枚举单例实现2.2 破坏单例3.懒汉式4.双检锁懒汉式5.内部类懒汉...
    99+
    2022-11-12
  • JAVA设计模式---单例模式你知道吗
    目录单例模式的介绍单例模式实现的八种方式饿汉式静态常量静态代码块懒汉式线程不安全的写法线程安全,同步锁—效率低,不推荐线程安全,同步代码块—无法解决线程安全问题,不推荐双重检查—解决...
    99+
    2022-11-12
  • Java创建型设计模式之单例模式
    目录介绍优点实现饿汉式懒汉式静态内部类枚举介绍 单例模式是一种创建型设计模式,其主要特点包括: 只有一个实例:单例模式确保系统中只有一个实例对象存在,所有对该对象的访问都是对同一个对...
    99+
    2023-05-18
    Java单例模式 Java创建型设计模式
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作