四、方法的调用
在JVM里面,将符号引用转换为调用方法的直接引用是跟方法的绑定机制有关的。
-
静态链接 当一个字节码文件装载进JVM内部时,如果被调用的方法在编译期可知,并且在运行期保持不变的情况下,那么这种转换的过程称之为静态链接。
-
动态链接 当被调用的方法在编译期无法确定,需要在运行期才可知,这种将符合引用转换为方法的直接引用的过程称之为动态链接。
静态链接对应的方法绑定机制为早期绑定;动态链接对应的方法绑定机制为晚期绑定
- 非虚方法 如果方法在编译期可以确定具体的调用版本,并且运行期是不可变的,那么这种方法称之为非虚方法,像静态方法、私有方法、final方法、实例构造器、父类方法都是非虚方法,对应也就是早期绑定
- 虚方法 类似于c++里面的虚函数,如果方法在编译器无法确定具体的调用版本,只能在运行期确定,那这种方法就称之为虚方法,与之对应也就是晚期绑定
具体的字节码指令
- invokestatic:调用static修饰的方法
- invokespecial:调用方法,私有及父类方法
- invokevirtual:调用所有的虚方法(注意!final 修饰的是非虚方法)
- invokeinterface:调用父类接口的方法
jdk 1.8之后引入了动态的调用指令(invokedynamic),这个设计是为了符合lambda表达式而来.
/**
* @author sijing.lu
* @create 2021/12/7 10:15 上午
* @description
* @since 1.0.0
*/
public class JVMMethod {
class Animal{
void eat(){
System.out.println("动物要吃饭");
}
}
interface Hunt{
void hunt();
}
public class Cat extends Animal implements Hunt{
@Override
public void eat() {
System.out.println("猫吃鱼");
}
@Override
public void hunt() {
System.out.println("猫捉老鼠");
}
}
public class Dog extends Animal implements Hunt{
@Override
void eat() {
super.eat();
}
@Override
public void hunt() {
System.out.println("狗拿耗子");
}
}
public void showEat(Animal animal){
// 晚期绑定
animal.eat();
}
public void showHunt(Hunt hunt){
// 晚期绑定
hunt.hunt();
}
public static void main(String[] args) {
JVMMethod method = new JVMMethod();
method.methodA();
method.methodB();
methodC();
}
public void methodA(){
System.out.println("methodA()");
}
public final void methodB(){
System.out.println("methodB()");
}
public static void methodC(){
System.out.println("methodC()");
}
}