单例与工厂模式下空指针异常原因排查求助
分析与解决:Singleton构造函数触发的空指针异常
我来帮你拆解这个棘手的问题——你遇到的空指针异常,根源在于类初始化阶段的循环依赖,以及静态变量的赋值时机。咱们一步步梳理整个执行流程,你就能明白为啥会出现null了:
问题触发的完整流程
- 当
Main类调用Singleton.getInstance()时,Singleton类还未被初始化,JVM开始初始化它:- JVM先为
Singleton的静态变量INSTANCE分配内存,默认值为null。 - 接着执行
INSTANCE = new Singleton():先创建Singleton实例(分配内存,实例变量value默认null),然后调用构造函数。
- JVM先为
Singleton构造函数执行:- 先调用
configureSingleton(),把当前实例的value设为"TEST"(注意:此时只是当前实例的value有值,但静态变量INSTANCE仍然是null,因为new Singleton()的返回值还没赋值给INSTANCE!) - 然后调用
Factory.build(),触发Factory类的初始化。
- 先调用
Factory类初始化时执行静态块:- 调用
Singleton.getInstance(),此时Singleton的静态变量INSTANCE还没被赋值(因为Singleton的构造函数还没执行完,INSTANCE = new Singleton()的赋值步骤还没完成),所以返回null。 - 接着调用
instance.getValue(),自然就抛出NullPointerException了!
- 调用
解决方案
针对这个循环初始化的问题,有几个简单的修复方案,你可以根据场景选择:
方案1:把Factory静态块的逻辑移到build()方法内
静态块会在类初始化时自动执行,这正是问题的导火索。把逻辑移到build()方法里,就能确保只有当Singleton完全初始化后,才会执行这段代码:
public class Factory { private Factory() { } public static void build() { Singleton instance = Singleton.getInstance(); System.out.println(instance.getValue()); System.out.println("Building now"); } }
方案2:修改Singleton构造函数,移除对Factory.build()的调用
把Factory.build()的调用从Singleton构造函数中移到Main类或者其他合适的地方,避免在Singleton初始化过程中触发Factory的初始化:
public class Singleton { private static final Singleton INSTANCE = new Singleton(); private String value; public static Singleton getInstance() { return INSTANCE; } private Singleton() { configureSingleton(); // 移除这里的Factory.build()调用 } // ... 其他方法不变 } // Main类修改: public class Main { public static void main(String[] args) { Singleton.getInstance().getValue(); Factory.build(); } }
方案3:改用静态内部类实现单例(推荐)
这种方式能避免饿汉式单例在初始化阶段的循环依赖问题,因为静态内部类只会在第一次被访问时才会初始化:
public class Singleton { private String value; private Singleton() { configureSingleton(); // 这里即使调用Factory.build()也不会有问题,因为INSTANCE已经初始化完成 } // 静态内部类,只有当getInstance()被调用时才会加载 private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } public static Singleton getInstance() { return SingletonHolder.INSTANCE; } private void configureSingleton() { value = "TEST"; } public String getValue() { return value; } public void setValue(String value) { this.value = value; } }
内容的提问来源于stack exchange,提问作者Hauke
相关产品推荐
相关产品推荐

