在上一篇博客中介绍了《Class文件结构》,其中就提到了一个例子,下面我们依然根据该例子的字节码来对方法的执行流程进行讲解。
java类源码如下:
public class TestClass{
private int num;
public int inc(){
for(int i=0; i<10; i++){
num = i;
}
return num;
}
public static void main(String[] args){
new TestClass().inc();
}
}
使用javap -verbose命令反编译后,输出常量表和字节码如下:
D:\JVM>javap -verbose TestClass Compiled from "TestClass.java" public class TestClass extends java.lang.Object SourceFile: "TestClass.java" minor version: 0 major version: 50 Constant pool: const #1 = Method #6.#20; // java/lang/Object."<init>":()V const #2 = Field #3.#21; // TestClass.num:I const #3 = class #22; // TestClass const #4 = Method #3.#20; // TestClass."<init>":()V const #5 = Method #3.#23; // TestClass.inc:()I const #6 = class #24; // java/lang/Object const #7 = Asciz num; const #8 = Asciz I; const #9 = Asciz <init>; const #10 = Asciz ()V; const #11 = Asciz Code; const #12 = Asciz LineNumberTable; const #13 = Asciz inc; const #14 = Asciz ()I; const #15 = Asciz StackMapTable; const #16 = Asciz main; const #17 = Asciz ([Ljava/lang/String;)V; const #18 = Asciz SourceFile; const #19 = Asciz TestClass.java; const #20 = NameAndType #9:#10;// "<init>":()V const #21 = NameAndType #7:#8;// num:I const #22 = Asciz TestClass; const #23 = NameAndType #13:#14;// inc:()I const #24 = Asciz java/lang/Object; { public TestClass(); Code: Stack=1, Locals=1, Args_size=1 0: aload_0 1: invokespecial #1; //Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 1: 0 public int inc(); Code: Stack=2, Locals=2, Args_size=1 0: iconst_0 //定义一个常量0,放入操作数栈 1: istore_1 //把该常量弹出栈顶存入到局部变量表 2: iload_1 //把该局部变量放入操作数栈 3: bipush 10 //把常量10放入操作数栈 5: if_icmpge 19 //把i和10进行比较 8: aload_0 //加载局部变量表index为0的变量放入操作数栈 9: iload_1 //加载局部变量表index为1的变量放入操作数栈 10: putfield #2; //Field num:I //把i的值赋给num字段 13: iinc 1, 1 //局部变量i自增1 16: goto 2 //跳转到第2行 19: aload_0 //加载局部变量表index为0的变量放入操作数栈 20: getfield #2; //Field num:I //获取字段num的值 23: ireturn //返回 LineNumberTable: line 5: 0 line 6: 8 line 5: 13 line 8: 19 StackMapTable: number_of_entries = 2 frame_type = 252 /* append */ offset_delta = 2 locals = [ int ] frame_type = 250 /* chop */ offset_delta = 16 public static void main(java.lang.String[]); Code: Stack=2, Locals=1, Args_size=1 0: new #3; //class TestClass 3: dup 4: invokespecial #4; //Method "<init>":()V //调用实例初始化方法 7: invokevirtual #5; //Method inc:()I //调用普通方法inc() 10: pop 11: return LineNumberTable: line 12: 0 line 13: 11 }
方法的调用指令分为以下几种:
- invokevirtual指令用于调用所有的虚方法。
- invokeinterface指令用于调用接口方法,它会在运行时搜索一个实现了这个接口方法的对象,找出适合的方法进行调用。
- invokespecial指令用于调用一些需要特殊处理的实例方法,包括实例构造器<init>化方法、私有方法和父类方法。
- invokestatic指令用于调用静态方法(static方法)。
其它具体的指令可以参考:字节码指令集