4

In The Java Tutorials - Initializing Fields, have a description about Instance Initialization Blocks(Instance Initializer):

The Java compiler copies initializer blocks into every constructor. Therefore, this approach can be used to share a block of code between multiple constructors.

If the paraphrase is correct , for the following codes:

public class ConstructorTest {

    public static void main(String[] args) {
        Child c = new Child();
    }
}

class Parent {
    Parent() {
        System.out.println("Parent non-argument Constructor");
    }
}

class Child extends Parent {

    {
        System.out.println("Child Instance Initialization Block");
    }

    Child() {
        this(2);
        System.out.println("Child no-argument Constructor");

    }

    Child(int i) {
        this(10, i);
        System.out.println("Child 1-argument Constructor");
    }

    Child(int i, int j) {
        System.out.println("Child 2-argument Constructor");
    }
}

The output should be:

Parent non-parm Constructor
Child Instance Initialization Block
Child 2-argument Constructor
Child Instance Initialization Block
Child 1-argument Constructor
Child Instance Initialization Block
Child no-argument Constructor

But the actually output is:

Parent non-argument Constructor
Child Instance Initialization Block
Child 2-argument Constructor
Child 1-argument Constructor
Child no-argument Constructor

I was misunderstood the meaning of that sentence, or the description is not accurate enough?

And another doubt about explicit constructor invocation:

Based on two Basics:

  • If present, the invocation of another constructor must be the first line in the constructor.
  • From within a constructor, use this() to call another constructor,use super() to call direct superclass's Corresponding constructor.

Is that MEANS use this() within subclass's constructor will implicit remove the default call to the no-argument constructor of the superclass?

Thanks for the reply.

Wuaner
  • 929
  • 2
  • 14
  • 31

3 Answers3

5

EDIT: It turns out the JLS is accurate after all, although it's hard to read. It's all detailed in section 12.5:

Just before a reference to the newly created object is returned as the result, the indicated constructor is processed to initialize the new object using the following procedure:

  1. Assign the arguments for the constructor to newly created parameter variables for this constructor invocation.

  2. If this constructor begins with an explicit constructor invocation (§8.8.7.1) of another constructor in the same class (using this), then evaluate the arguments and process that constructor invocation recursively using these same five steps. If that constructor invocation completes abruptly, then this procedure completes abruptly for the same reason; otherwise, continue with step 5.

  3. This constructor does not begin with an explicit constructor invocation of another constructor in the same class (using this). If this constructor is for a class other than Object, then this constructor will begin with an explicit or implicit invocation of a superclass constructor (using super). Evaluate the arguments and process that superclass constructor invocation recursively using these same five steps. If that constructor invocation completes abruptly, then this procedure completes abruptly for the same reason. Otherwise, continue with step 4.

  4. Execute the instance initializers and instance variable initializers for this class, assigning the values of instance variable initializers to the corresponding instance variables, in the left-to-right order in which they appear textually in the source code for the class. If execution of any of these initializers results in an exception, then no further initializers are processed and this procedure completes abruptly with that same exception. Otherwise, continue with step 5.

  5. Execute the rest of the body of this constructor. If that execution completes abruptly, then this procedure completes abruptly for the same reason. Otherwise, this procedure completes normally.

Note the highlighted part - the chained constructor is executed, and then we skip step 4, which would execute the instance initializer.

Reality is that the instance and field initializers are only executed once, as you can tell from your output.

Informally, I believe it's accurate to describe the procedure as:

  • Keep chaining constructors within the same class (this(...)) until you reach a constructor body which doesn't start with this.
  • Execute the appropriate super-constructor
  • Execute the instance variable initializers and the instance initializers
  • Execute the body of the "innermost" constructor
  • Keep popping the stack of constructor bodies until you end up with the "entry" constructor

Is that MEANS use this() within subclass's constructor will implicit remove the default call to the no-argument constructor of the superclass?

Yes. Somewhere in your chain of constructors within the class, you're guaranteed to end up with a constructor which calls super either implicitly or explicitly. That is the only superclass constructor which is called.

EDIT: Note that the tutorial you quoted is demonstrably incorrect.

Sample class:

public class Test {
    {
        System.out.println("Foo");
    }
    
    public Test() {
    }
    
    public Test(int i)  {
        this();
    }
}

Output from javap -c:

public class Test {
  public Test();
    Code:
       0: aload_0
       1: invokespecial #1  // Method java/lang/Object."<init>": ()V
       4: getstatic     #2  // Field java/lang/System.out:Ljava/io/PrintStream;
       7: ldc           #3  // String Foo
       9: invokevirtual #4  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      12: return

  public Test(int);
    Code:
       0: aload_0
       1: invokespecial #5                  // Method "<init>":()V
       4: return
}

As you can see, the constructor for Test(int) does not have the code for the instance constructor compiled into it.

Basically, only constructors which directly call superclass constructors have instance initializer code copied into them. All other constructors will end up causing the instance initializer code to be executed via a constructor which calls the superclass constructor, of course.

Community
  • 1
  • 1
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • but in step 2, it says "... continue with step 5" - means, step 4 "Execute the instance initializers " is explicitly skipped "If this constructor begins with an explicit constructor invocation (this())" so that the instance initializer is only excuted once before each of the constructor bodies is executed – Andreas Fester Sep 03 '12 at 21:01
  • @Andreas: Yup, thanks for that (here and on your answer). I've included that bit of the spec and highlighted the important bit. – Jon Skeet Sep 03 '12 at 21:08
  • Hello Jon, Thanks very much. How warm-hearted you are! the recursively procedure made me a little bit confused...but never mind. by the way,what tools do you use to decompile the Bytecode? – Wuaner Sep 03 '12 at 22:59
0

Initialization blocks runs just ONE time when you instantiate a object of the class (using any constructor <= here is your doubt). Static initilization blocks runs just ONE time when the class is loaded by the class loader.

Is that MEANS use this() within subclass's constructor will implicit remove the default call to the no-argument constructor of the superclass?

No. this() will call another constructor of the same class, in this case the default constructor (if present). This constructor (the default one) will call super().

davidbuzatto
  • 9,207
  • 1
  • 43
  • 50
  • The code doesn't show any *static* initializers. It shows an *instance* initializer, which will be run once per object constructed. – Jon Skeet Sep 03 '12 at 20:39
  • @JonSkeet: I really read wrongly OP code, but I just complemented my answer saying something about static blocks. – davidbuzatto Sep 03 '12 at 20:46
  • Not clearly, certainly. If your second sentence is meant to be about instance initializers, it's muddied by the third sentence, which is irrelevant to the question. Downvote removed as reading through your answer really carefully makes it merely poorly explained (IMO) rather than simply useless. – Jon Skeet Sep 03 '12 at 20:49
  • Bringing static initializers into the mix just adds to the confusion, IMO. – Jon Skeet Sep 03 '12 at 20:49
  • Additionally, I don't think the OP "misunderstood" the meaning of the sentence - the sentence is definitely incorrect. The instance initializer is simply *not* compiled into every constructor. I'll edit my answer to show that. – Jon Skeet Sep 03 '12 at 20:51
0

I think the Java language specification is more accurate: http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.6

An instance initializer declared in a class is executed when an instance of the class is created

and in http://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.5 steps 4 and 5 describe the output you observed:

4 . Execute the instance initializers and instance variable initializers for this class ...

5 . Execute the rest of the body of this constructor ...

Community
  • 1
  • 1
Andreas Fester
  • 36,091
  • 7
  • 95
  • 123
  • You missed out step 2, which states: "If this constructor begins with an explicit constructor invocation (§8.8.7.1) of another constructor in the same class (using this), then evaluate the arguments and process that constructor invocation recursively using these same five steps." The "recursively" part of that would imply to me that it *would* execute multiple times (once for the "nested" execution). The spec is badly written here. – Jon Skeet Sep 03 '12 at 20:55
  • Yes, but the same step 2 also says "continue with step 5" - I agree through that the spec is badly written / hard to understand .... – Andreas Fester Sep 03 '12 at 21:03
  • 1
    Doh - I'd missed that bit. That makes it all a lot clearer :) – Jon Skeet Sep 03 '12 at 21:06