Search code examples
javainner-classesstatic-initializer

Between outer and inner classes, their static initializers run order is different when invoking main method inside or outside the outer class


I have the following code:

class Foo {
    static { //static initializer block
        System.out.print("Foo");
    }
    class Bar {
        static { //static initializer block
            System.out.print("Bar");
        }
    }

    public static void main(String[] args) { // => This will print "FooBar" to the console
        new Foo().new Bar();
    }
}

public class Main {

    public static void main(String[] args) { // This will print "BarFoo" to the console
        new Foo().new Bar();
    }
}

As the comments say, they will print different result on independent invocation of main methods. Why the placement of main method in this case affect the result printed to the screen?


Solution

  • new Foo().new Bar() is compiled to the following bytecode:

    NEW Foo$Bar
    DUP
    NEW Foo
    DUP
    INVOKESPECIAL Foo.<init> ()V
    INVOKESPECIAL Foo$Bar.<init> (LFoo;)V
    

    Note that it is Bar that is first created (though Foo's constructor is called first). The new instruction is what causes a class to be initialised (see spec), not the constructor call. The When a class is initialised, its static initialisers run (see spec).

    Just in case you are not aware, non-static inner classes are implemented by having the constructors take an extra parameter, which serves as the enclosing instance. new Foo().new Bar() is basically saying new Foo.Bar(new Foo()), but only the former syntax is valid.

    I'm not sure if such an output is behaviour guaranteed by the spec. It could technically have been compiled in a way such that NEW Foo occurs first, but that would require an additional variable, as if you wrote:

    var f = new Foo();
    f.new Bar();
    

    So when you put new Foo().new Bar() in an unrelated class, Bar gets initialised first, and so Bar is printed first.

    When you put new Foo().new Bar() in a static method declared in Foo, however, Foo will be initialised before any of the new instructions, hence Foo is printed first. This is because calling a static method in Foo causes Foo to be initialised.

    See all the conditions for a class or interface T to be initialised in the spec:

    • T is a class and an instance of T is created.

    • A static method declared by T is invoked.

    • A static field declared by T is assigned.

    • A static field declared by T is used and the field is not a constant variable (§4.12.4).

    Note that the first one refers to the execution of the new instruction, not the constructor call.