详解Java设计模式之单例模式(Singleton Pattern)

简介: 详解Java设计模式之单例模式(Singleton Pattern)

大家在使用Windows的时候不知道有没有注意过一个细节,在我们使用任务管理器的时候没有办法同时打开两个,也就是说,它在整个系统中只有唯一的一个实例


b3793c3c8ad8496eacb02dff57afa394.png


对于系统中的某些类来说,只有一个实例很重要 !


例如:


一些设备管理器常常设计为单例模式,比如一个电脑有两台打印机,在输出的时候就要处理不能两台打印机打印同一个文件。

一个班级只有一个班主任。

在 Windows中就只能打开一个任务管理器(如图上图所示)。


如果不使用机制对窗口对象进行唯一化,将弹出多个窗口,如果这些窗口显示的内容完全一致,则是重复对象,浪费内存资源,如果这些窗口显示的内容不一致,则意味着在某一瞬间系统有多个状态,与实际不符,也会给用户带来误解,不知道哪一个才是真实的状态。因此,有时确保系统中某个对象的唯一性即一个类只能有一个实例非常重要。


模式定义

单例模式是一种对象创建型模式。

  • 单例模式确保某一个类只有一个实例,而且自行实例化并向整个例,这个类称为单例类,它提供全局访问的方法。

单例模式的要点有三个:

  1. 有一个实例。
  2. 它必须自行创建这个实例。
  3. 它必须自行向整个系统提供这个实例。

模式结构

23919972255f4dbeab5cccf6544331a9.png

单例模式只包含一个 Singleton(单例角色)类,在单例类的内部实现只生成一个实例,同时它提供一个静态的getInstance()工厂方法,让客户可以使用它的唯一实例。


为了防止在外部对其实例化,将其构造函数设计为私有,在单例类内部定义了一个 Singleton类型的静态对象,作为外部共享的唯一实例。


一般情况下单例模式的代码实现如下 👇

package singleton;
/**
 * @author mengzhichao
 * @create 2021-11-28-15:47
 */
public class Singleton {
    private static Singleton instance=null; //静态私有成员变量
    //私有构造函数
    private Singleton(){
    }
    //静态共有工厂方法,返回唯一实例
    public static Singleton getInstance(){
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

为了测试单例类所创建对象的唯一性,可以编写如下客户端测试代码

package singleton;
/**
 * @author mengzhichao
 * @create 2021-11-28-15:51
 */
public class Client {
    public static void main(String[] args) {
        Singleton s1=Singleton.getInstance();
        Singleton s2=Singleton.getInstance();
        System.out.println(s1==s2);
    }
}

编译代码并运行,输出结果为:true

  • 说明两次调用getInstance()时所获取的对象是同一实例对象,且无法在外部不Singleton进行实例化,因而确保系统中只有唯一的一个Singleton对象。

55aba35184b9412d8a26d3854f57e3c1.png

在单例模式的实现过程中,需要注意以下几点:


  1. 单例类的构造函数为私有。
  2. 提供一个自身的静态私有成员变量。
  3. 提供一个公有的静态工厂方法。
  4. getInstance()方法中需要使用同步锁synchronized (Singleton.class)防止多线程同时进入造成 instance 被多次实例化。

应用场景

场景一

  • 系统只需要一个实例对象,如系统要求提供一个唯一的序列号生成器,或者需要考虑资源消耗太大而只允许创建一个对象。

场景二

  • 客户调用类的单个实例只允许使用一个公共访问点,除了该公共访问点,不能通过其他途径访问该实例。

使用单例模式有一个必要条件: 在一个系统中要求一个类只有一个实例时才应当使用单例模式。反过来,如果一个类可以有几个实例共存,就需要对单例模式进行改进,使之成为多例模式

不适用场景

不要使用单例模式存取全局变量,因为这违背了单例模式的用意,最好将全局变量放到对应类的静态成员中。

不要将数据库连接做成单例,因为一个系统可能会与数据库有多个连接,并且在有连接池的情况下,应当尽可能及时释放连接。单例模式由于使用静态成员存储类的实例,所以可能会造成资源无法及时释放,带来一些问题。


模式优缺点

主要优点在于提供了对唯一实例的受控访问并可以节约系统资源。


  • 在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。
  • 避免对资源的多重占用(比如写文件操作)


其主要缺点在于因为缺少抽象层而难以扩展,且单例类职责过重。


  • 没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。


模式案例

一.JDK中单例模式的应用实例

  • java. lang.Runtime类。

在每一个Java应用程序里面,都有唯一的一个Runtime对象,通过这个Runtime对象,应用程序可以与其运行环境发生相互作用。在JDK中,Runtime类的源代码片段如下:

public class Runtime {
    private static Runtime currentRuntime = new Runtime();
    public static Runtime getRuntime() {
        return currentRuntime;
    }
    private Runtime() {}
    ...
}

二.Spring中的应用实例

当我们试图要从 Spring容器中获取某个类的实例时,默认情况下Spring会通过单例模式进行创建,也就是在Spring 的 bean 工厂中这个bean的实例只有一个,代码如下:

<bean id="date" class="java.util.Date" scope="singleton"/>

三.单例模式案例之身份证号码(练习)


在现实生活中,居民身份证号码具有唯一性,同一个人不允许有多个身份证号码,第一次申请身份证时将给居民分配一个身份证号码,如果之后因为遗失等原因补办时,还是使用原来的身份证号码,不会产生新的号码。

现使用单例模式模拟该场景。


创建单例类 IdentityCardNo(身份证号码类)


在单例类IdentityCardNo中除了静态工厂方法外,还可以包含一些其他业务方法﹐如本例中的setIdentityCardNo()方法和 getIdentityCardNo()方法。在工厂方法 getInstance()中,先判断对象是否存在,如果不存在则实例化一个新的对象﹐然后返回;如果存在则直接返回已经存在的对象。

package singletontest;
/**
 * @author mengzhichao
 * @create 2021-11-28-16:18
 */
public class IdentityCardNo {
    private static IdentityCardNo instance = null;
    private String no;
    private IdentityCardNo() {
    }
    public static IdentityCardNo getInstance(){
        if (instance==null){
            System.out.println("第一次办理身份证,分配新号码");
            instance=new IdentityCardNo();
            instance.setIdentityCardNo("No410111111122222222");
        }else {
            System.out.println("重复办理身份证,获取旧号码!");
        }
        return instance;
    }
    private void setIdentityCardNo(String no){
        this.no=no;
    }
    public String getIdentityCardNo(){
        return this.no;
    }
}
  1. 测试类 Client

在客户端测试代码中定义了两个IdentityCardNo类型的对象,通过调用两次静态工厂方法 getInstance()获取对象,然后判断它们是否相等﹔再通过业务方法 getIdentityCardNo()获取封装在对象中的属性号码no值,判断两次no值是否相同。

package singletontest;
/**
 * @author mengzhichao
 * @create 2021-11-28-16:26
 */
public class Client {
    public static void main(String[] args) {
        IdentityCardNo no1,no2;
        no1=IdentityCardNo.getInstance();
        no2=IdentityCardNo.getInstance();
        System.out.println("身份证号码是否一致:" + (no1==no2));
        String str1,str2;
        str1=no1.getIdentityCardNo();
        str2=no2.getIdentityCardNo();
        System.out.println("第一次号码:"+str1);
        System.out.println("第二次号码:"+str2);
        System.out.println("内容是否相同:"+str1.equalsIgnoreCase(str2));
        System.out.println("对象是否相同:"+ (str1==str2));
    }
}
  1. 运行结果

82b3a81d9dde401680207f4f4f5c9378.png

从结果可以看出,两次创建的IdentityCardNo对象内存地址相同,是同一个对象,封装在其中的号码no属性不仅值相等,其内存地址也一致,是同一个成员属性。


学习更多设计模式还请访问:https://blog.csdn.net/weixin_45692705?spm=1011.2124.3001.5343


相关文章
|
23天前
|
设计模式 消息中间件 搜索推荐
Java 设计模式——观察者模式:从优衣库不使用新疆棉事件看系统的动态响应
【11月更文挑战第17天】观察者模式是一种行为设计模式,定义了一对多的依赖关系,使多个观察者对象能直接监听并响应某一主题对象的状态变化。本文介绍了观察者模式的基本概念、商业系统中的应用实例,如优衣库事件中各相关方的动态响应,以及模式的优势和实际系统设计中的应用建议,包括事件驱动架构和消息队列的使用。
|
1月前
|
设计模式 Java 数据库连接
Java编程中的设计模式:单例模式的深度剖析
【10月更文挑战第41天】本文深入探讨了Java中广泛使用的单例设计模式,旨在通过简明扼要的语言和实际示例,帮助读者理解其核心原理和应用。文章将介绍单例模式的重要性、实现方式以及在实际应用中如何优雅地处理多线程问题。
38 4
|
1月前
|
设计模式 安全 Java
Java编程中的单例模式:理解与实践
【10月更文挑战第31天】在Java的世界里,单例模式是一种优雅的解决方案,它确保一个类只有一个实例,并提供一个全局访问点。本文将深入探讨单例模式的实现方式、使用场景及其优缺点,同时提供代码示例以加深理解。无论你是Java新手还是有经验的开发者,掌握单例模式都将是你技能库中的宝贵财富。
49 2
|
1月前
|
设计模式 安全 Java
Java编程中的单例模式深入解析
【10月更文挑战第31天】在编程世界中,设计模式就像是建筑中的蓝图,它们定义了解决常见问题的最佳实践。本文将通过浅显易懂的语言带你深入了解Java中广泛应用的单例模式,并展示如何实现它。
|
1月前
|
设计模式 JavaScript Java
Java设计模式:建造者模式详解
建造者模式是一种创建型设计模式,通过将复杂对象的构建过程与表示分离,使得相同的构建过程可以创建不同的表示。本文详细介绍了建造者模式的原理、背景、应用场景及实际Demo,帮助读者更好地理解和应用这一模式。
|
1月前
|
设计模式 安全 Java
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
|
3月前
|
设计模式 数据库连接 PHP
PHP中的设计模式:提升代码的可维护性与扩展性在软件开发过程中,设计模式是开发者们经常用到的工具之一。它们提供了经过验证的解决方案,可以帮助我们解决常见的软件设计问题。本文将介绍PHP中常用的设计模式,以及如何利用这些模式来提高代码的可维护性和扩展性。我们将从基础的设计模式入手,逐步深入到更复杂的应用场景。通过实际案例分析,读者可以更好地理解如何在PHP开发中应用这些设计模式,从而写出更加高效、灵活和易于维护的代码。
本文探讨了PHP中常用的设计模式及其在实际项目中的应用。内容涵盖设计模式的基本概念、分类和具体使用场景,重点介绍了单例模式、工厂模式和观察者模式等常见模式。通过具体的代码示例,展示了如何在PHP项目中有效利用设计模式来提升代码的可维护性和扩展性。文章还讨论了设计模式的选择原则和注意事项,帮助开发者在不同情境下做出最佳决策。
|
1月前
|
设计模式 开发者 Python
Python编程中的设计模式:工厂方法模式###
本文深入浅出地探讨了Python编程中的一种重要设计模式——工厂方法模式。通过具体案例和代码示例,我们将了解工厂方法模式的定义、应用场景、实现步骤以及其优势与潜在缺点。无论你是Python新手还是有经验的开发者,都能从本文中获得关于如何在实际项目中有效应用工厂方法模式的启发。 ###
|
1月前
|
设计模式 安全 Java
Kotlin - 改良设计模式 - 构建者模式
Kotlin - 改良设计模式 - 构建者模式
|
1月前
|
设计模式 安全 Java
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
42 1