繁体   English   中英

对象/静态创建中的无限递归/为什么此代码抛出NullPointerException?

[英]Infinite recursion in object/static creation/Why is this code throwing NullPointerException?

我不明白为什么以下代码不会导致堆栈溢出等效于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

无论你多么坚定,他们必须对这个问题有一个复杂的答案,非常简单。 你得到一个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

BTW,这与http://en.wiktionary.org/wiki/infinite_recursion完全不同

我不明白为什么以下代码不是这导致堆栈溢出相当于Heap(OutOfMemoryError或其他)

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

静态初始化是否提供了这样的保护,并抛出NullPointerException?

对于这种情况,字段不必是静态的。 如果在初始化之前访问非静态字段,则会发生同样的情况。

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

它没有加载,实际上它永远不会加载,因为它在它完成之前抛出一个Exception。 如果您在此之后尝试使用此类,则会因为类无法加载而获得NoClassDefError。

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

简单地说,

this.x = doMethod();

要求在将引用x赋值给结果之前完成doMethod()

你想做什么 ? 还是这种运动?

  private static ObjClass objClass = new ObjClass();

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

您正在尝试调用objClass.getSomething() ,您在null引用上调用getSomething() ,这会导致NullPointerException

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM