设计模式(1)——单例模式

目录

[1. 单例模式的含义](#1. 单例模式的含义)

[2. 单例模式的四种常见方式](#2. 单例模式的四种常见方式)

[2.1 饿汉式经典加载方式](#2.1 饿汉式经典加载方式)

[2.2 懒汉式加载方式](#2.2 懒汉式加载方式)

[2.3 静态内部类方式加载](#2.3 静态内部类方式加载)

[2.4 枚举类加载方式](#2.4 枚举类加载方式)


1. 单例模式的含义

单例,简单来说就是单个实例,是一种常见的软件的设计模式。

一个类只实例化自己的一个实例,并且不允许再创建多余的实例,在对外提供访问时,访问的都是同一个实例对象。

2. 单例模式的四种常见方式

2.1 饿汉式经典加载方式

很好理解,饿的见到什么都吃,在单例模式中指不管用到用不到的资源,都统统加载到内存中。

优点:实现简单,实用性强,且不存在线程安全问题,是我们最常使用的一种实现方式;

缺点:不管用到与否,类加载时就完成实例化统统加载到内存,用不用都要加载,感觉有点浪费和多余,占用内存空间;

代码实现如下,这种方法实现单例模式的核心原理是JVM会且仅会将一个类加载一次到内存中去,加载一次 Mgr01 类,那么 INSTANCE 对象就被创建出来了

java 复制代码
public class Mgr01 {
# 创建一个静态私有化 INSTANCE 对象
    private static final Mgr01 INSTANCE = new Mgr01();
# 私有化构造器,不允许其他人再创建新的类对象
    private Mgr01(){}
# 编写get方法,调用get方法即可返回已经创建好的 INSTANCE 对象
    public static Mgr01 getInstance(){
        return INSTANCE;
    }
# 此处main方法为测试方法,与设计模式无关
    public static void main(String[] args) {
        Mgr01 m1 = new Mgr01().getInstance();
        Mgr01 m2 = new Mgr01().getInstance();
        System.out.println(m1 == m2);
    }
}

我们来运行一下 main 方法,就会发现 m1 对象和 m2 对象是相等的,如下图所示

2.2 懒汉式加载方式

对比饿汉式加载,懒汉式最大的特点就是懒,我不加载,我在需要用到的时候才去进行加载。

java 复制代码
public class Mgr02 {
// 创建一个静态 Mgr02 对象 INSTANCE,但不进行实例化,
// 并且不许使用 volatile 修饰禁止指令重排,否则可能会出错,设计知识点较深,暂不解说
    public static volatile Mgr02 INSTANCE;

// 私有化无参构造,禁止其他人访问创建新实例
    private Mgr02(){}

// 在 get 方法中进行对象的实例化,并采用双重校验实现线程安全
    public static Mgr02 getInstance(){
        // 第一次检查,如果 INSTANCE 不为空,直接返回 INSTANCE 对象
        if (INSTANCE == null){
            // 双重检查再次判断避免出现线程安全问题
            synchronized (Mgr02.class){
                if (INSTANCE == null){
                    INSTANCE = new Mgr02();
                }
            }
        }
        return INSTANCE;
    }
// 此处为测试 main方法测试线程安全
    public static void main(String[] args) {
// 循环一百次,创建一百个线程
        for (int i = 0; i < 100; i++) {
// 匿名内部类的方法创建线程对象
            new Thread(new Runnable() {
                @Override
                public void run() {
// 打印获取到的实例对象的哈希码,如果一样则说明为同一个对象
                    System.out.println(Mgr02.getInstance().hashCode());
                }
            }).start;
        }
    }
}

运行 main 方法,我们可以在控制台发现打印的对象的哈希码是一样的,说明这100个线程对象获取到的 INSTANCE 对象本质上都是同一个;

2.3 静态内部类方式加载

静态内部类是通过JVM的方式保证线程安全,JVM会保证每个类之加载一次,但类中的静态内部类是不会被加载的,只有当我们调用 get 方法时,get 方法会返回给我们一个静态内部类的INSTANCE,而INSTANCE在静态内部类中指代的就是外部类的实例对象,非常神奇,非常花哨,并且可以做到线程安全,任何人来访问get方法获取Mgr03对象的实例,获取到的都是同一个INSTANCE。

实现代码如下所示

java 复制代码
public class Mgr03 {
// 私有化构造方法
    private Mgr03(){}
// 定义静态内部类,静态内部类不会随着类的加载而加载
    private static class Mgr03Helper{
        private final static Mgr03 INSTANCE = new Mgr03();
    }
// 定义静态 get 方法
    public static Mgr03 getInstance(){
        return Mgr03Helper.INSTANCE;
    }
// 此处为 测试 main方法,测试是否有线程安全问题
    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Mgr03.getInstance().hashCode());
                }
            }).start();
        }
    }
}

运行此方法,如下图所示,所有对向的哈希码都是一样的,

2.4 枚举类加载方式

枚举类实现单例模式的方法更为简单,我们只需要定义一个枚举类,然后在枚举类中定义一个INSTANCE对象,枚举类会在变量的前面默认添加 "public static final",所以不需要我们再额外添加

java 复制代码
// 定义一个枚举类 Mgr01
public enum Mgr04 {
// 定义 INSTANCE 对象
    INSTANCE;
// 此处为 main 测试方法,测试是否线程安全
    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Mgr04.INSTANCE.hashCode());
                }
            }).start();
        }
    }
}

运行 main 方法,可以在控制台发现所有的对象的哈希码都是一样的,是线程安全的

相关推荐
xiaobin889991 分钟前
matlab官方免费下载安装超详细教程2025最新matlab安装教程(MATLAB R2024b)
java·开发语言·其他·matlab
小伍_Five21 分钟前
spark数据处理练习题详解【下】
java·大数据·spark·scala
L汐1 小时前
02 K8s双主安装
java·容器·kubernetes
jackson凌1 小时前
【Java学习笔记】【第一阶段项目实践】房屋出租系统(面向对象版本)
java·笔记·学习
带刺的坐椅1 小时前
Solon Ai Flow 编排开发框架发布预告(效果预览)
java·ai·solon·dify·solon-flow
2302_809798322 小时前
【JavaWeb】JDBC
java·开发语言·servlet
小刘不想改BUG2 小时前
LeetCode LCR 010 和为 K 的子数组 (Java)
java·算法·leetcode
MeyrlNotFound2 小时前
(二十一)Java集合框架源码深度解析
java·开发语言
正在走向自律3 小时前
2025年、2024年最新版IntelliJ IDEA下载安装过程(含Java环境搭建+Maven下载及配置)
java·jvm·jdk·maven·intellij-idea
不会就选C.3 小时前
【开源分享】健康饮食管理系统(双端+论文)
java·spring boot·开源·毕业设计