Quantcast
Channel: Baeldung
Viewing all articles
Browse latest Browse all 4535

and Methods in the JVM

$
0
0

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 <init> and <clinit> methods for initialization purposes.

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 <init>. In JVM jargon, this method is an instance initialization method. A method is an instance initialization if and only if:

  • 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 <init>, let's consider another example:

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 <init> method:

  • 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 <init> method with a signature corresponding to the Java constructor.

The key takeaway here is that the constructors and other instance initializers are equivalent to the <init> method in the JVM world.

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 <clinit>
  • It returns void

Therefore, the only way to generate a <clinit> method in Java is to use static fields and static block initializers.

JVM invokes the <clinit> the first time we use the corresponding class. Therefore, the <clinit> invocation happens at runtime, and we can't see the invocation at the bytecode level.

4. Conclusion

In this quick article, we saw the difference between <init> and <clinit> methods in the JVM. The <init> method is used to initialize object instances.  Also, the JVM invokes the <clinit> method to initialize a class whenever necessary.

To better understand how initialization works in the JVM, it's highly recommended to read the JVM specification.


Viewing all articles
Browse latest Browse all 4535

Trending Articles