Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I do not understand why the following code isn't this a causing a Stack overflow equivalent for Heap (OutOfMemoryError or something) since it is some kind of infinite recursion (is it not?). Does the static initialization provide a guard against something like this and throws NullPointerException instead?

Edit: I do not believe I was understood: public static void main(String[] args) calls a static method from StaticClass. Before this method, doSmth(), is called StaticClass needs to be loaded first. When a class is loaded, all the static code is ran; in the StaticClass class, objClass is a static field attributed with the value of a new ObjClass() and thus shouldn't be null (to throw NullPointerException). Obviously new ObjClass() can not be instantiated because in order to do this you need to load the StaticClass and this already happens when StaticClass is loading (thus the analogy with infinite recursion, or probably deadlock analogy). The problem is the JVM says the objClass is null instead of saying it couldn't initialize new ObjClass() because some kind of recurrent call.

In a normal case, NullPointerException is thrown because of the caller. But in this case, you can change only the callee (remove the line with the comment at the end, in the ObjClass constructor) and then you won't receive 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());
    }
}

which gives:

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
like image 722
Random42 Avatar asked Mar 31 '26 14:03

Random42


2 Answers

Referencing StaticClass instantiates objClass, which calls the ObjClass constructor, which calls doSmth(). This uses the reference objClass, but that's not been assigned yet, since it needs a fully constructed objClass to refer to.

Briefly,

this.x = doMethod();

requires doMethod() to complete before the reference x is assigned to the result.

What are you trying to do ? Or is this some sort of exercise ?

like image 117
Brian Agnew Avatar answered Apr 03 '26 03:04

Brian Agnew


No matter how determined you are they there must be a complicated answer to this problem, is very simple. You get a NullPointerException because you dereferenced a null value. Nothing more.

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);
        }
    }
}

prints

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

As you can see, one class can call another before it has finished initialising and a reference before it has been initialised can be examined and it will be null, even a final one.


It's throwing a NullPointerException because objClass hasn't been set yet.

It won't be set until you return from the constructor, but you are in the constructor, so it will have it's default value of null.

BTW, This is nothing like http://en.wiktionary.org/wiki/infinite_recursion

I do not understand why the following code isn't this a causing a Stack overflow equivalent for Heap (OutOfMemoryError or something)

They are all different errors with specific meanings. The error thrown when a reference is used without being initialised is NullPointerException.

Does the static initialization provide a guard against something like this and throws NullPointerException instead?

The fields doesn't have to be static for this to happen. The same happens to non-static fields if you access them before they have been initialised.

objClass should be instantiated when StaticClass is loaded?

It hasn't loaded, in fact it never loads because it throws an Exception before it completes. If you where to try to use this class after this, you would get a NoClassDefError as the class failed to load.

like image 22
Peter Lawrey Avatar answered Apr 03 '26 05:04

Peter Lawrey



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!