2

I've asked several questions regarding the subject, but it seems like every time I get an answer, I have more questions. This question is continuation of my other question: Initialization in polymorphism of variables

Anyways, consider the following example.

class A{       //1
   int a = 1;  //2
}              

i heard this conceptually looks like,

class A {          //1
    int a = 0;     //2

    A() {          //3
      super();     //4
      a = 1;       //5
    }

From what I understand, this is because every time an object is created, instance objects are initialized to its default values.

  1. If I put initialization block say,

    System.out.print(i); 
    

    right below line 2 for both examples, top will print 1 and bottom will print 0. As far as I know, initialization block is executed before constructor. So is this only conceptual representation of constructors only? Or does the code actually change as such when the default constructor is called? Can someone clarify this for me?

  2. Why does it behave this way? In my other question, it seemed to only cause confusion as to which variable is called. Can't instance variable just be declared a=1 and gets used throughout the class? Shouldn't that make it simpler?

Community
  • 1
  • 1
SERich
  • 553
  • 2
  • 4
  • 13
  • Check http://stackoverflow.com/questions/19561332/in-what-order-do-static-blocks-and-initialization-blocks-execute-when-using-inhe – TheLostMind Sep 30 '16 at 07:08
  • Hint: besides asking questions ... consider to the **one** thing that a person **really** interested in such matters should do: study the Java Language Spec, for example https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.3.2 What I am saying is: it seems that you want to get to the deepest ground. Then dont trust people giving you answers here (too much). Learn to read the specs (on top or before asking other people to teach you) – GhostCat Sep 30 '16 at 07:40

4 Answers4

1

As you said, the equivalence between the two classes in your question is only conceptual.

In fact, if an non-static data field has an initialization value, it is initialized before calling the constructor. The initialization block is copied by the compiler to the beginning of every constructor (after the super line), so it is executed after the initialization of the field and before the constructor code itself.

AhmadWabbi
  • 2,253
  • 1
  • 20
  • 35
1

The difference between your example is the order of operations. In your first example, with the initializer block where you said, the order is:

  1. Assign 1 to a (in the declaration)
  2. Output a (in the initializer block)

...but in your example example, it's

  1. Assign 0 (the default value) to a (effectively in the declaration)
  2. Output a (in the initialization block)
  3. Assign 1 to a (in the constructor)

The key to understanding instance initialization for me is this: Instance initialization code is literally copied into the constructors — all of them, including the default one — by the compiler. It's copied in source code order, and it's before anything in the constructor (including super).

Here's a more complete example. Consider this class:

class Example {
    // Instance field with initializer
    private int i = 5;

    // Instance initialization block
    {
        System.out.println(this.i);
    }

    // constructor 1
    Example() {
        System.out.println(this.i * 2);
    }

    // constructor 2
    Example(int _i) {
        this.i = _i;
        System.out.println(this.i * 3);
    }
}

That's compiled into bytecode exactly as though it were this:

class Example {
    // Instance field
    private int i;

    // constructor 1
    Example() {
        // begin copied code
        this.i = 5;
        System.out.println(this.i);
        // end copied code
        System.out.println(i * 2);
    }

    // constructor 2
    Example(int _i) {
        // begin copied code
        this.i = 5;
        System.out.println(this.i);
        // end copied code
        this.i = _i;
        System.out.println(this.i * 3);
    }
}

In both cases above, Oracle's Java 8 outputs the exact same bytecode (as viewed by using javap -c Example after compiling):

Compiled from "Example.java"
class Example {
  Example();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."":()V
       4: aload_0
       5: iconst_5
       6: putfield      #2                  // Field i:I
       9: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
      12: aload_0
      13: getfield      #2                  // Field i:I
      16: invokevirtual #4                  // Method java/io/PrintStream.println:(I)V
      19: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
      22: aload_0
      23: getfield      #2                  // Field i:I
      26: iconst_2
      27: imul
      28: invokevirtual #4                  // Method java/io/PrintStream.println:(I)V
      31: return

  Example(int);
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."":()V
       4: aload_0
       5: iconst_5
       6: putfield      #2                  // Field i:I
       9: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
      12: aload_0
      13: getfield      #2                  // Field i:I
      16: invokevirtual #4                  // Method java/io/PrintStream.println:(I)V
      19: aload_0
      20: iload_1
      21: putfield      #2                  // Field i:I
      24: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
      27: aload_0
      28: getfield      #2                  // Field i:I
      31: iconst_3
      32: imul
      33: invokevirtual #4                  // Method java/io/PrintStream.println:(I)V
      36: return
}
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
1

Your description of how int a = 1 gets converted to a constructor is correct, but it is not the whole story.

  • If, in addition to a, there are other instance fields with initializers, all of their initializers are collected into a single block that runs as part of constructors
  • If, in addition to field initialization you have general-purpose initializer blocks, their content gets collected into that same block, along with field initializers.

For example, if you have

class A {
    {
        System.out.println(a);
    }
    int a = 1;
    {
        System.out.println(a);
        System.out.println(b);
    }
    int b = 2;
    {
        System.out.println(b);
    }
    public A() {
        // Code of A
    }
}

then the code block prior to Code of A looks like this:

System.out.println(a);
a = 1;
System.out.println(a);
System.out.println(b);
b = 2;
System.out.println(b);
// Code of A

It should be clear now why zero is printed in the initialization block prior to int a = 1 in the block preceding the initializer: initialization blocks are not treated separately from field initializers, their code gets mixed together in the same order that they appear in the source code.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
0

Instance variables are immediately available with the default variable if not otherwise set: Objects are set to null and primitive types to 0, false etc.

You have 3 options to set the value of an instance variable in Java:

1) Declare and instantiate immediately

class A {
    int i = 1;
}

2) Instantiate it in a instance initializer block

class A {
    int a; // it is default value 0 at this point
    { a = 1; } //instance initializer block
} 

3) Instantiate it in the constructor

class A{
    int a; // it is default value 0 at this point
    A() {
        a = 1;
    }
}    

During the instantiation of the A object, Java will

  1. first instantiate the variable a to its default if not done by the user,
  2. then it will go through any instance initializer block in the order they appear, and lastly
  3. it will enter the constructor.
Edo user1419293
  • 171
  • 2
  • 9