[设计模式Java实现附plantuml源码~创建型] 确保对象的唯一性~单例模式

简介: [设计模式Java实现附plantuml源码~创建型] 确保对象的唯一性~单例模式

前言:

为什么之前写过Golang 版的设计模式,还在重新写Java 版?

答:因为对于我而言,当然也希望对正在学习的大伙有帮助。Java作为一门纯面向对象的语言,更适合用于学习设计模式。

为什么类图要附上uml

因为很多人学习有做笔记的习惯,如果单纯的只是放一张图片,那么学习者也只能复制一张图片,可复用性较低,附上uml,方便有新理解时,快速出新图。


饿汉单例

@startuml
class Singleton {
  - static instance = new Singleton
  + static getInstance(): Singleton
  + operation(): void
}
Singleton --> Singleton : getInstance()
@enduml

懒汉单例

@startuml
class Singleton {
  - static instance = null
  - constructor: Singleton
  + static getInstance(): Singleton
  + operation(): void
}
Singleton --> Singleton : getInstance()
@enduml

懒汉加锁

@startuml
class Singleton {
  - static volatile instance = null
  - constructor: Singleton
  + static getInstance(): synchronized Singleton
  + operation(): void
}
note left of Singleton::getInstance
if (instance == null)
  instance = construct();
return instance;
end note
Singleton --> Singleton : getInstance()
@enduml

懒汉双重检测

@startuml
class Singleton {
  - static volatile instance = null
  - constructor: Singleton
  + static getInstance(): synchronized Singleton
  + operation(): void
}
note left of Singleton::getInstance
// 一重检测
if (instance == null){
    synchronized(..) {
//  二重检测
        if(instance == null) {
              instance = construct();
        }
    }
}
return instance;
end note
Singleton --> Singleton : getInstance()
@enduml

代码实现

饿汉
public class EagerSingleton {
    private static final EagerSingleton instance = new EagerSingleton();

    // 私有构造方法,防止外部实例化
    private EagerSingleton() {
    }

    // 获取单例对象的静态方法
    public static EagerSingleton getInstance() {
        return instance;
    }
}


EagerSingleton 类的构造方法被声明为私有,以防止外部通过 new 关键字实例化该类。而类中定义了一个私有静态变量 instance,它在类加载的时候就被创建并初始化为 EagerSingleton 类的一个实例。

getInstance() 方法是获取单例对象的静态方法,它直接返回了已经创建好的 instance 对象。

由于饿汉单例在类加载的时候就创建了对象,因此它具有线程安全的特性,但可能会造成一定的资源浪费,因为无论是否使用该单例对象,都会被提前创建。


懒汉单锁
public class LazySingleton {
    private static LazySingleton instance;

    // 私有构造方法,防止外部实例化
    private LazySingleton() {
    }

    // 获取单例对象的静态方法,使用synchronized关键字实现线程安全
    public static synchronized LazySingleton getInstance() {
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
}
懒汉双重检测
public class LazySingleton {
    private static volatile LazySingleton instance;

    // 私有构造方法,防止外部实例化
    private LazySingleton() {
    }

    // 获取单例对象的静态方法,使用双重检测实现延迟加载和线程安全
    public static LazySingleton getInstance() {
        if (instance == null) {
            synchronized (LazySingleton.class) {
                if (instance == null) {
                    instance = new LazySingleton();
                }
            }
        }
        return instance;
    }
}

在上面的代码中,LazySingleton 类的构造方法被声明为私有,以防止外部通过 new 关键字实例化该类。类中定义了一个私有静态变量 instance,它在第一次调用 getInstance() 方法时才会被创建并初始化为 LazySingleton 类的一个实例。

getInstance() 方法是获取单例对象的静态方法。在方法中,首先检查 instance 是否为空,如果为空则进入同步代码块。在同步代码块内部,再次检查 instance 是否为空,这是为了防止多个线程同时通过了第一个检查而进入同步块,

从而创建多个实例。如果 instance 仍然为空,则创建新的实例并将其赋值给 instance

使用 volatile 关键字修饰 instance 变量可以保证变量的可见性,从而避免在多线程环境下出现问题。

懒汉双重检测通过双重检查和同步块的方式实现了延迟加载和线程安全,同时也提高了性能。因此,它是一种常见的单例模式实现方式。

饿汉式单例类不能实现延迟加载,不管将来用不用,它始终占据内存;

懒汉式单例类线程安全控制烦琐,而且性能受影响。

可见,无论是饿汉式单例还是懒汉式单例都存在这样那样的问题。

有没有一种方法,能够将两种单例的缺点都克服,而将两者的优点合二为一呢?答案是肯定的。下面来学习这种更好的被称为 Initialization on Demand Holder(IoDH) 的技术。实现 IoDH 时,需在单例类中增加一个静态 (static) 内部类,在该内部类中创建单例对象,再将该单例对象通过 getInstance() 方法返回给外部使用

IODH实现懒汉模式
public class LazySingleton {
    private static class SingletonHolder {
        private static final LazySingleton instance = new LazySingleton();
    }

    // 私有构造方法,防止外部实例化
    private LazySingleton() {
    }

    // 获取单例对象的静态方法
    public static LazySingleton getInstance() {
        return SingletonHolder.instance;
    }
}

在上面的代码中,LazySingleton 类内部定义了一个私有静态内部类 SingletonHolder。在 SingletonHolder 内部,定义了一个私有静态变量 instance它是 LazySingleton 类的一个实例。

由于静态内部类 SingletonHolder 只有在 getInstance() 方法被调用时才会被加载,所以在类加载的过程中并不会创建 instance 对象。只有当第一次调用getInstance() 方法时,SingletonHolder 类会被加载,从而创建并初始化 instance 对象。这种方式利用了 Java 类加载的线程安全性和延迟加载的特性,实现了懒汉模式。

因此,通过 IODH 实现懒汉模式,可以在需要时延迟创建单例对象,并且保证了线程安全性。

单例模式总结

单例模式作为一种目标明确、结构简单、理解容易的设计模式,在软件开发中使用频率相当高,在很多应用软件和框架中都得以广泛应用。

1.主要优点单例模式的主要优点如下:

(1)单例模式提供了对唯一实例的受控访问。因为单例类封装了它的唯一实例,所以它可以严格控制客户怎样以及何时访问它。

(2)由于在系统内存中只存在一个对象,因此可以节约系统资源。对于一些需要频繁创建和销毁的对象,单例模式无疑可以提高系统的性能。(3)允许可变数目的实例。基于单例模式,开发人员可以进行扩展,使用与控制单例对象相似的方法来获得指定个数的实例对象,既节省系统资源,又解决了由于单例对象共享过多有损性能的问题。(注:自行提供指定数目实例对象的类可称之为多例类。)

单例模式的主要缺点如下:

(1)由于单例模式中没有抽象层,因此单例类的扩展有很大的困难。

(2)单例类的职责过重,在一定程度上违背了单一职责原则。因为单例类既提供了业务方法,又提供了创建对象的方法(工厂方法),将对象的创建和对象本身的功能耦合在一起。

(3)现在很多面向对象语言(如Java、C#)的运行环境都提供了自动垃圾回收技术,因此,如果实例化的共享对象长时间不被利用,系统会认为它是垃圾,会自动销毁并回收资源,下次利用时又将重新实例化,这将导致共享的单例对象状态的丢失。

3.适用场景在以下情况下可以考虑使用单例模式:

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

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

相关文章
|
14天前
|
XML Java 编译器
Java注解的底层源码剖析与技术认识
Java注解(Annotation)是Java 5引入的一种新特性,它提供了一种在代码中添加元数据(Metadata)的方式。注解本身并不是代码的一部分,它们不会直接影响代码的执行,但可以在编译、类加载和运行时被读取和处理。注解为开发者提供了一种以非侵入性的方式为代码提供额外信息的手段,这些信息可以用于生成文档、编译时检查、运行时处理等。
50 7
|
25天前
|
数据采集 人工智能 Java
Java产科专科电子病历系统源码
产科专科电子病历系统,全结构化设计,实现产科专科电子病历与院内HIS、LIS、PACS信息系统、区域妇幼信息平台的三级互联互通,系统由门诊系统、住院系统、数据统计模块三部分组成,它管理了孕妇从怀孕开始到生产结束42天一系列医院保健服务信息。
28 4
|
1月前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
71 2
|
6天前
|
存储 JavaScript 前端开发
基于 SpringBoot 和 Vue 开发校园点餐订餐外卖跑腿Java源码
一个非常实用的校园外卖系统,基于 SpringBoot 和 Vue 的开发。这一系统源于黑马的外卖案例项目 经过站长的进一步改进和优化,提供了更丰富的功能和更高的可用性。 这个项目的架构设计非常有趣。虽然它采用了SpringBoot和Vue的组合,但并不是一个完全分离的项目。 前端视图通过JS的方式引入了Vue和Element UI,既能利用Vue的快速开发优势,
52 13
|
20天前
|
缓存 监控 Java
Java线程池提交任务流程底层源码与源码解析
【11月更文挑战第30天】嘿,各位技术爱好者们,今天咱们来聊聊Java线程池提交任务的底层源码与源码解析。作为一个资深的Java开发者,我相信你一定对线程池并不陌生。线程池作为并发编程中的一大利器,其重要性不言而喻。今天,我将以对话的方式,带你一步步深入线程池的奥秘,从概述到功能点,再到背景和业务点,最后到底层原理和示例,让你对线程池有一个全新的认识。
50 12
|
14天前
|
JavaScript 安全 Java
java版药品不良反应智能监测系统源码,采用SpringBoot、Vue、MySQL技术开发
基于B/S架构,采用Java、SpringBoot、Vue、MySQL等技术自主研发的ADR智能监测系统,适用于三甲医院,支持二次开发。该系统能自动监测全院患者药物不良反应,通过移动端和PC端实时反馈,提升用药安全。系统涵盖规则管理、监测报告、系统管理三大模块,确保精准、高效地处理ADR事件。
|
20天前
|
设计模式 消息中间件 搜索推荐
Java 设计模式——观察者模式:从优衣库不使用新疆棉事件看系统的动态响应
【11月更文挑战第17天】观察者模式是一种行为设计模式,定义了一对多的依赖关系,使多个观察者对象能直接监听并响应某一主题对象的状态变化。本文介绍了观察者模式的基本概念、商业系统中的应用实例,如优衣库事件中各相关方的动态响应,以及模式的优势和实际系统设计中的应用建议,包括事件驱动架构和消息队列的使用。
|
1月前
|
设计模式 Java 数据库连接
Java编程中的设计模式:单例模式的深度剖析
【10月更文挑战第41天】本文深入探讨了Java中广泛使用的单例设计模式,旨在通过简明扼要的语言和实际示例,帮助读者理解其核心原理和应用。文章将介绍单例模式的重要性、实现方式以及在实际应用中如何优雅地处理多线程问题。
36 4
|
16天前
|
人工智能 移动开发 安全
家政上门系统用户端、阿姨端源码,java家政管理平台源码
家政上门系统基于互联网技术,整合大数据分析、AI算法和现代通信技术,提供便捷高效的家政服务。涵盖保洁、月嫂、烹饪等多元化服务,支持多终端访问,具备智能匹配、在线支付、订单管理等功能,确保服务透明、安全,适用于家庭生活的各种需求场景,推动家政市场规范化发展。
|
1月前
|
设计模式 JavaScript Java
Java设计模式:建造者模式详解
建造者模式是一种创建型设计模式,通过将复杂对象的构建过程与表示分离,使得相同的构建过程可以创建不同的表示。本文详细介绍了建造者模式的原理、背景、应用场景及实际Demo,帮助读者更好地理解和应用这一模式。