创建子类对象时,父类构造函数中调用被子类重写的方法为什么调用的是子类的方法?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class Basic{
public void add(int i)
{
System.out.println("Basic add");
}
public Basic()
{
add('a');
}
}
public class A extends Basic{
public void add(int i)
{
System.out.println("A add");
}
}
public class B extends Basic{
public void add(char i)
{
System.out.println("B add");
}
}
public class Main{
public static void main(String[] args)
{
A a = new A();
B b = new B();
}
}

问题:为什么创建A对象的时候父类会调用子类方法?
但是:创建B对象父类会调用父类的方法?

答案:
当子类被加载到内存方法区后,会继续加载父类到内存中。
如果,子类重写了父类的方法,子类的方法引用会指向子类的方法,否则子类的方法引用会指向父类的方法引用。
如果子类重载了父类方法,则子类重载方法引用还指向子类方法。
如果子类方法没有重写也没有重载父类方法,则方法引用会指向父类方法。

当子类对象创建时,会先行调用父类的构造方法(构造方法也是方法),虚拟机会在子类方法区寻找该方法并运行。
但是:由于java语言是静态多分派,动态单分派。其结果是当编译的时候,父类构造方法调用的方法的参数已经强制转换为符合父类方法的参数了。
上边代码在编译前已经转换为下面这个样子的了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class Basic{
public void add(int i)
{
System.out.println("Basic add");
}
public Basic()
{
add((int)'a');
}
}
public class A extends Basic{
public void add(int i)
{
System.out.println("A add");
}
}
public class B extends Basic{
public void add(char i)
{
System.out.println("B add");
}
}
public class Main{
public static void main(String[] args)
{
A a = new A();
B b = new B();
}
}

看一下上边的代码,是不是恍然大悟?