2

我不明白为什么下面的代码不会导致堆(OutOfMemoryError 或其他东西)的堆栈溢出等价物,因为它是某种无限递归(不是吗?)。静态初始化是否提供了对此类事情的防范并引发 NullPointerException?

编辑:我不相信我被理解: public static void main(String[] args)StaticClass. doSmth()在调用此方法之前,StaticClass需要先加载。当一个类被加载时,所有的静态代码都会运行;在StaticClass类中,objClass是一个带有 a 值的静态字段new ObjClass(),因此不应该是null(抛出NullPointerException)。显然new ObjClass()不能实例化,因为为了做到这一点,你需要加载StaticClass并且这在加载时已经发生StaticClass(因此与无限递归的类比,或者可能是死锁类比)。问题是 JVM 说它是空的,而不是说它因为某种循环调用objClass而无法初始化。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
4

3 回答 3

4

引用StaticClass实例化objClass,它调用ObjClass构造函数,它调用doSmth(). 这使用了 reference objClass,但尚未分配,因为它需要一个完全构造objClass的来引用。

简要地,

this.x = doMethod();

需要doMethod()在将引用x分配给结果之前完成。

你想做什么 ?或者这是某种运动?

于 2012-12-18T15:24:26.860 回答
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一个。


它抛出 NullPointerException 因为objClass尚未设置。

在您从构造函数返回之前不会设置它,但您在构造函数中,因此它的默认值为null.

顺便说一句,这与http://en.wiktionary.org/wiki/infinite_recursion完全不同

我不明白为什么下面的代码不是导致堆等效的堆栈溢出(OutOfMemoryError 或其他东西)

它们都是具有特定含义的不同错误。在未初始化的情况下使用引用时抛出的错误是 NullPointerException。

静态初始化是否提供了对此类事情的防范并引发 NullPointerException?

字段不必是静态的。如果在初始化之前访问它们,非静态字段也会发生同样的情况。

加载StaticClass时应该实例化objClass吗?

它还没有加载,实际上它永远不会加载,因为它在完成之前抛出了一个异常。如果您在此之后尝试在哪里使用此类,您将收到 NoClassDefError,因为该类无法加载。

于 2012-12-18T15:23:57.157 回答
1
  private static ObjClass objClass = new ObjClass();

当类加载 jvm 尝试创建实例时ObjClass,引用objClass指向null的时间ObjClass构造函数调用(实例创建尚未完成)。\

您正在尝试调用objClass.getSomething(),您正在调用导致getSomething()的参考。nullNullPointerException

于 2012-12-18T15:23:25.673 回答