Skip to content

Instantly share code, notes, and snippets.

@battis
Last active December 2, 2021 06:48
Show Gist options
  • Save battis/82064eda3da7736e8f57616e81e9fee1 to your computer and use it in GitHub Desktop.
Save battis/82064eda3da7736e8f57616e81e9fee1 to your computer and use it in GitHub Desktop.
Java initialization order of operations

Initializing fields in Java

There are four ways that a field can be initialized in Java. These are, in order:

  1. As part of a static initializer block
  2. As part of an instance initializer block
  3. Default value in declaration.
  4. In the constructor.

Static initializer block

Used when a static field needs to be initialized to a more complex value than can be expressed in a single line.

public class Example {
  private static int x;
  
  // static initializer block
  static {
    int prev = 1;
    x = 1;
    for (int i = 2; i < 100; i++) {
      prev = x;
      x = (x + prev);
    }
    // x initialized to the 100th Fibonacci number
  }
}

Instance initializer block

Same purpose as the static initializer block, but for instance fields.

public class Example {
  private int x;
  
  // instance inititalizer block
  {
    String s = "The quick brown fox jumps over the lazy dog.");
      String s = "The quick brown fox jumps over the lazy dog.";
      Map<Character, Integer> chars = new HashMap<>();
      for(char c : s.toCharArray()) {
        if (chars.containsKey(c)) {
            chars.put(c, chars.get(c) + 1);
        } else {
            chars.put(c, 1);
        }
      }
      x = chars.size(); // the number of distinct characters in s (29, in this case)
    }
  }

Default value in declaration

A common practice, these initializations, while defined in the field declaration are, when compiled, prepended to the constructor method itself.

public class Example {
private int x = 3;
}

Meaning that the above declaration is, in most cases, equivalent to...

Initialization in constructor

public class Example {
  private int x;
  
  public Example() {
    x = 3;
  }
}

Where things get complicated...

However, in the edge case demonstrated in this gist, a subclass declares a private instance variable that is referred to in its implementation of an abstract method declared in its superclass... and the supercalass calls that method from its own constructor. Meaning that the default value of a local field may appear to be applied later than anticipated.

The expected output of this gist is, in fact:

Superclass static initializer block
Subclass static initializer block
Superclass instance initializer block
Superclass constructor
Subclass implementation of method(), called by Superclass constructor
  x = 0
Subclass instance initializer block
Subclass constructor
Subclass implementation of method(), called by main()
  x = 1
public class Subclass extends Superclass {
static {
System.out.println("Subclass static initializer block");
}
{
System.out.println("Subclass instance initializer block");
}
private int x = 1;
public Subclass() {
super(); // must be first line of constructor
System.out.println("Subclass constructor");
}
@Override
void method(String caller) {
System.out.println("Subclass implementation of method(), called by " + caller);
System.out.println(" x = " + x);
}
public static void main(String[] args) {
Subclass s = new Subclass();
s.method("main()");
}
}
public abstract class Superclass {
static {
System.out.println("Superclass static initializer block");
}
{
System.out.println("Superclass instance initializer block");
}
public Superclass() {
System.out.println("Superclass constructor");
method("Superclass constructor");
}
abstract void method(String caller);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment