[方法调用并不等同于方法执行,方法调用阶段唯一的任务就是确定被调用方法的版本(即调用哪一个方法),暂时还不涉及方法内部的具体运行过程。在程序运行时,进行方法调用是最普
方法调用过程是指确定被调用方法的版本(即调用哪一个方法),并不包括方法执行过程。我们知道,Class 文件的编译过程中并不包括传统编译中的连接步骤,一切方法调用在 Class 文件调用里面存储的都只是符号引用,而不是方法在实际运行时的内存布局入口地址,也就是说符号引用解析成直接引用的过程。这个特性使得Java 具有强大的动态扩展能力,但也使得 Java方法调用过程变得复杂起来,需要在类加载器件,甚至是运行期间才确定目标方法的直接饮用。
1、解析调用
在类加载的解析阶段,会将其中一部分符号引用直接转化为直接饮用,前提是:方法在程序真正运行之前就有一个可确定的版本,并且这个方法的调用版本在运行期是不可改变的。换句话说,调用目标在程序代码写好,编译器进行编译时就必须确定下来。这类方法的调用称为解析(Resolution)。
符合这个要求的方法,主要包括:静态方法 和 私有方法。因为这两个方法的特点就决定了他们都不可能通过继承或别的方式重写其他版本。
与之相对应的,Java 虚拟机里面提供了5条方法调用字节码指令,分别如下:[Class文件的编译过程中不包含传统编译中的连接步骤,一切方法调用在Class文件里面存储的都只是符号引用,而不是方法在实际运行时内存布局中的入口地址。]
invokestatic:调用静态方法
invokespecial:调用方法、私有方法和父类方法
invokevirtual:调用所有的虚方法
invokeinterface:调用接口方法,会在运行时在确定一个实现此接口的对象
invokedynamic:会在运行时动态解析出调用电限定符所引用的方法,然后再执行该方法。 只能被 invokestatic 和 invokespecial 调用的方法,都可以在解析阶段中确定唯一的调用版本,符合这个条件的有
静态方法、私有方法、实例构造器、父类方法4类,这些方法称为
非虚方法,由于 final 修饰的方法不能被覆盖,也属于非虚方法。与之相反,其他的方法称为虚方法。
2、分派调用
作为一门面向对象的程序语言,Java 具备面型对象的3个特征:继承、封装和多态。下面我们将会讲解多态性特征的一些最基本的体现,如“重写”和“重载”在Java 虚拟机中是怎么实现的。
2.1静态分派
依赖于静态类型来定位方法执行版本的分派动作(如重载)称为静态分派。 首先我们先来看一下下面的这段代码:
package org.fenixsoft.polymorphic;
/**
* 方法静态分派演示
* @author zzm
*/
public class StaticDispatch {
static abstract clas