1.方法调用
先来说说java方法的调用,方法的调用不等于方法执行,方法调用阶段唯一的任务是确定被调用方法的版本(即调用哪个方法,不是唯一的,确定一个“更加合适”的版本),不涉及方法内部的具体运行过程。
我们都是知道java文件都需要编译成class文件,而一切方法调用在class文件里存储的都是符号引用,而不是方法的实际运行时内存布局的入口地址(相当于直接引用)。在类加载的解析阶段,会将其中的一部分符号引用转化为直接引用,这种解析成立的前提是:方法的程序真正运行之前就有一个可确认的调用版本,并且这个方法的调用版本在运行期是不可变的。换句话说,调用目标在程序代码写好、编辑器进行编译时就必须确认下来,这类方法调用的调用称为解析。
在Java虚拟机里提供了5条调用方法字节码指令,分别如下。
invokestatic:调用静态方法
invokespeciak: 调用实例构造器<init>方法、私用方法和父类方法
invokevirtual: 调用所有的虚方法
invokeinterface:调用接口时,会在运行再确定一个实现接口的对象
invokedynamic:现在运行时动态解析出调用点限定符引用的方法,再执行方法
只有被invokestatic和invokespecial指令调用的方法,可以在解析阶段中确定调用的版本,符合这个条件的静态方法、私有方法、实例构造器、父类方法。它们在类加载的解析时候就会把符号引用解析为直接引用。这些方法被称为非虚方法。解析调用一定是一个静态的过程,在编译期间就完全确定,而分配调用可能是静态的也可能是动态的
。
2.分派
Java是一门面向对象的编程语言,因为Java具备面向对象的3个基本特征:封装、继承、多态。来看看虚拟机如何通过分派确定“重写”和”重载“方法的目标方法。
来看一个静态分配的例子