Object Initialization Order in Java
An object is a chunk of memory bundled with the code that manipulates memory. In the memory, the object maintains its state (the values of its instance variables), which can change and evolve throughout its lifetime. To get a newly-created object off to a good start, its newly-allocated memory must be initialized to a proper initial state. Here we take an in-depth look at the mechanisms Java uses to manage object initialization.
Note: For brevity, several examples below place more than one class in a single code block. In real Java source, each
publictop-level class must live in its own file named after the class, so treat the helper classes (Bowl,Pan,BowlTest, and so on) as separate.javafiles.
Java’s Initialization Mechanisms
At the beginning of an object’s life, the Java virtual machine (JVM) allocates enough memory on the heap to accommodate the object’s instance variables. When that memory is first allocated, however, the data it contains is unpredictable. If the memory were used as is, the behavior of the object would also be unpredictable. To guard against such a scenario, Java makes certain that memory is initialized, at least to predictable default values, before it is used by any code.
Java initializes objects at two levels:
- Class level (run once, when the JVM loads the class): static variable initializers and static initialization blocks.
- Instance level (run every time an object is created): instance variable initializers, instance initialization blocks, and constructors.
All of these result in Java code that is executed automatically. When you allocate memory for a new object with the new operator (or reflectively, via Constructor.newInstance(); the older Class.newInstance() has been deprecated since Java 9), the JVM ensures that the relevant initialization code runs before you can use the newly-allocated memory. If you design your classes so that initializers and constructors always produce a valid state for newly-created objects, there is no way for anyone to create and use an object that isn’t properly initialized.
The rest of this post walks through each mechanism in turn, then shows how they combine, both within a single class and across an inheritance hierarchy.
Default Initial Values
If you provide no explicit initialization to instance variables, they will be awarded predictable default initial values, which are based only on the type of the variable. The table below shows the default initial values for each of the variable types. (These are the default initial values for both instance and class variables. Local variables are not given default initial values. They must be initialized explicitly before they are used.)
| Type | Default Value |
|---|---|
| boolean | false |
| byte | 0 |
| char | \u0000 |
| short | 0 |
| int | 0 |
| long | 0L |
| float | 0.0f |
| double | 0.0d |
| Object Reference | null |
If you don’t explicitly initialize an instance variable, that variable will retain its default initial value when new returns its object reference.
Instance Variable Initializers and Constructors
In the source file, a constructor looks like a method declaration in which the method has the same name as the class but has no return type. For example, here is a constructor declaration for class Bowl:
public class Bowl {
public Bowl() {
System.out.println(value + "\t" + obj);
value = 256;
obj = new Object();
System.out.println(value + "\t" + obj);
}
private int value;
private Object obj;
}
public class BowlTest {
public static void main(String[] args) {
Bowl bowl = new Bowl();
}
}
Here’s the output:
0 null
256 java.lang.Object@dc6ecd
The first line of output shows the instance variables at their default values (0 and null) before the constructor body assigns them; the second line shows the values after assignment. In other words, default initialization happens before any constructor code runs.
Static Variables
When the data is static, the same thing happens: if it’s a primitive and you don’t initialize it, it gets the standard primitive default value; if it’s a reference to an object, it’s null unless you create a new object and attach your reference to it.
If you want to place initialization at the point of definition, it looks the same as for non-statics. There’s only a single piece of storage for a static, regardless of how many objects are created. But when does the static storage get initialized? An example makes this question clear:
public class Bowl {
public Bowl() {
System.out.println("The constructor of Bowl is invoked");
}
}
public class CupBoard {
public CupBoard() {
System.out.println("The constructor of CupBoard is invoked");
}
private static Bowl bowl = new Bowl();
}
public class CupBoardTest {
public static void main(String[] args) {
CupBoard cupBoard = new CupBoard();
cupBoard = new CupBoard();
}
}
Here’s the output:
The constructor of Bowl is invoked
The constructor of CupBoard is invoked
The constructor of CupBoard is invoked
The static field bowl is initialized only once, when the CupBoard class is first loaded, even though we create two CupBoard objects. That’s why Bowl’s constructor runs a single time while CupBoard’s constructor runs twice.
Static Initialization Blocks
A class can have any number of static initialization blocks, and they can appear anywhere in the class body. The runtime system guarantees that static initializers (both static variable initializers and static blocks) run in the order they appear in the source code. And don’t forget: this code runs when the JVM loads the class, before any instance is created. Let’s see an example.
public class Bowl {
public Bowl(int index) {
System.out.println("The constructor of Bowl invoked #" + index);
}
}
public class CupBoard {
private static Bowl bowl0 = new Bowl(0);
static {
Bowl bowl1 = new Bowl(1);
}
public CupBoard() {
System.out.println("This is constructor");
}
private static Bowl bowl2 = new Bowl(2);
private Bowl bowl4 = new Bowl(4);
static {
Bowl bowl3 = new Bowl(3);
}
public static void main(String[] args) {
CupBoard cupBoard = new CupBoard();
}
}
Here’s the output:
The constructor of Bowl invoked #0
The constructor of Bowl invoked #1
The constructor of Bowl invoked #2
The constructor of Bowl invoked #3
The constructor of Bowl invoked #4
This is constructor
Notice that bowl0 through bowl3 (static fields and static blocks, interleaved) all run at class-load time in source order, before the instance field bowl4 and the constructor body.
Static vs. Instance Initialization Order
Because static initializers run when the class is loaded and instance initializers run when an object is created, the statics always finish first. The following example makes the ordering explicit:
public class Bowl {
public Bowl() {
System.out.println("The constructor of Bowl is invoked");
}
}
public class Pan {
public Pan() {
System.out.println("The constructor of Pan is invoked");
}
}
public class CupBoard {
public CupBoard() {
System.out.println("The constructor of CupBoard is invoked");
}
private Pan pan = new Pan();
private static Bowl bowl = new Bowl();
}
public class CupBoardTest {
public static void main(String[] args) {
CupBoard cupBoard = new CupBoard();
}
}
Here’s the output:
The constructor of Bowl is invoked
The constructor of Pan is invoked
The constructor of CupBoard is invoked
Even though the instance field pan is declared before the static field bowl, bowl is initialized first: static initialization happens once, at class-load time, before any instance initialization begins. Only then does the instance field pan initialize, followed by the constructor body.
Instance Initialization Blocks
Like static blocks, a class can have instance initialization blocks, written as a bare { ... } block in the class body. Instance block code runs every time an object is created, right after the static initialization (which runs only once) and just before the constructor body. Here’s an example:
public class Bowl {
static {
System.out.println("Static Initialize Block Reached");
}
{
System.out.println("Initialize Block Reached");
}
public Bowl() {
System.out.println("The constructor of Bowl invoked");
}
}
public class BowlTest {
public static void main(String[] args) {
Bowl bowl0 = new Bowl();
Bowl bowl1 = new Bowl();
}
}
Static Initialize Block Reached
Initialize Block Reached
The constructor of Bowl invoked
Initialize Block Reached
The constructor of Bowl invoked
The static block runs a single time, when the class is loaded; for each of the two Bowl objects, the instance block runs first, then the constructor.
Initialization Order for Classes with Inheritance
The general order of initialization for classes with inheritance is:
- First, parent static items, then child static items
- Then parent instance variables, followed by the parent constructor
- Then child instance variables, followed by the child constructor
public class Bowl {
public Bowl(int index) {
System.out.println("The constructor of Bowl invoked #" + index);
}
}
public class Base {
static Bowl bowl0 = new Bowl(0);
public Base() {
Bowl bowl5 = new Bowl(5);
}
Bowl bowl4 = new Bowl(4);
static {
Bowl bowl1 = new Bowl(1);
System.out.println("Static Blocks in Base.");
}
}
public class Derived extends Base {
static {
Bowl bowl2 = new Bowl(2);
System.out.println("Static Blocks in Derived.");
}
Bowl bowl6 = new Bowl(6);
static Bowl bowl3 = new Bowl(3);
public Derived() {
Bowl bowl7 = new Bowl(7);
}
}
public class CupBoard {
public static void main(String[] args) {
Derived derived = new Derived();
}
}
Here’s the output:
The constructor of Bowl invoked #0
The constructor of Bowl invoked #1
Static Blocks in Base.
The constructor of Bowl invoked #2
Static Blocks in Derived.
The constructor of Bowl invoked #3
The constructor of Bowl invoked #4
The constructor of Bowl invoked #5
The constructor of Bowl invoked #6
The constructor of Bowl invoked #7
The Disqus comment system is loading ...
If the message does not appear, please check your Disqus configuration.