设计模式-单例模式(Singleton)


设计模式文档无水印可复制

一、单例模式概述

1.1 什么是单例模式

Java 单例模式(Singleton)是一种设计模式,它确保一个类只有一个实例,并提供一个全局访问点。这种模式通常用于需要频繁创建和销毁的对象,例如数据库连接、线程池等。

1.2、实现 Java 单例模式的步骤

  • 1、将类的构造方法设置为私有,以防止外部直接创建实例。
  • 2、在类中创建一个静态私有成员变量,用于存储唯一的实例。
  • 3、提供一个公共的静态方法,用于获取唯一的实例。在这个方法中,首先检查实例是否已经存在,如果不存在,则创建一个新的实例并将其赋值给静态成员变量;如果已经存在,则直接返回该实例。
  • 4、为了确保线程安全,可以使用双重检查锁定(Double-Checked Locking)机制。在获取实例的方法中,首先检查实例是否已经创建,如果没有创建,才进入同步代码块。这样可以降低锁的开销,提高性能。

以下是一个简单的 Java 单例模式实现:

public class Singleton {
    // 静态私有成员变量,用于存储唯一的实例
    private static Singleton instance;

    // 将构造方法设置为私有,以防止外部直接创建实例
    private Singleton() {
    }

    // 提供一个公共的静态方法,用于获取唯一的实例
    public static Singleton getInstance() {
        // 第一次检查:如果实例尚未创建,才进入同步代码块
        if (instance == null) {
            synchronized (Singleton.class) {
                // 第二次检查:确保在当前线程进入同步代码块之前,其他线程没有创建实例
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

1.3 使用单例模式注意事项

  • 1、私有构造函数:确保类的构造函数是私有的,以防止外部实例化。
  • 2、静态成员变量:在类中定义一个静态成员变量,用于存储唯一的实例。
  • 3、获取实例的方法:提供一个公共静态方法,用于获取唯一的实例。在这个方法中,需要判断实例是否已经创建,如果没有创建,则创建一个新的实例并返回;如果已经创建,则直接返回已创建的实例。
  • 4、线程安全:如果需要在多线程环境下使用单例模式,需要考虑线程安全问题。可以使用双重检查锁定(Double-Checked Locking)或者静态内部类的方式来实现线程安全的单例模式。
  • 5、延迟加载:为了提高性能,可以将实例的创建放在第一次访问时进行,而不是在类加载时就创建。这样可以节省资源,特别是在实例创建成本较高的情况下。

二、 单例模式的用途

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一,它提供了一种创建对象的最佳方式。这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

以下是一些主要的应用场景:

  • 1、整个程序的运行中只允许有一个类的实例。这是单例模式最典型的应用场景。
  • 2、需要频繁实例化然后销毁的对象。例如,数据库连接、线程池等。在这种情况下,使用单例模式可以降低系统资源消耗,因为只需要创建一个实例,避免了反复创建和销毁对象的性能开销。
  • 3、创建的对象需要占用大量的系统资源,例如 I/O 流、网络连接等。为了保证资源在任何时刻都能被共享,可以使用单例模式来实现对象的全局访问。
  • 4、当类需要维护某些状态时,也可以使用单例模式。因为单例模式保证只有一个实例,所以可以确保状态在全局范围内保持一致。

三、单例模式的实现方式

单例模式的实现方式有多种,以下是一些常见的实现方式:饿汉式、懒汉式、双重校验(Dual-Check)、静态内部类、枚举;

3.1 饿汉式

这种实现方式在类被加载时就完成了实例化,所以类加载比较慢,但获取对象的速度快。这种方式基于类加载机制避免了多线程的同步问题,不过,它不能实现延时加载,也没有用到同步技术。

以下是使用 Java 饿汉模式实现单例模式的示例代码:

public class Singleton {
    // 创建一个私有静态成员变量,用于存储单例对象
    private static final Singleton instance = new Singleton();

    // 将构造方法设为私有,防止外部直接创建对象
    private Singleton() {}

    // 提供一个公共静态方法,用于获取单例对象
    public static Singleton getInstance() {
        return instance;
    }
}

在上述代码中,我们定义了一个名为 Singleton 的类,并在其中创建了一个私有静态成员变量 instance,用于存储单例对象。然后,我们将构造方法设为私有,以防止外部直接创建对象。最后,我们提供了一个公共静态方法 getInstance(),用于获取单例对象。当调用该方法时,会直接返回已经创建好的单例对象。

3.2 懒汉式

该方式是当第一次调用 getInstance() 方法时实例化对象。这种方式相比于饿汉式,节省了系统资源,但第一次获取对象时需要加锁,会影响性能。

以下是使用 Java 实现懒汉式单例模式的示例代码:

public class Singleton {
    // 创建一个私有静态成员变量,用于存储单例对象
    private static Singleton instance;

    // 将构造方法设为私有,防止外部直接创建对象
    private Singleton() {}

    // 提供一个公共静态方法,用于获取单例对象
    public static synchronized Singleton getInstance() {
        // 判断实例是否已经创建,如果未创建则创建并返回
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

在上述代码中,我们首先定义了一个私有静态成员变量 instance,用于存储单例对象。然后,我们将构造方法设为私有,以防止外部直接创建对象。接下来,我们提供了一个公共静态方法 getInstance(),用于获取单例对象。在该方法中,我们首先判断实例是否已经创建,如果未创建则创建并返回。由于使用了 synchronized 关键字,因此这个方法是线程安全的。

3.3 双重校验(Dual-Check)

这种方式既能保证线程安全又能保证延迟加载。在多线程环境中仍能保持高性能。

双重校验实现单例模式的代码如下:

public class Singleton {
    private static volatile Singleton instance;

    private Singleton() {}

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

3.4 静态内部类

这种方式兼具延迟初始化和线程安全性。这种方式采用了静态内部类的特性来实现单例,不仅能保证线程安全,而且能够支持懒加载。

以下是一个示例:

public class Singleton {
    // 创建一个私有静态内部类
    private static class SingletonHolder {
        // 创建一个私有静态实例
        private static final Singleton INSTANCE = new Singleton();
    }

    // 将构造方法设为私有,防止外部实例化
    private Singleton() {}

    // 提供一个公共静态方法,用于获取单例实例
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

使用这个单例类的方式如下:

public class Main {
    public static void main(String[] args) {
        Singleton singleton1 = Singleton.getInstance();
        Singleton singleton2 = Singleton.getInstance();

        System.out.println(singleton1 == singleton2); // 输出 true,表示两个实例是同一个对象
    }
}

3.5 枚举

这种方式通过 Java 枚举类型本身的特性来保证了实例创建的单一性,同时枚举类型的实例是在类加载时就进行了初始化的,所以对资源的利用率更高。
以下是一个简单的示例:

public enum Singleton {
    INSTANCE;

    public void doSomething() {
        System.out.println("Doing something...");
    }
}

在这个示例中,Singleton是一个枚举类型,它只有一个实例INSTANCE。你可以通过Singleton.INSTANCE来访问这个实例,并调用其方法。例如:

public class Main {
    public static void main(String[] args) {
        Singleton.INSTANCE.doSomething();
    }
}

这段代码会输出"Doing something…"。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

一碗油泼面

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

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

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

打赏作者

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

抵扣说明:

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

余额充值