静态方法本身不能直接实现延迟加载,但可通过访问延迟初始化的单例实例来实现;推荐用静态内部类方式,利用JVM类加载机制保证线程安全与天然延迟;也可用volatile+双重检查锁,适用于需灵活初始化的场景;直接在静态方法中new对象会破坏单例性;static代码块属饿汉式,不满足延迟加载要求。

静态方法本身不能直接实现单例的“延迟加载”,但可以通过静态方法来**访问**一个延迟初始化的单例实例——关键在于把实例的创建逻辑放到首次调用时才执行,而不是在类加载时就初始化。
用静态内部类实现延迟加载
这是最推荐、线程安全且天然延迟的方式。外部类加载时,静态内部类不会被加载,只有首次调用 getInstance() 时,JVM 才会加载并初始化内部类,从而初始化单例实例。
public class Singleton {
private Singleton() {}
private static class Holder {
static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return Holder.INSTANCE;
}
}
- 构造函数私有,防止外部 new
- Holder 类是静态的、私有的,仅用于持有实例
- getInstance() 是静态方法,提供对外访问入口
- JVM 类加载机制保证线程安全和延迟加载(无需 synchronized)
用 volatile + 双重检查锁(DCL)
适用于需要更灵活初始化逻辑(比如带参数构造、初始化耗时操作)的场景,但需注意 volatile 和同步细节,否则可能因指令重排序导致问题。
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;
}
}
- 第一次调用 getInstance() 时才创建实例(延迟)
- volatile 防止 instance 引用被重排序,确保其他线程看到完全构造的对象
- 外层 null 检查减少同步开销;内层再次检查避免多线程重复创建
为什么不能直接在静态方法里 new?
如果写成这样:public static Singleton getInstance() { return new Singleton(); }
每次调用都新建对象,不是单例。真正的单例必须复用同一个实例,所以必须借助静态字段保存唯一引用,并控制其初始化时机。
立即学习“Java免费学习笔记(深入)”;
- 静态字段(如 instance)是存储单例对象的容器
- 静态方法只是触发初始化和返回该字段的“门面”
- 延迟与否,取决于这个字段何时被赋值,而非方法是否静态
不建议用 static 代码块初始化
虽然也能实现单例,但它是类加载时就执行,属于**饿汉式**,不满足延迟加载要求:
public class Singleton {
private static final Singleton instance = new Singleton(); // 类加载即初始化
private Singleton() {}
public static Singleton getInstance() { return instance; }
}
- 无论是否调用 getInstance(),只要该类被主动使用(如首次引用),就会初始化
- 无法做到“首次调用 getInstance() 时才创建”
- 若构造耗时或依赖未就绪资源,可能引发问题


















