Java设计模式之创建型-单例模式(UML类图+案例分析)

本文介绍了单例模式的概念和重要性,详细讲解了不同实现方式,包括饿汉模式、懒汉模式(线程不安全和安全)、双重检索模式、静态内部类和枚举的实现,分析了各自的优缺点和适用场景。单例模式主要优点是内存优化和全局访问,但也存在扩展困难和可能被破坏的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

一、基础概念

二、UML类图

三、角色设计

四、案例分析

4.1、饿汉模式

4.2、懒汉模式(线程不安全)

4.3、懒汉模式(线程安全)

4.4、双重检索模式

4.5、静态内部类

4.6、枚举 

五、总结


一、基础概念

单例模式确保一个类只有一个实例,提供一个全局访问点。一般实现方式是把构造函数设为私有,并提供一个静态方法获取实例对象。

二、UML类图

三、角色设计

角色描述
单例在单例类的内部实现只生成一个实例,同时它提供一个静态的 getInstance()方法,让外部以访问它的唯一实例;为了防止在外部对其实例化,将其构造函数设计为私有;在单例类内部定义了一个 Singleton类型的静态对象,作为外部共享的唯一实例。

四、案例分析

4.1、饿汉模式

直接在类内部创建一个静态final常量实例,线程安全,调用效率高,但无法延迟加载。

public class Singleton_Hungry {
    /**
     * 私有实例,静态变量会在类加载的时候初始化,是线程安全的
     */
    private static final Singleton_Hungry instance = new Singleton_Hungry();

    /**
     * 私有构造方法
     */
    private Singleton_Hungry() {
    }

    /**
     * 获取实例的方法
     */
    public static Singleton_Hungry getInstance() {
        return instance;
    }
}

4.2、懒汉模式(线程不安全)

起初没有实例,第一次调用才初始化,线程不安全,需加锁处理。

public class Singleton_Lazy {
    /**
     * 私有实例
     */
    private static Singleton_Lazy instance;

    /**
     * 私有构造方法
     */
    private Singleton_Lazy() {
    }

    /**
     * 获取实例的方法
     */
    public static Singleton_Lazy getInstance() {
        if (instance == null) {
            instance = new Singleton_Lazy();
        }
        return instance;
    }
}

4.3、懒汉模式(线程安全)

加锁处理,线程安全但影响效率,大部分情况下不需要同步。

public class Singleton_Lazy_Safe {

    /**
     * 私有实例
     */
    private static Singleton_Lazy_Safe instance;

    /**
     * 私有构造方法
     */
    private Singleton_Lazy_Safe () {
    }

    /**
     * 获取实例的方法,该方法使用synchronized加锁,来保证线程安全性
     */
    public static synchronized Singleton_Lazy_Safe getInstance() {
        if (instance == null) {
            instance = new Singleton_Lazy_Safe ();
        }
        return instance;
    }
}

4.4、双重检索模式

双重检查锁单例模式是在懒汉式单例模式的基础上,额外加锁保证多线程安全,同时保证效率,并且加入了第二次判断,避免多线程下创建多个实例。

public class Singleton_Lazy_Double {
    
    //用volatile关键字确保 instance 在多线程下的可见性
    private static volatile Singleton_Lazy_Double instance = null;

    //将构造方法私有化,禁止外部通过构造方法创建实例
    private Singleton_Lazy_Double() {}

    //提供一个公共的访问方法,用于获取该类的唯一实例
    public static Singleton_Lazy_Double getInstance() {
        //如果instance为空,就进行实例化
        if (instance == null) {
            //保证多线程下只有一个线程进行实例化
            synchronized (Singleton_Lazy_Double.class) {
                //第二次判断,避免多线程下创建多个实例
                if (instance == null) {
                    instance = new Singleton_Lazy_Double();
                }
            }
        }
        return instance;
    }

}

4.5、静态内部类

静态内部类单例模式是在类加载时会创建一个静态内部类,调用getInstance方法时才会去加载该内部类并初始化INSTANCE实例。该方式既保证了线程安全,也保证了懒加载和高效访问的特性。

public class SingletonStatic {

    /**
     * 私有构造方法
     */
    private SingletonStatic() {
    }

    /**
     * 取实例的方法
     */
    public static SingletonStatic getInstance() {
        return LazyHolder.INSTANCE;
    }

    /**
     * 私有静态内部类
     */
    private static class LazyHolder {
        private static final SingletonStatic INSTANCE = new SingletonStatic();
    }
}

4.6、枚举 

使用枚举实现单例模式可以节省大量代码,并且防止多线程安全以及反序列化及反射攻击的问题。

public enum SingletonEnum {

    INSTANCE;

    public void test() {
        System.out.println("hello");
    }

}

五、总结

优点:

1、在内存中只有一个实例,减少内存开销。避免对资源的多重占用。设置全局访问点,严格控制访问。

缺点:

1、不符合单一职责原则。扩展困难,如果要扩展需要修改源代码。攻击者可通过反射和反序列化的方式获取实例而破坏单例。

应用场景:

1、需要频繁实例化然后销毁的对象、创建对象时消耗资源过多的情况等。

2、单例模式应用在工具类、线程池、配置类、对话框、日志对象、缓存等场景中,在工具类中应用是最为典型的。

符合的设计原则:

1、单一职责原则(Single Responsibility Principle)

单例类只负责创建自己的一个实例,有且只有这一个职责。

2、开闭原则(Open Closed Principle)

单例类可以通过扩展改变内部实现逻辑,而不影响调用端。

3、里氏替换原则(Liskov Substitution Principle)

单例类一般会实现一个接口定义方法,任何扩展的实现会保持一致性。

4、依赖倒转原则(Dependency Inversion Principle)

调用端通过接口依赖单例,不依赖具体实现,降低了依赖度。

5、接口隔离原则(Interface Segregation Principle)

单例类只定义获取实例的接口,符合接口隔离原则。

枚举实现单例最大优点是实现简单,天生线程安全,防反射攻击,是首选方式。

总的来说,以上各种实现方式之间存在效率、线程安全、懒加载等方面的权衡和区别,应根据需要选用最合适的单例模式实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

黄团团

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值