1.JVM提供了5种方法调用指令,其作用列举如下:
- invokestatic:该指令用于调用静态方法,即使用 static 关键字修饰的方法;
- invokespecial:该指令用于三种场景:调用实例构造方法,调用私有方法(即private关键字修饰的方法)和父类方法(即super关键字调用的方法);
- invokeinterface:该指令用于调用接口方法,在运行时再确定一个实现此接口的对象;
- invokevirtual:该指令用于调用虚方法(就是除了上述三种情况之外的方法);
- invokedynamic:在运行时动态解析出调用点限定符所引用的方法之后,调用该方法;在JDK1.7中推出,主要用于支持JVM上的动态脚本语言(如Groovy,Jython等)。
2.下面看一段代码例子
public class Test {
static class Father {
public void say() {
System.out.println("i am fater");
System.out.println(this);
this.hello();
this.hi();
}
private void hello() {
System.out.println("father say hello");
}
public void hi() {
System.out.println("father say hi");
}
}
static class Son extends Father {
public void hello() {
System.out.println("son say hello");
}
public void hi() {
System.out.println("son say hi");
}
}
public static void main(String[] args) throws InterruptedException, FileNotFoundException {
Father test = new Son();
test.say();
}
}
输出结果
i am fater
Test$Son@15db9742
father say hello
son say hi
先用javac Test.java 去编译源代码,生成
然后 用javap -v Test$Father.class ,查看jvm指令
我们重点关注say方法
第一行
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
getstatic指令: 获取类的静态字段,将其值压入栈顶。
也就是将System类中的静态变量out压栈
第二行
3: ldc #3 // String i am fater
ldc指令 : int、float或String型常量从常量池推送至栈顶
第三行
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
调用PrintStream的println方法.
第四行
同第一行
第五行
11: aload_0
将this引用变量入栈
第六行
12: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
打印this
第七,八行
15: aload_0
16: invokespecial #6 // Method hello:()V
这里是 invokespecial ,说明是执行的本实例的方法即Father类的hello方法.
第九,十行
19: aload_0
20: invokevirtual #7 // Method hi:()V
这里是invokevirtual 指令,说明是调用的虚方法,也就是执行的子类的方法。
上面就已经解释了,什么时候调子类的,什么时候是父类的,通过看jvm指令就知道了。