【Java】单例模式

单例模式

所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。

单例模式包含懒汉式和饿汉式,运行有且仅有一个实例化对象,只会new一次,两者区别在于何时new一个对象

原理:

如果我们要让类在一个虚拟机中只能产生一个对象,我们首先必须将类的构造方法的访问权限设置为private,这样,就不能用new操作符在类的外部产生类的对象了,但在类内部仍可以产生该类的对象。因为在类的外部开始还无法得到类的对象,只能调用该类的某个静态方法以返回类内部创建的对象,静态方法只能访问类中的静态成员变量,所以,指向类内部产生的该类对象的变量也必须定义成静态的。

实例化对象的创建要消耗大量的时间和资源

在整个软件系统运行过程中,这个类只被实例化一次,以后不论在哪都只调用这一个实例

要求:

  • 掌握五种单例模式的实现方式
  • 理解为何 DCL 实现时要使用 volatile 修饰静态变量
  • 了解 jdk 中用到单例的场景

饿汉式

在类加载之后先通过new关键字创建一个对象,后续调用getInstance()方法时直接返回该对象

java 复制代码
public class Singleton implements Serializable {
    // 构造方法私有化,不能通过new关键字来创建对象
    private Singleton() {
        // 构造方法抛出异常是防止反射破坏单例
        if (INSTANCE != null) {
            throw new RuntimeException("单例对象不能重复创建");
        }
        System.out.println("private Singleton()");
    }

    // 私有,静态,不可变
    private static final Singleton INSTANCE = new Singleton();

    public static Singleton getInstance() {
        return INSTANCE;
    }

    public static void otherMethod() {
        System.out.println("otherMethod()");
    }

    // 防止反序列化破坏单例
    public Object readResolve() {
        return INSTANCE;
    }
}

枚举饿汉式

枚举饿汉式能天然防止反射、反序列化破坏单例

java 复制代码
public enum Singleton {
    INSTANCE;

    private Singleton() {
        System.out.println("private Singleton()");
    }

    @Override
    public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

    public static Singleton getInstance() {
        return INSTANCE;
    }

    public static void otherMethod() {
        System.out.println("otherMethod()");
    }
}

懒汉式

在第一次调用getInstance()方法时通过new创建对象,以后再次调用该方法时,直接返回第一次调用时创建的对象

java 复制代码
public class Singleton implements Serializable {
    private Singleton() {
        System.out.println("private Singleton()");
    }

    private static Singleton INSTANCE = null;

    // 同步执行,避免线程问题
    public static synchronized Singleton getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new Singleton();
        }
        return INSTANCE;
    }

    public static void otherMethod() {
        System.out.println("otherMethod()");
    }

}

其实只有首次创建单例对象时才需要同步,但该代码实际上每次调用都会同步,因此有了下面的双检锁改进

双检锁懒汉式

java 复制代码
public class Singleton implements Serializable {
    private Singleton() {
        System.out.println("private Singleton()");
    }

    private static volatile Singleton INSTANCE = null; // 可见性,有序性

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

    public static void otherMethod() {
        System.out.println("otherMethod()");
    }
}

为何必须加 volatile:

  • INSTANCE = new Singleton4() 不是原子的,分成 3 步:创建对象、调用构造、给静态变量赋值,其中后两步可能被指令重排序优化,变成先赋值、再调用构造
  • 如果线程1 先执行了赋值,线程2 执行到第一个 INSTANCE == null 时发现 INSTANCE 已经不为 null,此时就会返回一个未完全构造的对象

内部类懒汉式

java 复制代码
public class Singleton implements Serializable {
    private Singleton() {
        System.out.println("private Singleton()");
    }

    private static class Holder {
        static Singleton INSTANCE = new Singleton();
    }

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

    public static void otherMethod() {
        System.out.println("otherMethod()");
    }
}
  • 避免了双检锁的缺点

实例

JDK 中单例的体现

  • Runtime 体现了饿汉式单例
  • Console 体现了双检锁懒汉式单例
  • Collections 中的 EmptyNavigableSet 内部类懒汉式单例
  • ReverseComparator.REVERSE_ORDER 内部类懒汉式单例
  • Comparators.NaturalOrderComparator.INSTANCE 枚举饿汉式单例
相关推荐
菜鸟一枚在这14 分钟前
深度解析建造者模式:复杂对象构建的优雅之道
java·开发语言·算法
gyeolhada32 分钟前
2025蓝桥杯JAVA编程题练习Day5
java·数据结构·算法·蓝桥杯
菜鸟一枚在这34 分钟前
深入理解设计模式之代理模式
java·设计模式·代理模式
sealaugh321 小时前
aws(学习笔记第二十九课) aws cloudfront hands on
笔记·学习·aws
小天努力学java1 小时前
【面试系列】Java开发--AI常见面试题
java·人工智能·面试
river661 小时前
java开发——为什么要使用动态代理?
java
Zayn~1 小时前
JVM系列--虚拟机类加载机制
java
FakeOccupational1 小时前
【计算社会学】 多智能体建模 ABM Agent Based Modeling 笔记
笔记
m0_748248021 小时前
Redis使用手册
java
CoderCodingNo1 小时前
【GESP】C++二级真题 luogu-b3924, [GESP202312 二级] 小杨的H字矩阵
java·c++·矩阵