1

I am trying to understand initialization order of Java class. Specifically when and in what order are static and Instance initializer/fields are executed. I came up with example as in this stackoverflow Question. Why does adding static to the self constructor invocation stop the code from going into recursion.

public class Test {
    public static void main(String a[]) {
        Cons1 c1 = new Cons1();
    }
}

class Cons1 {
    static Cons1 c = new Cons1(); /* if static is removed then recursion 
                                    occurs */
    Cons1() {
         //does something
    }
}

Is there any specific reason for this difference in behavior between static and instance context. I went through the Java doc Detailed Initialization Procedure, but unable to understand what is the logic behind such behavior. Any explanation or reference to JLS specs will be helpful.

PS : I have gone through this Similar stackoverflow post, However I cannot get my answer from there.

Andrew Tobilko
  • 44,067
  • 12
  • 74
  • 128
Mayank Madhav
  • 177
  • 2
  • 14
  • Basically the same reason [as here](https://stackoverflow.com/a/49709741/2071828). You need to understand what `static` means. – Boris the Spider Apr 14 '18 at 21:55
  • 1
    This is because a [static field](https://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.3.1.1) is incarnated only once - when the class is initialized. – Oleksandr Pyrohov Apr 14 '18 at 22:01
  • Does 'incarnated' here mean assign the rhs value to the reference c?. If so this variable can be incarnated only when we successfully create the object Cons1. So when new Cons1() is called still the full statement has not completed the execution. Does it not mean that the static variable is not incarnated yet. And hence upon object creation this statement will be executed again? – Mayank Madhav Apr 14 '18 at 22:07
  • 1
    It means "Execute the class variable initializers". – Oleksandr Pyrohov Apr 14 '18 at 22:26

1 Answers1

2

If a field is declared static, there exists exactly one incarnation of the field, no matter how many instances (possibly zero) of the class may eventually be created. A static field, sometimes called a class variable, is incarnated when the class is initialized (§12.4).

JLS 10 - 8.3.1.1. static Fields

On the other hand,

If the declarator is for an instance variable (that is, a field that is not static), then the following rules apply to its initializer:

  • At runtime, the initializer is evaluated and the assignment performed each time an instance of the class is created.

JLS 10 - 8.3.2. Field Initialization


Let's add a println statement to see the whole picture:

class Cons1 {
    static Cons1 c = new Cons1();

    Cons1() {
        System.out.println("the constructor was called");
    }

    public static void main(String[] args) {
        Cons1 c1 = new Cons1();
        Cons1 c2 = new Cons1();
    }
}

It outputs "the constructor was called" three times:

1 - when the class was loaded and the static field c got initialised;
2 - when c1 was created;
3 - when c2 was created.

Now we will compare that with the example with an instance field:

class Cons1 {
    Cons1 c = new Cons1();

    Cons1() {
        System.out.println("the constructor was called");
    }

    public static void main(String[] args) {
        Cons1 c1 = new Cons1();
    }
}

Obviously, it fails with the next stacktrace printing nothing:

Exception in thread "main" java.lang.StackOverflowError
    at Cons1.<init>(Cons1.java:33)
    ...
    at Cons1.<init>(Cons1.java:33)

The reason is that each instance of Cons1 requires another Cons1 object. So we overflow the call stack pushing Cons1.<init> methods in it. As a result, we end up with the exception when the stack reaches its max-allowed size.

Community
  • 1
  • 1
Andrew Tobilko
  • 44,067
  • 12
  • 74
  • 128