Java的多态方法的与静态与动态分派
前言
1 |
|
- 运行结果
1 | I am a Person 11 |
2.再看Java
1 |
|
- 运行结果
1 | I am a Student |
3.分析原因
Java与C的绑定不同。Java中只有final、static、private和构造方法是静态绑定的,其它所有方法都采用动态绑定,而C++只有虚函数进行的是动态绑定。
所以在C++中,p0.Show()才用静态绑定的Show(),尽管指向的是Student。而Java则是动态绑定,因为有JVM,JVM会调用变量实际指向的对象的方法。p0实际是指向s0的,所以会调用s0的Show()。
- 从java的字节码角度出发,有5种调用指令
- invokeinterface: 调用接口中的方法,实际上是在运行期决定实际实现该接口的哪一个对象
- invokestatic 调用静态方法
- invokespecial 调用实际的私有方法,构造方法
- invokevirtual 调用虚方法 运行期间动态查找的过程
- invokedynamic 动态调用
- 方法重载时一种静态类型,调用的时候就是声明的类型,编译时可以确定。
- 方法重写是一种动态类型,是实际调用的时候,才知道的。需要运行时确定。
- 虚拟机一定调用引用对象的”实际类“的最适合的方法。虚拟机预先为每一个类创建一个
方法表,在运行时,虚拟机会查这个表。首先适配实际类的方法,否则在超类中继续寻找,以此类推。
字节码带来的疑惑?
INVOKEVIRTUAL Person.Show ()V //—这里明明显示的是调用Person.Show怎么调用有到 Student.Show
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// access flags 0x9
public static main([Ljava/lang/String;)V
L0
LINENUMBER 15 L0
NEW Student
DUP
INVOKESPECIAL Student.<init> ()V
ASTORE 1
L1
LINENUMBER 16 L1
ALOAD 1
ASTORE 2
L2
LINENUMBER 17 L2
ALOAD 2
INVOKEVIRTUAL Person.Show ()V //---这里明明显示的是调用Person.Show怎么调用有到 Student.Show
L3
LINENUMBER 18 L3
ALOAD 1
INVOKEVIRTUAL Student.Show ()V
L4
LINENUMBER 19 L4
RETURN
L5
LOCALVARIABLE arg [Ljava/lang/String; L0 L5 0
LOCALVARIABLE s0 LStudent; L1 L5 1
LOCALVARIABLE p0 LPerson; L2 L5 2
MAXSTACK = 2
MAXLOCALS = 3
}这个就涉及到原理INVOKEVIRTUAL了
INVOKEVIRTUAL
- 首先去找在操作数的栈顶的实际指向的那个对象的实际类型,寻找到描述和访问权限都符合,则直接返回这个方法的直接引用。
- 如果找不到,然后去找父类
- 再找不到,抛异常
这样依次去找会不会很慢?
- JVM会优化,生成一个叫vtable的方法表。
- 通过vtable代替实际查找。