Are fields initialized before constructor code is run in Java?

Can anyone explain the output of following program? I thought constructors are initialized before instance variables. So I was expecting the output to be "XZYY".

class X {
Y b = new Y();


X() {
System.out.print("X");
}
}


class Y {
Y() {
System.out.print("Y");
}
}


public class Z extends X {
Y y = new Y();


Z() {
System.out.print("Z");
}


public static void main(String[] args) {
new Z();
}
}
26519 次浏览

If you look at the decompiled version of the class file

class X {
Y b;


X() {
b = new Y();
System.out.print("X");
}
}


class Y {
Y() {
System.out.print("Y");
}
}


public class Z extends X {


Y y;


Z() {
y = new Y();
System.out.print("Z");
}


public static void main(String args[]) {
new Z();
}
}

You can find that the instance variable y is moved inside the constructor, so the execution sequence is as follows

  1. Call the constructor of Z
  2. It triggers the default constructor of X
  3. First line of X constructor new Y() is called.
  4. Print Y
  5. Print X
  6. Call the first line in constructor Z new Y()
  7. Print Y
  8. Print Z

All the instance variables are initialized by using constructor statements.

The correct order of initialisation is:

  1. Static variable initialisers and static initialisation blocks, in textual order, if the class hasn't been previously initialised.
  2. The super() call in the constructor, whether explicit or implicit.
  3. Instance variable initialisers and instance initialisation blocks, in textual order.
  4. Remaining body of constructor after super().

See sections §2.17.5-6 of the Java Virtual Machine Specification.

To clarify the misconceptions with static - I will simply refer to this small piece of code:

public class Foo {


{
System.out.println("Instance Block 1");
}


static {
System.out.println("Static Block 1");
}


public static final Foo FOO = new Foo();


{
System.out.println("Instance Block 2");
}


static {
System.out.println("Static Block 2 (Weird!!)");
}


public Foo() {
System.out.println("Constructor");
}


public static void main(String[] args) {
System.out.println("In Main");
new Foo();
}
}

Surprise is that the output is as follows:

Static Block 1
Instance Block 1
Instance Block 2
Constructor
Static Block 2 (Weird!!)
In Main
Instance Block 1
Instance Block 2
Constructor

Note that we have a static {} that is called after two instance {}. this happens because we throw in the constructor in the middle, interjecting execution order the first time the constructor is called.

Discovered this when I was working on this answer - https://stackoverflow.com/a/30837385/744133.

Basically we observe this to happen:

  1. During the first time an object is initialized, Initialize current object for both static and instance initialization intermixed based on order of occurrence

  2. For all next initializations, only do the instance initialization in the order of occurrence, as static initialization already happened.

I need to research how the mix of inheritance, and both explicit and implicit calls to super and this will affect this, and will update with findings. It would be likely similar to the other supplied answers, except that they got it wrong with the static initialization.

When you invoke a constructor, the instance variable initializers run before the body of the constructor. What do you think the output of the below program ?

public class Tester {
private Tester internalInstance = new Tester();
public Tester() throws Exception {
throw new Exception("Boom");
}
public static void main(String[] args) {
try {
Tester b = new Tester();
System.out.println("Eye-Opener!");
} catch (Exception ex) {
System.out.println("Exception catched");
}
}
}

The main method invokes the Tester constructor, which throws an exception. You might expect the catch clause to catch this exception and print Exception catched. But if you tried running it, you found that it does nothing of that sort and It throws a StackOverflowError.

Observe the code:

public class CodeBlockSeq {
static int k = 0;
static protected String log(Class<?> cls, String message) {
return log(cls, "static", message);
}
static protected String log(Class<?> cls, String name, String message) {
System.out.println(k++ +":"+  cls.getSimpleName() +':'+ name +':'+ message );
return message;
}


static public class Hello {
final protected String name;


private static String s0 = CodeBlockSeq.log(Hello.class,"var 0");
private String a0 = log(Hello.class,"var 0");
static {
CodeBlockSeq.log(Hello.class, "block 0");
}
private static String s1 = CodeBlockSeq.log(Hello.class,"var 1");
private String a1 = log(Hello.class, "var 1");
{
log(Hello.class, "init block 1");
}


public Hello(String name) {
this.name = name;
log(Hello.class,"Constructor\n");
}
private static String s2 = CodeBlockSeq.log(Hello.class,"var 2");
private String a2 = log(Hello.class, "var 2");
{
log(Hello.class, "init block 2");
}
static {
CodeBlockSeq.log(Hello.class, "block 2");
}


private static String s3 = CodeBlockSeq.log(Hello.class,"var 3");
private String a3 = log(Hello.class, "var 3");
{
log(Hello.class, "init block 3");
}


private static String s4 = CodeBlockSeq.log(Hello.class, "var 4");


protected String log(Class<?> cls, String message) {
return CodeBlockSeq.log(cls, "Hello-"+name, message);
}


}


static public class Bello extends Hello {
//  static private Hello s0 = new Hello("jello");
private static String s0 = CodeBlockSeq.log(Bello.class,"var 0");
private String a0 = log(Bello.class, "var 0");
static {
CodeBlockSeq.log(Bello.class, "block 0");
}
private static String s1 = CodeBlockSeq.log(Bello.class,"var 1");
private String a1 = log(Bello.class, "var 1");
{
log(Bello.class, "init block 1");
}


public Bello(String name) {
super(name);
log(Bello.class, "Constructor\n");
}


private static String s2 = CodeBlockSeq.log(Bello.class,"var 2");
private String a2 = log(Bello.class, "var 2");
{
log(Bello.class, "init block 2");
}
static {
CodeBlockSeq.log(Bello.class, "block 2");
}


private static String s3 = CodeBlockSeq.log(Bello.class,"var 3");
private String a3 = log(Bello.class, "var 3");
{
log(Bello.class, "init block 3");
}


private static String s4 = CodeBlockSeq.log(Bello.class,"var 4");
public String getName() {
return this.name;
}
protected String log(Class<?> cls, String message) {
return null;
}
}


@Test
public void testIt() {
Hello x = new Bello("pollo") {
private String a1 = log(this.getClass(), "var 1");
{
log(this.getClass(), "init block 1");
}
private String a2 = log(this.getClass(), "var 2");
{
log(this.getClass(), "init block 2");
}
private String a3 = log(this.getClass(), "var 3");
{
log(this.getClass(), "init block 3");
}


protected String log(Class<?> cls, String message) {
return CodeBlockSeq.log(cls, "pollo-"+name, message);
}


};
}
}

Running the test would produce:

0:Hello:static:var 0
1:Hello:static:block 0
2:Hello:static:var 1
3:Hello:static:var 2
4:Hello:static:block 2
5:Hello:static:var 3
6:Hello:static:var 4
7:Bello:static:var 0
8:Bello:static:block 0
9:Bello:static:var 1
10:Bello:static:var 2
11:Bello:static:block 2
12:Bello:static:var 3
13:Bello:static:var 4
14:Hello:pollo-null:var 0
15:Hello:pollo-null:var 1
16:Hello:pollo-null:init block 1
17:Hello:pollo-null:var 2
18:Hello:pollo-null:init block 2
19:Hello:pollo-null:var 3
20:Hello:pollo-null:init block 3
21:Hello:pollo-pollo:Constructor


22:Bello:pollo-pollo:var 0
23:Bello:pollo-pollo:var 1
24:Bello:pollo-pollo:init block 1
25:Bello:pollo-pollo:var 2
26:Bello:pollo-pollo:init block 2
27:Bello:pollo-pollo:var 3
28:Bello:pollo-pollo:init block 3
29:Bello:pollo-pollo:Constructor


30::pollo-pollo:var 1
31::pollo-pollo:init block 1
32::pollo-pollo:var 2
33::pollo-pollo:init block 2
34::pollo-pollo:var 3
35::pollo-pollo:init block 3

Notice that they are run in this order:

  1. static blocks and objects of parent class
    • in the order that they are declared.
  2. static blocks and objects of extension class
    • in the order that they are declared.
  3. instance blocks and objects of parent class
    • in the order that they are declared.
  4. parent constructor
  5. instance blocks and objects of extension class
    • in the order that they are declared.
  6. extension class constructor
  7. instance blocks and objects of anonymous extension class
    • in the order that they are declared.

Notice that:

//  static private Hello s0 = new Hello("jello");
private static String s0 = CodeBlockSeq.log(Bello.class,"var 0");

Is a static object that is printed as the first line.

What if replace that with:

static private Hello s0 = new Hello("jello");
//  private static String s0 = CodeBlockSeq.log(Bello.class,"var 0");

So that it will print in this order

  1. static blocks and objects of parent class

    • in the order that they are declared.
  2. Then the first static member of extension class Bello

    • 2.1 static blocks and objects of jello's static class

      • But has already run in step 1, so was not done again.
    • 2.2 instance blocks and objects of jello instance

      • in the order that they are declared.
    • 2.3 constructor for jello instance

    • 2.4 The rest of the static blocks and objects of extension class

      • in the order that they are declared.

Just as it would have done for static String s0.

Then

  1. instance blocks and objects of parent class
    • in the order that they are declared.
  2. parent constructor
  3. instance blocks and objects of extension class
    • in the order that they are declared.
  4. extension class constructor
  5. instance blocks and objects of anonymous extension class
    • in the order that they are declared.

0:Hello:static:var 0
1:Hello:static:block 0
2:Hello:static:var 1
3:Hello:static:var 2
4:Hello:static:block 2
5:Hello:static:var 3
6:Hello:static:var 4
7:Hello:Hello-null:var 0
8:Hello:Hello-null:var 1
9:Hello:Hello-null:init block 1
10:Hello:Hello-null:var 2
11:Hello:Hello-null:init block 2
12:Hello:Hello-null:var 3
13:Hello:Hello-null:init block 3
14:Hello:Hello-jello:Constructor


15:Bello:static:block 0
16:Bello:static:var 1
17:Bello:static:var 2
18:Bello:static:block 2
19:Bello:static:var 3
20:Bello:static:var 4
21:Hello:pollo-null:var 0
22:Hello:pollo-null:var 1
23:Hello:pollo-null:init block 1
24:Hello:pollo-null:var 2
25:Hello:pollo-null:init block 2
26:Hello:pollo-null:var 3
27:Hello:pollo-null:init block 3
28:Hello:pollo-pollo:Constructor


29:Bello:pollo-pollo:var 0
30:Bello:pollo-pollo:var 1
31:Bello:pollo-pollo:init block 1
32:Bello:pollo-pollo:var 2
33:Bello:pollo-pollo:init block 2
34:Bello:pollo-pollo:var 3
35:Bello:pollo-pollo:init block 3
36:Bello:pollo-pollo:Constructor


37::pollo-pollo:var 1
38::pollo-pollo:init block 1
39::pollo-pollo:var 2
40::pollo-pollo:init block 2
41::pollo-pollo:var 3
42::pollo-pollo:init block 3

Just because the static object s0 changed from String to Hello class, the java runtime does not have built-in intelligence to, make an exception for and then, relegate s0 to the back of the execution queue. It does not matter what class s0 is.