1. Overview
The JVM uses two distinctive methods to initialize object instances and classes.
In this quick article, we’re going to see how the compiler and runtime use the
2. Instance Initialization Methods
Let’s start with a straightforward object allocation and assignment:
Object obj = new Object();
If we compile this snippet and take a look at its bytecode via javap -c, we’ll see something like:
0: new #2 // class java/lang/Object
3: dup
4: invokespecial #1 // Method java/lang/Object."<init>":()V
7: astore_1
To initialize the object*,* the JVM calls a special method named
- It is defined in a class
- Its name is <**init>
- It returns void
Each class can have zero or more instance initialization methods. These methods usually are corresponding to constructors in JVM-based programming languages such as Java or Kotlin.
2.1. Constructors and Instance Initializer Blocks
To better understand how the Java compiler translates constructors to
public class Person {
private String firstName = "Foo"; // <init>
private String lastName = "Bar"; // <init>
// <init>
{
System.out.println("Initializing...");
}
// <init>
public Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
// <init>
public Person() {
}
}
This is the bytecode for this class:
public Person(java.lang.String, java.lang.String);
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: ldc #7 // String Foo
7: putfield #9 // Field firstName:Ljava/lang/String;
10: aload_0
11: ldc #15 // String Bar
13: putfield #17 // Field lastName:Ljava/lang/String;
16: getstatic #20 // Field java/lang/System.out:Ljava/io/PrintStream;
19: ldc #26 // String Initializing...
21: invokevirtual #28 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
24: aload_0
25: aload_1
26: putfield #9 // Field firstName:Ljava/lang/String;
29: aload_0
30: aload_2
31: putfield #17 // Field lastName:Ljava/lang/String;
34: return
Even though the constructor and the initializer blocks are separate in Java, they are in the same instance initialization method at the bytecode level. As a matter of fact, this
- First, initializes the firstName and lastName fields (index 0 through 13)
- Then, it prints something to the console as part of the instance initializer block (index 16 through 21)
- And finally, it updates the instance variables with the constructor arguments
If we create a Person as follows:
Person person = new Person("Brian", "Goetz");
Then this translates to the following bytecode:
0: new #7 // class Person
3: dup
4: ldc #9 // String Brian
6: ldc #11 // String Goetz
8: invokespecial #13 // Method Person."<init>":(Ljava/lang/String;Ljava/lang/String;)V
11: astore_1
This time JVM calls another
The key takeaway here is that the constructors and other instance initializers are equivalent to the
3. Class Initialization Methods
In Java, static initializer blocks are useful when we’re going to initialize something at the class level:
public class Person {
private static final Logger LOGGER = LoggerFactory.getLogger(Person.class); // <clinit>
// <clinit>
static {
System.out.println("Static Initializing...");
}
// omitted
}
When we compile the preceding code, the compiler translates the static block to a class initialization method at the bytecode level.
Put simply, a method is a class initialization one if and only if:
- Its name is
- It returns void
Therefore, the only way to generate a
JVM invokes the
4. Conclusion
In this quick article, we saw the difference between
To better understand how initialization works in the JVM, it’s highly recommended to read the JVM specification.