There is two classes 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 invoke the method printThree
of class Sub1
I expected the output to be:
Print Three
3
Because Sub1
constructor calling the super.printThree();
.
But I actually get
0
Print Three
3
I know 0 is default value of int
but how it is happening ?
In the constructor body of a derived class (with extends ), the super keyword may appear as a "function call" ( super(... args) ), which must be called before the this keyword is used, and before the constructor returns.
When a method in a subclass has the same name, same parameters or signature, and same return type(or sub-type) as a method in its super-class, then the method in the subclass is said to override the method in the super-class. Method overriding is one of the way by which java achieve Run Time Polymorphism.
“this()” and “super()” cannot be used inside the same constructor, as both cannot be executed at once (both cannot be the first statement). “this” can be passed as an argument in the method and constructor calls.
The overridden method shall not be accessible from outside of the classes at all. But you can call it within the child class itself. to call a super class method from within a sub class you can use the super keyword.
You're seeing the effects of three things:
Default super-constructor calls, and
Instance initializers relative to super calls, and
How overridden methods work
Your Sub1
constructor is really this:
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();
}
(Surprising, I know, but it's true. Use javap -c YourClass
to look. :-) )
The reason it looks like that is that the superclass must have a chance to initialize its part of the object before the subclass can initialize its part of the object. So you get this kind of interwoven effect.
And given that that's what Sub1
really looks like, let's walk through it:
The JVM creates the instance and sets all instance fields to their defaults (all bits off). So at this point, the three
field exists, and has the value 0
.
The JVM calls Sub1
.
Sub1
immediately calls super()
(Super1
), which...
...calls printThree
. Since printThree
is overridden, even though the call to it is in the code for Super1
, it's the overridden method (the one in Sub1
) that gets called. This is part of how Java implements polymorphism. Since three
's instance initializer hasn't been run yet, three
contains 0
, and that's what gets output.
Super1
returns.
Back in Sub1
, the instance initializer code for three
that the compiler inserted (relocated, really) runs and gives three
a new value.
Sub1
calls printThree
. Since three
's instance initializer code has now run, printThree
prints 3
.
With regard to this instance initializer code getting moved into the constructor, you might be wondering: What if I have more than one constructor? Which one does the code get moved into? The answer is that the compiler duplicates the code into each constructor. (You can see that in javap -c
, too.) (If you have a really complicated instance initializer, I wouldn't be surprised if the compiler effectively turned it into a method, but I haven't looked.)
It's a bit clearer if you do something really naughty and call a method during your instance init: (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's printThree");
}
}
class Sub extends Super
{
int three = this.initThree();
Sub() {
this.printThree();
}
private int initThree() {
System.out.println("Sub's initThree");
return 3;
}
protected void printThree() {
System.out.println("Sub's printThree: " + this.three);
}
}
Output:
Super constructor Sub's printThree: 0 Sub's initThree Sub's printThree: 3
Note where "Sub's initThree" came in that sequence.
When the instance is created, the Sub1
constructor is called.
The first instruction in any constructor is a call to the superclass constructor. If you don't have an explicit call, there will be an implicit call to the no-args constructor of Super1
.
The no-args constructor is calling this.printThree()
. This method is overridden in Sub1
. Now, this part may be confusing, but even if the code is in the superclass, this.method()
still refers to the overriding method.
So it's calling the printThree()
in Sub1
, which prints the uninitialized value of the variable three
- 0
.
Now that the superclass's constructor is done, it completes Sub1
constructor, which uses super.printThree()
. Since it specifically says super
, the method from Super1
is used rather than the overriding one. This prints the Print Three
.
Now the Sub1
constructor is also done, and main
calls the new instance's printThree()
. Now three
is already initialized, so you get the output 3
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With