方法调用并不等同于方法执行,方法调用阶段唯一的人物就是确定被调用方法的版本(即调用哪一个方法),暂时还不涉及方法内部的具体运行过程。
什么是解析
所有方法调用中的目标方法在Class文件里都是一个常量池中的符号引用,在类加载的解析阶段,会将其中的一部分符号引用转化为直接引用,前提是:调用目标在程序代码写好、编译器编译时就必须确定下来。这类方法的调用称为解析(Resolution)。
在Java中符合“编译期可知,运行期不可变”这个要求的方法,主要包括静态方法和私有方法两大类。
- 静态方法:与类型直接关联
- 私有方法:外部不可被访问
它们都不可能通过继承或其他方式被重写,因此它们都适合在类加载阶段进行解析。
Java虚拟机提供了5条方法调用的字节码指令,如下:
- invokestatic:调用静态方法
- invokespecial:调用实例构造器<init>方法、私有方法和父类方法
- invokevirtual:调用所有的虚方法
- invokeinterface:调用接口方法,会在运行时再确定一个实现此接口的对象
- invokedynamic:先在运行时动态解析出调用点限定符所引用的方法,然后再执行该方法,在此之前的4条调用指令,分派逻辑是固化在Java虚拟机内部的,而invokedynamic指令的分派逻辑是由用户所设定的引导方法决定的
只要能被 invokestatic 和 invokespecial 指令调用的方法,都可以在解析阶段中确定唯一的调用版本,它们在类加载的时候就会把符号引用解析为该方法的直接引用。这类方法被称为“非虚方法”,与此相对的是“虚方法”(去除final方法)。
//静态方法解析
public class StaticResolution {
public static void say(){
System.out.println("hello");
}
public static void main(String[] args) {
StaticResolution.say();
}
}


使用javap命令查看这段代码的字节码,会发现是通过invokestatic命令来调用say()方法的。
Java中的非虚方法除了使用invokestatic和invokespecial 指令调用的方法,还有被final修饰的方法。
虽然final方法是使用invokevirtual指令调用的,但由于它无法被覆盖,没有其他版本,所以也无需对方法接收者进行多态选择。在Java语言规范中名曲说明了final方法是一种非虚方法。