Java中MyThread初始化抛NullPointerException,为何s未被初始化?
问题原因分析与解决方案
这是个很典型的Java实例初始化顺序问题,我来给你理清楚为什么会抛出NullPointerException:
核心问题:Java对象的初始化顺序和你的预期相反
当你调用new MyThread("name")创建对象时,JVM的执行流程是这样的:
- 先为
MyThread对象分配内存,所有实例变量会被默认初始化——也就是String s被设为null,char[] ch也被设为null。 - 接下来,JVM会按照实例变量在类中声明的顺序执行它们的初始化表达式。你的代码里
char[] ch = s.toCharArray();(也就是你标注的第1行)写在构造器之前,所以这一步会先执行。但此时构造器里的this.s = s还没运行,s仍然是null,调用null.toCharArray()自然就触发空指针异常了。 - 只有当所有实例变量的初始化都完成后,JVM才会执行构造器里的代码(包括给
s赋值和打印"Constructor")。
你误以为构造器会先给s赋值,之后才初始化ch,但实际顺序刚好相反——实例变量的初始化逻辑会先于构造器体的执行。
修复方案
有两种简单的方式可以解决这个问题:
方案1:把ch的初始化移到构造器中
这是最直观的方式,确保s已经被赋值后再初始化ch:
class MyThread implements Runnable { String s; char[] ch; // 仅声明,不提前初始化 MyThread(String s) { this.s = s; this.ch = s.toCharArray(); // 此时s已经有值了 System.out.println("Constructor"); } public void run() { for (char c : ch) { System.out.println(c); } } }
方案2:使用实例初始化块(适合多构造器复用场景)
如果你的类有多个构造器,不想重复写ch的初始化逻辑,可以用实例初始化块(不过还是建议配合构造器先给s赋值,避免空指针):
class MyThread implements Runnable { String s; char[] ch; MyThread(String s) { this.s = s; // 构造器内先赋值,再初始化ch this.ch = s.toCharArray(); System.out.println("Constructor"); } // 实例初始化块,会在构造器执行前运行,但这里依赖构造器先给s赋值 { // 可以在这里统一处理ch的初始化逻辑,适合多构造器场景 if (s != null) { ch = s.toCharArray(); } } public void run() { for (char c : ch) { System.out.println(c); } } }
总结
记住这个关键规则:类中声明的实例变量的初始化表达式,会在构造器体的代码执行之前运行。所以不要在实例变量的初始化逻辑中,依赖那些只有构造器才会赋值的变量,否则很容易踩空指针的坑。
内容的提问来源于stack exchange,提问作者springcloudlearner
相关产品推荐
相关产品推荐

