详解设计模式-单例模式

单例模式作为创建型设计模式的一种,保证一个类只有一个实例,并提供全局访问点。本文详细介绍了单例模式的意图、应用场景、实现方式,包括懒汉式、饿汉式、静态内部类和枚举四种实现,以及它们的优缺点和线程安全性。
摘要由CSDN通过智能技术生成

简介

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

设计模式分为三大类
创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

注意:

  • 单例类只能有一个实例。
  • 单例类必须自己创建自己的唯一实例。
  • 单例类必须给所有其他对象提供这一实例。

实现细节

意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。

主要解决:一个全局使用的类频繁地创建与销毁。

何时使用:当您想控制实例数目,节省系统资源的时候。

如何解决:判断系统是否已经有这个单例,如果有则返回,如果没有则创建。

关键代码:构造函数是私有的。

优点

  1. 在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例。
  2. 避免对资源的多重占用(比如写文件操作)。

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

实现方式

懒汉式 < Lazy singleton >

懒初始化:否
多线程安全:是 (通过synchronized关键字 + 双重校验锁 )
描述: 通过双锁机制,安全且在多线程情况下能保持高性能。getInstance() 的性能对应用程序很关键。
synchronized关键字内外都加了一层 if 条件判断,这样既保证了线程安全,又比直接上锁提高了执行效率,还节省了内存空间

package com.study.designmode.singleton.lazy;

/**
 * Lazy singleton
 *
 * @author yongwang.lu
 */
public class LazySingleton {

    /**
     * 实例
     * volatile 修饰
     */
    private volatile static LazySingleton instance;

    /**
     * 私有化构造函数
     */
    private LazySingleton() {

    }

    /**
     * 获取实例入口
     *
     * @return LazySingleton
     */
    public static LazySingleton getInstance() {
        if (instance == null) {
            // 双重校验锁
            synchronized (LazySingleton.class) {
                // 防止多线程多次初始化
                if (instance == null) {
                    instance = new LazySingleton();
                }
            }
        }
        return instance;
    }
}

饿汉式< Hungry singleton >

懒初始化:否
多线程安全:是 (Java类加载机制避免了多线程的同步问题)
描述:这种方式比较常用,但容易产生垃圾对象。
- 优点:没有加锁,执行效率会提高。
- 缺点:类加载时就初始化,浪费内存。

package com.study.designmode.singleton.hungry;

/**
 * Lazy singleton
 *
 * @author yongwang.lu
 */
public class HungrySingleton {

    /**
     * 实例
     */
    private static final HungrySingleton INSTANCE = new HungrySingleton();

    /**
     * 私有化构造函数
     */
    private HungrySingleton() {
        // 防止反射攻击
        if (INSTANCE != null) {
            throw new RuntimeException("单例不允许多个实例");
        }
    }
    /**
     * 获取实例入口
     *
     * @return LazySingleton
     */
    public static HungrySingleton getInstance() {
        return INSTANCE;
    }
}

静态内部类< Inner class singleton >

懒初始化:是
多线程安全:是 (Java类加载机制避免了多线程的同步问题)
描述:个人理解这个是对饿汉式的优化
饿汉式方式只要类被装载了,那么 instance 就会被实例化(没有达到懒初始化效果),而这种方式是 Singleton 类被装载了,instance 不一定被初始化。因为 SingletonHolder 类没有被主动使用,只有通过显式调用 getInstance 方法时,才会显式装载 SingletonHolder 类,从而实例化 instance。

package com.study.designmode.singleton.inner;

/**
 * Inner class singleton
 *
 * @author yongwang.lu
 */
public class InnerClassSingleton {

  	/**
  	 * 静态内部类
  	 */
    private static class InnerClassSingletonHolder {
        private static final InnerClassSingleton INSTANCE = new InnerClassSingleton();
    }

    /**
     * 私有化构造函数
     */
    private InnerClassSingleton() {
        // 防止反射攻击
        if (InnerClassSingletonHolder.INSTANCE != null) {
            throw new RuntimeException("单例不允许多个实例");
        }
    }

    public static InnerClassSingleton getInstance() {
        return InnerClassSingletonHolder.INSTANCE;
    }
}

枚举< Enum singleton >

懒初始化:否
多线程安全:是 (Java类加载机制避免了多线程的同步问题)
描述:这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。
这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。不过,由于 JDK1.5 之后才加入 enum 特性,用这种方式写不免让人感觉生疏,在实际工作中,也很少用。
不能通过 reflection attack 来调用私有构造方法。

package com.study.designmode.singleton.enums;

/**
 * EnumSingleton
 *
 * @author yongwang.lu
 */
public enum EnumSingleton {

    /**
     * 实例
     */
    INSTANCE;

    EnumSingleton() {
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

L丶lulu

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

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

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

打赏作者

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

抵扣说明:

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

余额充值