设计模式-创建型模式-单例模式

0 引言

创建型模式(Creational Pattern)关注对象的创建过程,是一类最常用的设计模式,每个创建型模式都通过采用不同的解决方案来回答3个问题:创建什么(What),由谁创建(Who)和何时创建(When)。

1 单例模式

单例模式有3个要点:①某个类只能有一个实例;②它必须自行创建这个实例;③它必须自行向整个系统提供这个实例。

单例模式(Singleton),保证一个类仅有一个实例,并提供一个访问它的全局访问点。[DP]

1.1 饿汉单例模式

在类加载的时候就已经实例化,并且创建单例对象,以后直接使用即可。这种模式下,类加载较慢,但获取对象的速度快,且线程安全。

public class HungrySingleton {
    // 在类加载时就已经完成了实例的初始化
    private static final HungrySingleton instance = new HungrySingleton();

    // 构造器私有,防止外部通过new关键字创建对象
    private HungrySingleton() {}

    // 提供全局访问点
    public static HungrySingleton getInstance() {
        return instance;
    }

    // 如果需要,可以添加其他方法或属性
    public void showMessage() {
        System.out.println("This is an instance of HungrySingleton.");
    }

    public static void main(String[] args) {
        // 获取单例对象
        HungrySingleton instance1 = HungrySingleton.getInstance();
        HungrySingleton instance2 = HungrySingleton.getInstance();

        // 输出实例,验证是否为同一个对象
        System.out.println(instance1);
        System.out.println(instance2);

        // 验证是否为同一个对象的引用
        System.out.println(instance1 == instance2);

        // 调用实例方法
        instance1.showMessage();
    }
}

1.2 懒汉单例模式

一开始不会实例化,什么时候用就什么时候进行实例化。这种模式下,类加载较快,但获取对象的速度稍慢,且可能在多线程情况下出现线程安全问题。

存在线程安全问题,

public class LazySingleton {
    // 私有静态实例,初始化为null
    private static LazySingleton instance = null;

    // 私有构造方法,防止外部通过new关键字创建对象
    private LazySingleton() {}

    // 提供全局访问点
    public static  LazySingleton getInstance() {
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }

    // 如果需要,可以添加其他方法或属性
    public void showMessage() {
        System.out.println("This is an instance of LazySingleton.");
    }

    public static void main(String[] args) {
        // 获取单例对象
        LazySingleton instance1 = LazySingleton.getInstance();

        // 调用实例方法
        instance1.showMessage();
    }
}

加锁,

public class LazySingleton {
    // 私有静态实例,初始化为null
    private static LazySingleton instance = null;

    // 私有构造方法,防止外部通过new关键字创建对象
    private LazySingleton() {}

    // 同步方法,提供全局访问点
    public static synchronized LazySingleton getInstance() {
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }

    // 如果需要,可以添加其他方法或属性
    public void showMessage() {
        System.out.println("This is an instance of LazySingleton.");
    }

    public static void main(String[] args) {
        // 获取单例对象
        LazySingleton instance1 = LazySingleton.getInstance();
        LazySingleton instance2 = LazySingleton.getInstance();

        // 输出实例,验证是否为同一个对象
        System.out.println(instance1);
        System.out.println(instance2);

        // 验证是否为同一个对象的引用
        System.out.println(instance1 == instance2);

        // 调用实例方法
        instance1.showMessage();
    }
}

然而,同步方法会导致性能下降,因为每次调用getInstance()方法时都需要进行同步。为了解决这个问题,可以使用**双重校验锁(Double-Checked Locking,DCL)**来实现更高效的懒汉单例模式:现在这样,我们不用让线程每次都加锁,而只是在实例未被创建的时候再加锁处理。同时也能保证多线程的安全。这种做法被称为Double-Check Locking(双重锁定)。

public class LazySingletonWithDCL {
    // volatile关键字确保instance在多线程环境下被正确初始化
    private static volatile LazySingletonWithDCL instance = null;

    // 私有构造方法,防止外部通过new关键字创建对象
    private LazySingletonWithDCL() {}

    // 提供全局访问点
    public static LazySingletonWithDCL getInstance() {
        if (instance == null) {
            // 第一次检查
            synchronized (LazySingletonWithDCL.class) {
                if (instance == null) {
                    // 第二次检查
                    instance = new LazySingletonWithDCL();
                }
            }
        }
        return instance;
    }

    // 如果需要,可以添加其他方法或属性
    public void showMessage() {
        System.out.println("This is an instance of LazySingletonWithDCL.");
    }

    public static void main(String[] args) {
        // 获取单例对象
        LazySingletonWithDCL instance1 = LazySingletonWithDCL.getInstance();
        LazySingletonWithDCL instance2 = LazySingletonWithDCL.getInstance();

        // 输出实例,验证是否为同一个对象
        System.out.println(instance1);
        System.out.println(instance2);

        // 验证是否为同一个对象的引用
        System.out.println(instance1 == instance2);

        // 调用实例方法
        instance1.showMessage();
    }
}

使用内部静态类来实现单例模式,这种方式的特点是利用了类加载机制来保证初始化实例时只有一个实例被创建,并且由于JVM的类加载机制,这种方式是线程安全的。只适合java。

public class Singleton {
    // 私有构造方法,防止外部通过new关键字创建对象
    private Singleton() {}

    // 静态内部类,持有单例对象
    private static class SingletonHolder {
        // 静态初始化器,由JVM保证线程安全
        private static final Singleton INSTANCE = new Singleton();
    }

    // 提供全局访问点
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }

    // 如果需要,可以添加其他方法或属性
    public void showMessage() {
        System.out.println("This is an instance of Singleton.");
    }

    public static void main(String[] args) {
        // 获取单例对象
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();

        // 输出实例,验证是否为同一个对象
        System.out.println(instance1);
        System.out.println(instance2);

        // 验证是否为同一个对象的引用
        System.out.println(instance1 == instance2);

        // 调用实例方法
        instance1.showMessage();
    }
}
相关推荐
越甲八千4 小时前
重温设计模式--享元模式
设计模式·享元模式
码农爱java5 小时前
设计模式--抽象工厂模式【创建型模式】
java·设计模式·面试·抽象工厂模式·原理·23种设计模式·java 设计模式
越甲八千6 小时前
重温设计模式--中介者模式
windows·设计模式·中介者模式
犬余6 小时前
设计模式之桥接模式:抽象与实现之间的分离艺术
笔记·学习·设计模式·桥接模式
Theodore_10227 小时前
1 软件工程——概述
java·开发语言·算法·设计模式·java-ee·软件工程·个人开发
越甲八千9 小时前
重拾设计模式--组合模式
设计模式·组合模式
思忖小下11 小时前
梳理你的思路(从OOP到架构设计)_设计模式Composite模式
设计模式·组合模式·eit
机器视觉知识推荐、就业指导12 小时前
C++设计模式:组合模式(公司架构案例)
c++·后端·设计模式·组合模式
越甲八千12 小时前
重拾设计模式--工厂模式(简单、工厂、抽象)
c++·设计模式
重生之绝世牛码13 小时前
Java设计模式 —— 【结构型模式】外观模式详解
java·大数据·开发语言·设计模式·设计原则·外观模式