23天设计模式之单例模式

23天设计模式之单例模式

文章简介

《23天设计模式之单例模式》这是个人第二篇博客。在接下来的23天内,咱们将23种设计模式都去了解一下。今天咱们就来学习最简单的单例模式java

在学习设计模式以前咱们不可避免要去想为何要学习这个东西,它是用来干吗的?算法

  • 要知道在软件开发中,要实现可维护、可扩展,就必须尽可能复用代码,而且下降代码的耦合度
  • 另外,我认为学习设计模式能够潜移默化地对咱们的编程思想产生好的影响。其实咱们常常都有使用到设计模式,只是没有去学习过便没有意识到,好比JDK的动态代理就是一种代理模式
  • 设计模式主要是基于OOP编程【面向对象编程(object-oriented programming);】提炼的,它基于如下几个原则。

OOP7大原则

  • 开闭原则。对扩展开放,对修改关闭。
  • 里氏替换原则。继承必须确保超类所拥有的性质在子类中仍然成立。(子类在程序中能够替换父类)
  • 依赖倒置原则。要面向接口编程,不要面向实现编程。(实现公共接口,而不是每一个接口都实现)
  • 单一职责原则。控制类的粒度大小、将对象解耦、提升其内聚性。(方法的原子性)
  • 接口隔离原则。要为各个类创建他们所须要的专用接口。
  • 迪米特法则。只与你的直接朋友交谈,不跟“陌生人”说话。
  • 合成复用原则。尽可能先使用组合或聚合等关联关系来实现,其次才考虑使用继承关系来实现。

设计模式分类

整体分为3大类:编程

  • 建立型模式:关注点是如何建立对象,其核心思想是要把对象的建立和使用相分离,这样使得二者能相对独立地变换。
    • 工厂方法:Factory Method
    • 抽象工厂:Abstract Factory
    • 建造者:Builder
    • 原型:Prototype
    • 单例:Singleton
  • 结构型模式。把类或对象结合在一块儿造成一个更大的结构。
    • 适配器
    • 桥接
    • 组合
    • 装饰器
    • 外观
    • 享元
    • 代理
  • 行为型模式。类和对象如何交互,及划分责任和算法。
    • 责任链
    • 命令
    • 解释器
    • 迭代器
    • 中介
    • 备忘录
    • 观察者
    • 状态
    • 策略
    • 模板方法
    • 访问者

单例模式

  • 单例模式指在一个进程中,某个类有且仅有一个实例。
  • 为了防止调用者调用new方法继续构造,构造方法要设置为private。
  • 调用者要怎么获取到单例呢?咱们提供一个public static修饰的静态方法给用户返回单例。
  • 单例模式也分为如下几种,在下面详细介绍。

饿汉式单例

  • 所谓饿汉式单例是指,不论是否调用获取实例的方法,都会产生一个单例。直接上代码!
  • 缺点:无论用不用都会产生单例,浪费空间。
  • 也称为静态单例模式。
public class Singleton {

    //静态单例
    private static final Singleton INSTANCE = new Singleton();

    //私有的构造方法
    private Singleton(){};

    // 获取单例的静态方法
    public static Singleton getInstance() {
        return INSTANCE;
    }
}

懒汉式单例

  • 所谓懒汉是指,这个单例很懒,只有在调用方调用时,才去生成单例。
  • 若是在多线程下不加锁的话,会屡次调用构造方法建立多个实例。直接上代码。
public class Singleton {

    //调用getInstance()方法时初始化
    private static Singleton instance;

    //输出文字看这个构造方法被调用了几回
    private Singleton(){
        System.out.println(Thread.currentThread().getName() + "调用了构造方法");
    };

    //加了判空
    public static Singleton getInstance(){
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }

    //使用多线程测试构造方法被调用了几回。正常来讲应该只调用一次。
    public static void main(String[] args) {
        for (int i = 0; i < 4; i++) {
            new Thread(Singleton::getInstance).start();
        }
    }
}

//输出以下:
Thread-1调用了构造方法
Thread-2调用了构造方法
Thread-0调用了构造方法

分析:windows

  • 每次运行程序,输出都不同;构造方法的明显被屡次调用,即建立了多个单例。

DCL懒汉式单例

  • DCL是指双重检锁。double-checked locking
public class Singleton {

    //volatile禁止指令重排优化
    private static volatile Singleton instance;

    private Singleton(){
        System.out.println(Thread.currentThread().getName() + "调用了构造方法");
    };

    //DCL双重检锁
    public static Singleton getInstance(){
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 4; i++) {
            new Thread(Singleton::getInstance).start();
        }
    }
}

//输出:
Thread-0调用了构造方法

分析:设计模式

  • 不管执行多少次main函数,始终只有一条线程能调用构造函数。安全

  • 指令重排:编译器、JVM 或者 CPU 都有可能出于优化等目的,对于实际指令执行的顺序进行调整。多线程

  • 为何要禁止指令重排:多线程环境中线程交替执行,因为编译器指令重排的存在,两个线程使用的变量可否保证一致性是没法确认的,结果没法预测。函数

静态内部类实现单例

public class Singleton {

    private Singleton(){
    };

    public static Singleton getInstance() {
        return InnerClass.INSTANCE;
    }

    //静态内部类
    public static class InnerClass {
        private static final Singleton INSTANCE = new Singleton();
    }
}

枚举类实现单例

  • 正所谓道高一尺,魔高一丈。即便咱们私有了构造器,单例也并不安全。万能的反射能够仍调用私有构造器,所以咱们须要枚举单例
  • 枚举单例不会被反射破坏。
public enum Singleton {

    INSTANCE;

    public Singleton getInstance(){
        return INSTANCE;
    }
}

以上就是单例模式的主要内容。接下来咱们了解如下单例模式的常见应用场景。oop

单例模式的应用场景

  • Windows的Task Manager(任务管理器)就是很典型的单例模式学习

  • windows的Recycle Bin(回收站)就是典型的单例应用。在整个系统运行过程当中,回收站一直维护着仅有的一个实例。

  • 网站的计数器,通常也是采用单例模式实现,不然难以同步。

  • 多线程的线程池的设计通常也是采用单例模式,这是因为线程池要方便对池中的线程进行控制。

以上

感谢您花时间阅读个人博客,以上就是我对单例模式的一些理解,如有不对之处,还望指正,期待与您交流。

本篇博文系原创,仅用于我的学习,转载请注明出处。

相关文章
相关标签/搜索