我不明白为什么以下代码不会导致堆栈溢出等效于Heap(OutOfMemoryError或其他),因为它是某种无限递归(不是吗?)。静态初始化是否提供了这样的保护并改为抛出NullPointerException?
编辑:我不相信我的理解:
public static void main(String[] args)
从StaticClass
调用静态方法。在此方法之前,需要首先加载doSmth()
,称为StaticClass
。加载类时,运行所有静态代码;在StaticClass
班级中,objClass
是一个静态字段,归因于new ObjClass()
的值,因此不应为null
(抛出NullPointerException
)。显然new ObjClass()
无法实例化,因为为了做到这一点,你需要加载StaticClass
并且这已经在加载StaticClass
时发生(因此类比无限递归,或者可能是死锁类比)。问题是JVM说objClass
是null而不是说它无法初始化new ObjClass()
,因为某种类型的循环调用。
在正常情况下,由于调用者而抛出NullPointerException。但在这种情况下,您只能更改被调用者(在ObjClass
构造函数中删除末尾注释的行),然后您将不会收到NullPointerException
。
package pack;
public class ObjClass
{
public ObjClass() {
StaticClass.doSmth();//if removed, no NullPointerException
}
public String getSomething() {
return "get";
}
public static void main(String[] args) {
StaticClass.loadStaticClass();
}
}
class StaticClass {
private static ObjClass objClass = new ObjClass();
static void loadStaticClass() {
}
static void doSmth() {
System.out.println(objClass.getSomething());
}
}
给出:
Exception in thread "main" java.lang.ExceptionInInitializerError
at pack.ObjClass.main(ObjClass.java:28)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
Caused by: java.lang.NullPointerException
at pack.ObjClass$StaticClass.doSmth(ObjClass.java:39)
at pack.ObjClass.<init>(ObjClass.java:20)
at pack.ObjClass$StaticClass.<clinit>(ObjClass.java:34)
... 6 more
答案 0 :(得分:4)
无论你多么坚定,他们必须对这个问题有一个复杂的答案,非常简单。您得到NullPointerException,因为您取消引用了null
值。没什么。
public class Main {
public static void main(String... args) {
new A();
}
static class A {
static {
System.out.println("Start of class A initialisation, A.b is " + A.b);
}
static final B b = new B();
static void method(String from) {
System.out.println("Called a method in A from " + from + ", A.b is " + A.b);
B.method();
}
static {
System.out.println("End of class A initialisation, A.b is " + A.b);
}
}
static class B {
static {
System.out.println("Start of class B initialisation, A.b is " + A.b);
A.method("B static block");
System.out.println("End of class B initialisation, A.b is " + A.b);
}
B() {
A.method("B() constructor");
System.out.println("Only after this should A.b be set.");
}
static void method() {
System.out.println("Called a method in B, A.b is " + A.b);
}
}
}
打印
Start of class A initialisation, A.b is null
Start of class B initialisation, A.b is null
Called a method in A from B static block, A.b is null
Called a method in B, A.b is null
End of class B initialisation, A.b is null
Called a method in A from B() constructor, A.b is null
Called a method in B, A.b is null
Only after this should A.b be set.
End of class A initialisation, A.b is Main$B@33b7b32c
正如您所看到的,一个类可以在完成初始化之前调用另一个类,并且可以检查在初始化之前的引用,它将是null
,甚至是final
个。
由于尚未设置objClass
,因此抛出NullPointerException。
在从构造函数返回之前不会设置它,但是你在构造函数中,所以它的默认值为null
。
BTW,这与http://en.wiktionary.org/wiki/infinite_recursion
完全不同我不明白为什么以下代码不会导致堆栈溢出等效于堆(OutOfMemoryError或其他)
它们都是具有特定含义的不同错误。在未初始化的情况下使用引用时引发的错误是NullPointerException。
静态初始化是否提供了这样的保护并改为抛出NullPointerException?
字段不一定是静态的。如果在初始化之前访问非静态字段,则会发生同样的情况。
当加载StaticClass时,应该实例化objClass吗?
它没有加载,实际上它永远不会加载,因为它在完成之前抛出一个Exception。如果您在此之后尝试使用此类,则会因为类无法加载而获得NoClassDefError。
答案 1 :(得分:4)
引用StaticClass
实例化objClass
,调用ObjClass
构造函数,调用doSmth()
。这使用引用objClass
,但尚未分配,因为它需要一个完全构造的objClass
来引用。
简言之,
this.x = doMethod();
在将参考doMethod()
分配给结果之前,需要x
完成。
你想做什么?或者这是某种运动?
答案 2 :(得分:1)
private static ObjClass objClass = new ObjClass();
当类加载jvm尝试为ObjClass
创建实例时,引用objClass
指向null
被ObjClass
构造函数调用(实例创建尚未完成)。
您正在尝试拨打objClass.getSomething()
,在getSomething()
引用null
上调用导致NullPointerException
的内容。