What happens if you call an overridden method using super in the constructor

There are two classes of Super1 and Sub1

Super1.class

 public class Super1 { Super1 (){ this.printThree(); } public void printThree(){ System.out.println("Print Three"); } } 

Sub1.class

 public class Sub1 extends Super1 { Sub1 (){ super.printThree(); } int three=(int) Math.PI; public void printThree(){ System.out.println(three); } public static void main(String ...a){ new Sub1().printThree(); } } 

When I call the printThree method of the printThree class, I expected the result to be:

Seal of three
3

Because the constructor Sub1 calls super.printThree(); .

But I really get

0
Seal Three
3

I know that 0 is the default value of int , but how does this happen?

+6
source share
3 answers

You see the effects of three things:

  • By default, calls to the super constructor and

  • Instance initializers regarding supercalls and

  • How overridden methods work

Constructor Sub1 really :

 Sub1(){ super(); // <== Default super() call, inserted by the compiler three=(int) Math.PI; // <== Instance initializers are really inserted // into constructors by the compiler super.printThree(); } 

(Surprisingly, I know, but it's true. Use javap -c YourClass to search. :-))

It seems like this is so that the superclass must be able to initialize its part of the object before the subclass can initialize its part of the object. Thus, you get such an intertwined effect.

And given that what Sub1 really looks like allows you to get through it:

  • The JVM instantiates and sets all instance fields to defaults (all bits are off). So, at this moment there is a three field and has a value of 0 .

  • JVM calls Sub1 .

  • Sub1 immediately calls super() ( Super1 ), which ...

    • ... calls printThree . Since printThree overridden, although the call to it is in the code for Super1 , it calls the overridden method (the one that is in Sub1 ). This is part of how Java implements polymorphism. Since three instance initializer is not running yet, three contains 0 , and that gets the result.

    • Super1 returns.

  • Back in Sub1 , the instance initializer code for three , which was inserted (moved, really) by the compiler, and gives three new value.

  • Sub1 calls printThree . Since three instance initializer code is now running, printThree prints 3 .

As for this instance initializer code that moves to the constructor, you might be wondering: what if I have multiple constructors? What code has the code moved to? The answer is that the compiler duplicates the code in each constructor. (You can also see this in javap -c .) (If you have a really complex instance initializer, I would not be surprised if the compiler turned it into a method, but I did not look.)

This is a little clearer if you are doing something really naughty and calling the method during your init instance: ( live copy )

 class Super { public static void main (String[] args) { new Sub(); } Super() { System.out.println("Super constructor"); this.printThree(); } protected void printThree() { System.out.println("Super printThree"); } } class Sub extends Super { int three = this.initThree(); Sub() { this.printThree(); } private int initThree() { System.out.println("Sub initThree"); return 3; } protected void printThree() { System.out.println("Sub printThree: " + this.three); } } 

Output:

  Super constructor
 Sub printThree: 0
 Sub initThree
 Sub printThree: 3

Notice where "Sub initThree" appeared in this sequence.

+14
source

When an instance is created, the constructor Sub1 .

The first instruction in any constructor is to call the constructor of the superclass. If you do not have an explicit call, there will be an implicit call to the no-args Super1 .

The no-args constructor calls this.printThree() . This method is overridden in Sub1 . Now this part may be misleading, but even if the code is in a superclass, this.method() still refers to the override method.

So, it calls printThree() on Sub1 , which prints the uninitialized value of the variable three - 0 .

Now that the superclass constructor has completed, it completes the Sub1 constructor, which uses super.printThree() . Since he specifically says super , the method from Super1 , not overriding. This prints Print Three .

Now the Sub1 constructor is also executed, and main calls a new instance of printThree() . Now three already initialized, so you get pin 3 .

+3
source

While the previous answers gave you a clear answer to what is happening, they did not give you any instructions on how to avoid your problems in the future, so I would also like to add my contribution to this.

If you intend to inherit, you should make the class superclass as stupid as possible. for instance

 public class Super{ private int a,b; public Super(int a, int b) { this.a = a; this.b = b; } //all the methods operating on the data provided by constructor } 

and then using the helper constructor

  private int c,d; public Sub(int a, int b) { super(a,b); c = a; d = b; } 

Great, and you get minimal side effects while maintaining the functionality of the parent class.

But having

 public Super(){ method1(); method2(); } 

and then with sub do it

 public Sub(){ super.method1(); super.method2(); } 

In fact, the question is asked about the problems and it may be difficult to track errors. The less an object does during initialization, the better, because it gives flexibility to children. Inheritance management is like a dumb manager and a smart manager. The dumb manager calls Tim and Tracy because they are both employees, and their job as accountant and HR manager is just tags. A smart manager knows that Tim and Tracy are an accountant and manager, and he doesn't care that they are basically just employees.

0
source

All Articles