方法调用
解析
- 所有方法调用的目标方法在Class文件里面都是一个常量池中的符号引用,在类加载的解析阶段,会将其中的一部分符号引用转化为直接引用,这种解析能够成立的前提是:方法在程序真正运行之前就有一个可确定的调用版本,并且这个方法的调用版本在运行期是不可改变的。换句话说,调用目标在程序代码写好、编译器进行编译那一刻就已经确定下来。这类方法的调用被称为解析。
- 主要有静态方法和私有方法两大类,符合“编译期可知,运行期不可变”这个要求。
- 调用不同类型的方法,字节码指令集里设计了不同的指令。
- invokestatic:用于调用静态方法。
- invokespecial:用于调用实例构造器
<init>()
方法、私有方法和父类中的方法。 - invokevirtual:用于调用所有的虚方法。
- invokeinterface:用于调用接口方法,会在运行时再确定一个实现该接口的对象。
- invokedynamic:用于运行时动态解析出调用点限定符所引用的方法,然后再执行该方法。前面4条调用指令,分派逻辑都固化在Java虚拟机内部,而invokedynamic指令的分派逻辑是由用户设定的引导方法来决定的。
- 只要能被invokestatic和invokespecial指令调用的方法,都可以在解析阶段中确定唯一的调用版本,Java语言里符合这个条件的方法共有静态方法、私有方法、实例构造器、父类方法4种。再加上被final修饰的方法(虽然它使用invokevirtual指令调用),这5种方法调用会在类加载的时候就可以把符号引用解析为该方法的直接引用。这些方法统称为“非虚方法”。
分派
静态分派
- 实现方法重载的本质。
动态分派
- 在运行期根据实际类型确定方法执行版本。实现方法重写的本质。
单分派与多分派
- 方法的接收者与方法的参数统称为方法的宗量,根据分派基于多少种宗量,可以将分派划分为单分派和多分派两种。
- 单分派是根据一个宗量对目标方法进行选择。静态分派属于多分派类型。
- 多分派则是根据多于一个宗量对目标方法进行选择。动态分派属于单分派类型。