Java 101: Foundations

Java 101: Class and object initialization in Java

Prepare Java classes and objects for successful execution

1 2 Page 2
Page 2 of 2

Mirroring class initialization

Apart from constructors, object initialization mirrors class initialization. Instead of class field initializers, you have object field initializers. Furthermore, instead of class initialization blocks, you have object initialization blocks. You can also reference previously declared and initialized object fields but you cannot reference subsequently declared and initialized object fields. All of these concepts are demonstrated in Listing 8.

Listing 8. Mirroring class initialization capabilities from an object perspective

class Mirror
{
   int x = 2;
   int y = x;

   {
      System.out.println("x = " + x);
      System.out.println("y = " + y);
   }

   public static void main(String[] args)
   {
      Mirror mirror = new Mirror();
   }
}

Listing 8 reveals that an object initialization block is a block of statements introduced into the class body. Unlike a class initialization block, an object initialization block isn't prefixed by anything. Because you can initialize an object in a constructor, about the only good use for an object initialization block is in the context of an anonymous class, which doesn't have a constructor and which I'll discuss in a future article.

Compile Listing 8 (javac Mirror.java) and run the resulting application (java Mirror). You'll discover the following output:

x = 2
y = 2

Combining constructors and object field initializers/initialization blocks

You can combine multiple constructors, object field initializers, and object initialization blocks in an application. Listing 9 provides an example.

Listing 9. Performing object initialization in a strange order

class MCOFIOIB
{
   MCOFIOIB()
   {
      System.out.println("MCOFIOIB() called");
   }

   int x = 5;
   {
      x += 6;
   }

   int i;

   MCOFIOIB(int i)
   {
      this.i = i;
      System.out.println("MCOFIOIB(i) called: i = " + i);
   }

   {
      System.out.println("i = " + i);
      System.out.println("x = " + x);
   }

   public static void main(String[] args)
   {
      new MCOFIOIB();
      System.out.println();
      new MCOFIOIB(6);
   }
}

Listing 9 declares a pair of constructors (MCOFIOIB() and MCOFIOIB(int i)), a pair of object fields (x and i), and a pair of object initialization blocks. Compile this listing as follows:

javac MCOFIOIB.java

Then run the resulting application:

java MCOFIOIB

You should observe the following output:

i = 0
x = 11
MCOFIOIB() called

i = 0
x = 11
MCOFIOIB(i) called: i = 6

This output is from the creation of two MCOFIOIB objects. The first part originates from new MCOFIOIB(); and the second part originates from new MCOFIOIB(6);. Each part reveals that object field initializers and object initialization blocks execute before a constructor executes. Furthermore, it reveals that object field initializers and object initialization blocks execute in top-down order. (x had to be initialized to 11 before x = 11 could be output.)

<init>() methods

If you were to examine the bytecode that the compiler generates for MCOFIOIB.class, you would observe the presence of <init>() methods instead of constructors. The JVM invokes these methods instead of constructors.

You would also observe the following partial disassembly of the x and i fields:

Field #1

0000026d        Access Flags
0000026f        Name                                  x
00000271        Descriptor                            I
00000273        Attributes Count                      0

Field #2

00000275        Access Flags
00000277        Name                                  i
00000279        Descriptor                            I
0000027b        Attributes Count                      0

Next, you would observe the following information and bytecode sequence for the MCOFIOIB() constructor:

 0        aload_0
 1        invokespecial java/lang/Object/()V
 4        aload_0
 5        iconst_5
 6        putfield MCOFIOIB/x I
 9        aload_0
10        dup
11        getfield MCOFIOIB/x I
14        bipush 6
16        iadd
17        putfield MCOFIOIB/x I
20        getstatic java/lang/System/out Ljava/io/PrintStream;
23        new java/lang/StringBuilder
26        dup
27        invokespecial java/lang/StringBuilder/()V
30        ldc "i = "
32        invokevirtual java/lang/StringBuilder/append(Ljava/lang/String;)Ljava/lang/StringBuilder;
35        aload_0
36        getfield MCOFIOIB/i I
39        invokevirtual java/lang/StringBuilder/append(I)Ljava/lang/StringBuilder;
42        invokevirtual java/lang/StringBuilder/toString()Ljava/lang/String;
45        invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V
48        getstatic java/lang/System/out Ljava/io/PrintStream;
51        new java/lang/StringBuilder
54        dup
55        invokespecial java/lang/StringBuilder/()V
58        ldc "x = "
60        invokevirtual java/lang/StringBuilder/append(Ljava/lang/String;)Ljava/lang/StringBuilder;
63        aload_0
64        getfield MCOFIOIB/x I
67        invokevirtual java/lang/StringBuilder/append(I)Ljava/lang/StringBuilder;
70        invokevirtual java/lang/StringBuilder/toString()Ljava/lang/String;
73        invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V
76        getstatic java/lang/System/out Ljava/io/PrintStream;
79        ldc "MCOFIOIB() called"
81        invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V
84        return

The instruction sequence from offset 0 through offset 1 is equivalent to invoking the Object superclass's no-argument constructor:

new Object();

The instruction sequence from offset 4 through offset 17 is equivalent to the following class initialization block:

int x = 5;
{
   x += 6;
}

The instruction sequence from offset 20 through offset 73 executes the second object initialization block:

{
   System.out.println("i = " + i);
   System.out.println("x = " + x);
}

The instruction sequence from offset 76 through offset 84 executes the MCOFIOIB() constructor code and returns execution to the constructor's caller.

Again, don't worry about what the bytecode means. The important thing to remember is the initialization order. When you call MCOFIOIB(), the following tasks are performed:

  1. The superclass's noargument constructor is invoked first.
  2. Object field initializers and object initialization blocks are then executed in top-down order.
  3. The constructor's code is executed last.

For brevity, I won't present the bytecode sequence for the MCOFIOIB(int i) constructor; it's very similar to MCOFIOIB(). The only difference is that the final code to be executed is MCOFIOIB(int i)'s code and not MCOFIOIB()'s code.

In conclusion

The JVM initializes classes and objects before they are used. Class initialization consists of class field initializers and class initialization blocks. Object initialization consists of constructors, object field initializers, and object initialization blocks. Class initialization always completes before the first object is created from the class.

Now that you know how initialization works, you've largely completed your exploration of Java's class and object language features, though I will discuss nested classes and garbage collection in future articles. In my next Java 101 article we'll move on to interfaces, a slightly more advanced topic for beginning Java developers.

1 2 Page 2
Page 2 of 2