本文目录导读:

Java 静态内部类实现单例模式是线程安全且延迟加载的经典做法,它利用了 Java 类加载机制(类加载时才会初始化静态内部类)和 final 字段的线程安全性。
以下是具体的案例代码及详细解释:
标准写法
public class Singleton {
// 1. 私有化构造方法,防止外部直接 new
private Singleton() {
// 可以在这里做一些初始化操作
System.out.println("Singleton 实例被创建");
}
// 2. 公有的静态获取实例方法
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
// 3. 静态内部类(核心)
// 只有当 getInstance() 方法被调用时,此类才会被 JVM 加载
private static class SingletonHolder {
// 4. 在静态内部类中持有外部类的唯一实例
// 该变量在类加载时被初始化,且由 JVM 保证线程安全性
private static final Singleton INSTANCE = new Singleton();
}
// 为了演示效果,添加一个业务方法
public void doSomething() {
System.out.println("执行单例对象的方法");
}
}
客户端测试代码
public class Main {
public static void main(String[] args) {
// 验证是否为同一个对象
Singleton instance1 = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println("instance1 == instance2: " + (instance1 == instance2)); // 输出 true
// 测试方法调用
instance1.doSomething();
}
}
进阶:带参数的单例
单例通常是无状态的,但有时需要传递配置参数,静态内部类也可以支持:
public class ConfigSingleton {
private final String value;
// 私有构造方法带参数
private ConfigSingleton(String value) {
this.value = value;
}
public static ConfigSingleton getInstance(String configValue) {
// 避免在第一次 getInstance() 时传入不同的值
return SingletonHolder.INSTANCE;
}
// 注意:这种方式第一次初始化后,参数就固定了
public String getValue() {
return value;
}
private static class SingletonHolder {
// 需要你预先知道默认值,或者通过某种方式设置
private static final ConfigSingleton INSTANCE = new ConfigSingleton("defaultConfig");
}
}
为什么这种方式是线程安全的?
- 类加载机制:
SingletonHolder是一个内部类,只有在getInstance()方法被第一次调用时,JVM 才会加载SingletonHolder类。 - 初始化锁:JVM 在类加载期间会获取一个初始化锁,保证类的
<clinit>()方法(包含静态字段初始化和静态代码块)只会被一个线程执行一次。 final关键字:INSTANCE被static final修饰,保证了可见性和不可变性,所有线程看到的一定是初始化完全的对象。
对比其他单例模式
| 特点 | 饿汉式 | 懒汉式(DCL双重检查锁定) | 静态内部类 |
|---|---|---|---|
| 延迟加载 | ❌ (类加载时就创建) | ✅ | ✅ |
| 线程安全 | ✅ | ✅ (需小心实现) | ✅ (最简洁安全) |
| 实现复杂度 | 低 | 中(需用 volatile + synchronized) | 低 |
| 可能的问题 | 浪费内存 | 代码易出错 | 无法传参(初始化后) |
- 优点:代码清晰,性能高,无锁开销,线程绝对安全。
- 适用场景:大多数需要延迟加载的单例场景。
- 最佳实践:不需要考虑参数传递的通用单例,推荐直接使用这种写法。