【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 枚举饿汉式单例
相关推荐
_jacobfu4 分钟前
Foundry 学习笔记 005
笔记·学习·web3·区块链·智能合约
永日456705 分钟前
学习日记-spring-day45-7.10
java·学习·spring
小屁孩大帅-杨一凡1 小时前
如何解决ThreadLocal内存泄漏问题?
java·开发语言·jvm·算法
HXR_plume1 小时前
【计算机网络】王道考研笔记整理(2)物理层
笔记·计算机网络·考研
学习3人组2 小时前
在 IntelliJ IDEA 系列中phpstorm2025设置中文界面
java·ide·intellij-idea
快乐点吧2 小时前
【前端】异步任务风控验证与轮询机制技术方案(通用笔记版)
前端·笔记
cainiao0806053 小时前
Java 大视界:基于 Java 的大数据可视化在智慧城市能源消耗动态监测与优化决策中的应用(2025 实战全景)
java
长风破浪会有时呀4 小时前
记一次接口优化历程 CountDownLatch
java
极光雨雨4 小时前
【设计模式】单例模式 饿汉式单例与懒汉式单例
单例模式·设计模式
云朵大王4 小时前
SQL 视图与事务知识点详解及练习题
java·大数据·数据库