路人战队|授人以鱼不如授人以渔,软妹手把手教你javap反编译分解代码( 五 )


文章图片
还是javap-verboseTestJVM将所有剩下的指令展示出来 , 可以看到方法的描述和调用
常量表前面已经贴出了{publicintb;descriptor:Iflags:ACC_PUBLICpublicTestJVM();descriptor:()Vflags:ACC_PUBLICCode:stack=2,locals=1,args_size=10:aload_01:invokespecial#1//Methodjava/lang/Object."":()V4:aload_05:iconst_36:putfield#2//Fielda:I9:aload_010:iconst_411:putfield#3//Fieldb:I14:returnLineNumberTable:line5:0line6:4line7:9LocalVariableTable:StartLengthSlotNameSignature0150thisLTestJVM;publicintadd();descriptor:()Iflags:ACC_PUBLICCode:stack=2,locals=1,args_size=10:aload_01:getfield#2//Fielda:I4:aload_05:getfield#3//Fieldb:I8:iadd9:ireturnLineNumberTable:line10:0LocalVariableTable:StartLengthSlotNameSignature0100thisLTestJVM;publicintmulti();descriptor:()Iflags:ACC_PUBLICCode:stack=2,locals=1,args_size=10:aload_01:getfield#2//Fielda:I4:aload_05:getfield#3//Fieldb:I8:imul9:ireturnLineNumberTable:line14:0LocalVariableTable:StartLengthSlotNameSignature0100thisLTestJVM;publicstaticvoidmain(java.lang.String[]);descriptor:([Ljava/lang/String;)Vflags:ACC_PUBLIC,ACC_STATICCode:stack=3,locals=1,args_size=10:getstatic#4//Fieldjava/lang/System.out:Ljava/io/PrintStream;3:new#5//classTestJVM6:dup7:invokespecial#6//Method"":()V10:invokevirtual#7//Methodmulti:()I13:invokevirtual#8//Methodjava/io/PrintStream.println:(I)V16:returnLineNumberTable:line18:0line20:16LocalVariableTable:StartLengthSlotNameSignature0170args[Ljava/lang/String;}SourceFile:"TestJVM.java"复制代码可以看到一共四个方法 , 和我们之前看到字节码推论到的数目一样 , args_size都为1 , 但是无论是实例构造器 , 还是add() , multi()方法都没有参数 , 这个的原因是:在任何的实例方法我们知道可以通过this.method()来进行调用 , 通过this来访问到此方法所属对象 , 他的实现就是通过javac编译器编译的时候把对this关键字的访问变成对一个普通方法参数的访问 , 然后在虚拟机调用实例方法时候自动传入此参数 , 因此在实例方法的局部变量表里面至少会存在一个指向当前对象实例的局部变量 , 局部变量表也会预留第一个slot位来存放对象实例的引用 , 其他的方法参数自然靠边站从1开始计算了 。
字节码分析 , 从上面的方法属性表位置开始:
路人战队|授人以鱼不如授人以渔,软妹手把手教你javap反编译分解代码
文章图片
attribute_name_index是一项指向CONSTANT_UTF8_INFO的常量索引 , 常量值固定为Code,代表了该属性的属性名称 。
看一段包含异常语法的简单代码:
/***@authorbyzengzhiqin*2020-09-13*/publicclassTestException{publicintinc(){intx;try{x=1;returnx;}catch(Exceptione){x=2;returnx;}finally{x=3;}}}复制代码再看其内容(字节码0~4行做的就是将证书1赋值给变量x,并且将x的值复制一份副本到最后一个本地变量表的slot中 , 这个slot里面的值在ireturn指令执行前将会被重读到操作栈顶 , 作为方法返回值使用 , 这个slot用returnValue表示):
0:iconst_0//常量0压入操作数栈1:istore_2//弹出操作数栈栈顶元素 , 保存到局部变量表第2个位置2:iload_0//第0个变量压入操作数栈顶3:iload_1//第1个变量压入操作数栈顶4:iadd//操作数栈中的前两个int相加 , 并将结果压入操作数栈顶5:istore_2//弹出操作数栈栈顶元素 , 保存到局部变量表第2个位置6:iload_2//加载局部变量表的第2个变量到操作数栈顶7:ireturn//返回8:aload//从局部变量表的相应位置装载一个对象引用到操作数栈的栈顶复制代码上面是一些需要用的的指令的相关解释


推荐阅读