单例设计模式所有情况解析

news/2024/11/29 23:37:53/

单例设计模式解析

🥤概述:单例设计模式是一种创建型设计模式,旨在确保一个类只有一个实例,并提供一个全局访问点来访问该实例。

一、饿汉式实现

🎈介绍:饿汉式是一种单例设计模式的实现方式,其核心思想是在类加载时就创建实例对象并将其静态化,之后在每次获取实例时直接返回该静态对象。因此,饿汉式实现起来相对简单,在多线程环境下也比较安全,但是如果该实例一直没有被使用,会浪费一定的系统资源。

🔔代码实现:

public class Singleton {/*** 将自身实例化对象设置为一个属性,并用static修饰*/private static final Singleton INSTANCE = new Singleton();/*** 构造方法私有化,防止外部实例化对象*/private Singleton() {}/*** 静态方法返回该实例**/public static Singleton getInstance() {return INSTANCE;}/*** 测试单例是否唯一*/public static void main(String[] args) {System.out.println(singleton.enum_.Singleton.getInstance() == singleton.enum_.Singleton.getInstance());}
}

😊代码解析:

这是一个单例模式的经典实现,具体解释如下:

  1. 将自身实例化对象设置为一个属性,并用static修饰,这样在类加载时就会创建这个实例,并且该实例在整个应用程序生命周期中只会创建一次,从而保证了单例的唯一性。同时,由于该属性被声明为private,外部无法访问,从而保证了单例的封装性。
  2. 将构造器私有化,这样外部就无法通过new关键字创建实例,只能通过getInstance方法获取实例。这样可以控制单例的创建过程,并保证了单例的一致性。
  3. 提供一个静态方法getInstance(),该方法返回该实例。由于INSTANCE属性被声明为static,所以getInstance()方法也需要声明为static,这样可以在不创建实例的情况下访问该属性,并返回该实例。同时,由于该方法是静态方法,所以可以直接通过类名来调用,从而保证了单例的简单性和易用性。

总的来说,该实现方式简单明了,且具有较好的线程安全性和性能,是单例模式的经典实现方式之一。

二、懒汉式实现

🎈介绍:懒汉式是单例模式的一种实现方式,其特点是在需要时才创建实例,而不是在类加载时就创建实例。懒汉式通常有两种实现方式:加锁的和不加锁的

🔔代码实现:

public class Singleton {/*** 将自身实例化对象设置为一个属性,并用static修饰*/private static Singleton INSTANCE;/*** 构造方法私有化,防止外部实例化对象*/private Singleton() {}/*** 静态方法返回该实例**/public static Singleton getINSTANCE() {if (INSTANCE == null) {INSTANCE = new Singleton();}return INSTANCE;}/*** 测试单例是否唯一*/public static void main(String[] args) {System.out.println(singleton.enum_.Singleton.getInstance() == singleton.enum_.Singleton.getInstance());}
}

😊代码解析:

这是一个单例模式的懒汉式实现,具体解释如下:

  1. 将自身实例化对象设置为一个属性,并用static修饰,但不在定义时进行实例化,而是在getINSTANCE()方法中通过判断是否为null来决定是否需要实例化。这样在类加载时不会创建实例,而是在第一次使用时创建,从而实现了懒加载的效果,节省了系统资源。
  2. 构造器被私有化,从而避免了外部通过new关键字来创建实例,只能通过getINSTANCE()方法获取实例。这样可以控制单例的创建过程,并保证了单例的一致性。
  3. 提供了一个静态方法getINSTANCE(),该方法返回该实例。由于INSTANCE属性被声明为static,所以getINSTANCE()方法也需要声明为static,这样可以在不创建实例的情况下访问该属性,并返回该实例。同时,由于该方法是静态方法,所以可以直接通过类名来调用,从而保证了单例的简单性和易用性。

总的来说,该实现方式实现了懒加载的效果,但由于在多线程环境下存在竞争条件,可能会导致多个线程同时创建实例,从而影响单例的唯一性。因此,在多线程环境下需要进行线程安全处理。

➡️➡️这是懒汉式的单例实现方式,与饿汉式的主要区别是:懒汉式在第一次调用 getINSTANCE() 方法时才会实例化对象,而不是在类加载时就实例化对象。

  • 这种实现方式在单线程环境下没有问题,但在多线程环境下存在线程安全问题,可能会创建出多个实例。因为多个线程可能同时进入 if (INSTANCE == null) 的判断语句,导致多次实例化。
  • 为了解决这个问题,可以在 getINSTANCE() 方法上加锁,或者使用双重校验锁等方式来保证线程安全。

三、双重检查锁实现

🎈介绍:双重检查锁实现单例设计模式-懒汉式,是一种更加高效、线程安全的单例实现方式。它利用了volatile关键字和synchronized关键字的特性,避免了多线程环境下创建多个实例的问题。

🔔代码实现:

public class Singleton {/*** 将自身实例化对象设置为一个属性,并用static、volatile修饰*/private static volatile Singleton singleton;//锁private final static Object MONITOR = new Object();/*** 构造方法私有化,防止外部实例化对象*/private Singleton() {}/*** 静态方法获取实例*/public static Singleton getSingleton() {if (singleton == null) {synchronized (MONITOR) {if (singleton == null) {singleton = new Singleton();}return singleton;}}return singleton;}/*** 测试单例是否唯一*/public static void main(String[] args) {System.out.println(singleton.enum_.Singleton.getInstance() == singleton.enum_.Singleton.getInstance());}
}

😊代码解析:

这是一个单例模式的懒汉式-双重检查锁实现,具体解释如下:

  1. 首先,该类为单例模式的实现类,通过保证一个类仅有一个实例来保证单例模式的实现。
  2. INSTANCE属性使用了volatile关键字进行修饰。volatile关键字的作用是保证可见性和禁止指令重排序。这样可以确保当一个线程修改了该属性的值之后,其他线程能够立即看到该修改,并且该属性在初始化时不会出现指令重排序的情况,从而保证了线程安全性。
  3. MONITOR对象被定义为一个final static的对象,并在静态代码块中进行初始化。该对象用于实现懒加载时的同步锁。由于MONITOR对象是final static的,因此只会在类被加载时初始化一次,并且该对象是线程安全的,因此可以放心使用。
  4. 构造方法被私有化,从而禁止外部通过new关键字来实例化该类的对象。
  5. getSingleton()方法为静态方法,用于获取该类的实例对象。在该方法内部,首先检查singleton对象是否为空,如果为空,则进入同步块,再次检查singleton对象是否为空,如果为空,则实例化一个Singleton对象并赋值给singleton,否则直接返回singleton对象。通过双重检查锁机制,保证了线程安全性和懒加载的效果。同时,为了避免多个线程在同一时间同时进入同步块,导致性能问题,采用了MONITOR对象来作为同步锁。

总的来说,该实现方式采用了双重检查锁机制来保证线程安全性和懒加载效果,同时也兼顾了性能问题。但是,需要注意的是,该方式并不是完美的,可能存在一些潜在的问题,如可能会受到指令重排序等问题的影响,需要开发者根据具体情况进行实际应用。

四、静态内部类实现

🎈介绍:

  • 静态内部类是一种嵌套在外部类中的类,它的特点是只有在第一次使用时才会被加载和初始化,且它的初始化过程是线程安全的。基于这个特点,可以使用静态内部类来实现单例模式。
  • 具体实现方法是将单例对象的实例化放到静态内部类中,在外部类中提供一个公共的静态方法,用于获取该单例对象的实例。由于静态内部类只有在第一次使用时才会被加载,所以这种方法可以实现懒加载,同时也保证了线程安全性和单例唯一性。

🔔代码实现:

public class Singleton {/*** 构造器私有化,防止外部实例化*/private Singleton() {}/*** 返回单例实例的静态方法*/public static Singleton getInstance() {return SingletonHolder.INSTANCE;}/*** 写一个静态内部类,里面实例化外部类*/private static class SingletonHolder {private static final Singleton INSTANCE = new Singleton();}/*** 测试单例是否唯一*/public static void main(String[] args) {System.out.println(singleton.enum_.Singleton.getInstance() == singleton.enum_.Singleton.getInstance());}
}

😊代码解析:

这是静态内部类实现单例设计模式的经典写法。具体解释如下:

  1. 外部类 Singleton 的构造方法被私有化,这样外部无法通过 new 关键字创建实例,只能通过类的静态方法 getInstance() 获取单例对象。
  2. 内部类 SingletonHolder 被声明为私有的静态类,只有 Singleton 类能够访问它,从而保证了单例的封装性和安全性。
  3. 内部类 SingletonHolder 包含一个静态常量 INSTANCE,它被初始化为一个 Singleton 类的实例,利用类加载器保证了单例的线程安全性。
  4. 对外提供公共的静态方法 getInstance(),该方法返回 SingletonHolder.INSTANCE,实现了懒加载和线程安全。

总的来说,这种实现方式既保证了线程安全性,又实现了懒加载,同时代码量也比较简洁,是静态内部类实现单例设计模式的经典写法。

五、枚举实现

🎈介绍:

  • 使用枚举类实现单例模式是一种简单、安全、易于实现的方式,因为枚举类本身就保证了单例的唯一性,同时也避免了其他单例模式中可能遇到的反射攻击和序列化问题。

  • 在使用枚举类实现单例模式时,只需要声明一个枚举类型,并在其中定义一个实例,这个实例就是该单例模式的唯一实例,而且该实例是由枚举类在加载时自动创建的,因此无需考虑线程安全等问题。同时,由于枚举类是final类型的,不允许被继承,也不允许通过反射来创建实例,因此可以很好地避免反射攻击和序列化问题。

🔔代码实现:

public class Singleton {/*** 构造器私有化,防止外部实例化*/private Singleton() {}/*** 返回单例实例的静态方法*/public static Singleton getInstance() {return SingletonHolder.INSTANT.instance;}/*** 静态内部枚举类,实例化单例对象*/private enum SingletonHolder {INSTANT;private final Singleton instance;/*** 实例化单例对象*/SingletonHolder() {instance = new Singleton();}}/*** 测试单例是否唯一*/public static void main(String[] args) {System.out.println(Singleton.getInstance() == Singleton.getInstance());}
}

😊代码解析:

这段代码是通过枚举类实现单例模式。具体解释如下:

  1. 构造方法私有化,这样外部无法通过new关键字来创建该类的实例。
  2. 定义一个静态的getInstance()方法,返回单例实例,通过访问SingletonHolder.INSTANT.instance来获取。
  3. 内部定义一个枚举类SingletonHolder,在该枚举类中定义一个单例的实例instance,并在枚举类的构造方法中实例化该实例。由于枚举类中的成员变量都是static final类型的,因此保证了单例实例的唯一性和不可变性。
  4. 在getInstance()方法中,直接返回SingletonHolder.INSTANT.instance即可获取单例实例。

总的来说,枚举类实现单例模式的代码比较简洁,而且线程安全性和序列化安全性都能得到保障,因此在实际开发中也比较常用。


http://www.ppmy.cn/news/47747.html

相关文章

【LeetCode: 673. 最长递增子序列的个数 | 动态规划】

🚀 算法题 🚀 🌲 算法刷题专栏 | 面试必备算法 | 面试高频算法 🍀 🌲 越难的东西,越要努力坚持,因为它具有很高的价值,算法就是这样✨ 🌲 作者简介:硕风和炜,…

《面试1v1》HashMap

没有人比中国人更懂 HashMap 我是 javapub,一名 Markdown 程序员从👨‍💻,八股文种子选手。 面试官:HashMap 是Java程序员用得最频繁的集合之一,可以给我简单介绍一下它的内部实现机制吗? 候选人: Hash…

华为OD机试真题(Java),数组合并(100%通过+复盘思路)

一、题目描述 现在有多组整数数组,需要将他们合并成一个新的数组。 合并规则从每个数组里按顺序取出固定长度的内容,合并到新的数组,取完的内容会删除掉。 如果改行不足固定长度,或者已经为空,则直接取出剩余部分的内…

Axios请求(对ajax的二次封装)——Axios API、Axios实例、请求配置、Axios响应结构

axios起步——介绍和使用基本用例post请求 场景复现核心干货axios APIaxios(config)axios(url[,config])请求方式别名 axios实例创建一个axios实例axios.create([config])实例方法 axios请求配置axios响应结构 场景复现 最近学习与前端相关的小程序时,接触了异步请…

@爱打游戏的你,当游戏测试是什么感觉?

爱打游戏的你,当游戏测试是一种什么感觉? 去年《宝可梦朱紫》大火的那段时间,想必各位爱好游戏的友友们都刷到过这样的图吧: (量子纠缠) (天怎么黑了) (弹簧巨怪&#x…

4-数据结构

数据结构(data structure) 1. 简介 数据结构是在计算机中组织与存储数据的方式 如果想要表示“一排数字”,自然想到使用「数组」数据结构 数组的存储方式可以表示数字的相邻关系、顺序关系,但至于其中存储的是整数int&#xff0c…

Python并发编程在爬虫中的应用

并发编程在爬虫中的应用 本文将为大家介绍 Python 中的多线程、多进程和异步编程,并且以爬取“360图片”网站的图片并保存到本地为例,为大家分别展示使用单线程、多线程和异步 I/O 编程的爬虫程序有什么区别,同时也对它们的执行效率进行简单…

非计算机专业如何转行成为程序员?我用亲身经历教你用这三种方法

哈喽大家好啊!我想分享一下,非计算机专业的学生如何转行成为程序员。首先,我先介绍一下我的情况。我是18年毕业的,大学学的专业是土木工程,与计算机一点关系都没有。但是在大学时,我对程序员比较感兴趣。本…