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

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();
    }
}
相关推荐
Gixy2 小时前
聊聊纯函数与不可变数据结构
前端·设计模式
Java菜鸟、2 小时前
设计模式(代理设计模式)
java·开发语言·设计模式
何中应3 小时前
【设计模式-3.7】结构型——组合模式
java·设计模式·组合模式
magic 2453 小时前
Java设计模式之观察者模式详解
观察者模式·设计模式
秋田君7 小时前
深入理解JavaScript设计模式之闭包与高阶函数
开发语言·javascript·设计模式
何中应9 小时前
【设计模式-4.11】行为型——解释器模式
java·设计模式·解释器模式
找不到、了10 小时前
实现单例模式的常见方式
java·开发语言·单例模式
WispX88810 小时前
【设计模式】门面/外观模式
java·开发语言·设计模式·系统架构·外观模式·插件·架构设计
蔡蓝10 小时前
设计模式-外观模式
microsoft·设计模式·外观模式
琢磨先生David10 小时前
简化复杂系统的优雅之道:深入解析 Java 外观模式
java·设计模式·外观模式