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

文章图片
由上面这些信息可以推导源代码定义的字段为privateinta;和源码相符 。
懂了字段表之后 , 方法表结构几乎和字段表结构是一模一样的 , 通过访问标志、名称索引、描述符索引可清楚的表达方法的定义 。 除了一些标志位不同 , 毕竟有些修饰符可以修饰方法不能修饰字段 , 有些修饰符可以修饰字段但是方法没有 , 内容如下对比字段表标志有添加有删减有相同:

文章图片
重载(Overload)一个方法:
要与原方法具有相同的简单名称要与原方法有不同的特征签名(特征签名就是一个方法中各个参数在常量池中字段符号的引用集合 , 因为返回值不在特征签名里面 , 所以返回值不同作为重载条件)
文章图片
u20x0004->第一个跟着的是方法数量,这个类有4个方法表数据 , 分别是add(),multi(),main()和构造器方法以下是第一个方法表内容:u20x0001public为真 , 其他为假u20x000d方法简单名称name_index,由上面JAVAP常量表可知#14为()v,v由描述符含义可知是特殊类型void,()代表无参数 , 即构造函数u20x000e方法描述符descriptor_index,由上面JAVAP常量表可知#15指Code,Code之后再讲u20x0001attribute_count,属性表集合有一项属性用于存储一些额外信息attribute_info0x000f由JAVAP看到的指令 , 指向#15,即对应常量”Code“ , 说明此属性是方法的字节码描述复制代码
文章图片
第一个方法:

文章图片
讲了大半年 , 还只是讲了字段 , 方法头这些内容可以通过访问标志 , 名称索引 , 方法描述符来表达清楚 , 这些都是些元数据 , 那么方法体上哪去了呢?这就要属性表出山啦!
有眼力见的朋友可能已经讲字段表和方法表的时候 , 就发现了属性表的踪影 , 用来描述某些场景专有信息的 , 与上面讲到的其他的数据项目不同的是 , 其他数据项目要求严格的顺序 , 长度和内容 , 属性表的限制是放养状态 , 不要求各个属性表具有严格的顺序 , 只要不与已有的属性名重复 , 任何人实现的编译器都可以向属性表中写入自己定义的属性信息 , JVM会运行时会忽略掉他不认识的 。
Java程序方法体中的代码经过Javac编译处理后 , 最终变为字节码指令存储在Code属性中 , Code属性出现在方法表的属性集合之中 。 但并非所有方法表都有Code属性 , 例如抽象类或接口 。
code属性表结构如图:

文章图片
attribute_name_index指向CONSTANT_Utf8_info类型常量的值固定为“Code”attribute_length标识属性值的总长度max_stack代表了操作数据(OperandStacks)深度的最大值max_locals代表了局部变量所表示的存储空间单位:Slotcode_length和code是用来存储Java源程序编译后产生的字节码指令 , codelength代表字节码长度 , code是用于存储字节码指令的一系列字节流 。 字节码指令 , 每个指令字节码代表的指令含义 , 是否需参数 , 是u1类型的单字节 , 取值范围是0x00~0xFF , 即0~255 , 一共可以表达256条指令 , 目前JVM规范已经定义了约200条指令了 。属性有很多的 , JAVA虚拟机规范预定义了21项 , 我们平时能看到的都有

文章图片
还是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开始计算了 。
推荐阅读
- 南宁邦|当街跪地被母亲骂:“这辈子你去坐牢啊!”,惨!南宁一学生开摩托撞死路人
- 爱游戏的甲子|G2战队“杀人诛心”,阿P用两句话嘲讽LCK战队,DWG被逼入绝境
- 老猫要有腹肌|AG超玩会战队人气位列榜首,久诚人气不及一诺
- 财经女记者部落|老板却坚决拒绝粉丝经济,选手只招大学生,电竞战队斩获《炉石传说》联赛冠军
- 电竞小肥仔|和平精英:战队专属皮肤推荐合集,最后JDE队服堪比至尊金龙!
- 海军陆战队|美两大军种争资源,抢着对抗中国?美专家:对手导弹太多,应互补
- 4am战队|绝地求生秋季赛决赛第一日-rng登顶,4am稳定,ifty、tianba拉闸
- 青年|太丢人了!LNG首发五人韩服排在同一局,却被LDL选手+路人暴打!
- 麻醉说游戏|KPL秋季赛一半赛程已过!这两支战队即将提前锁定胜者组?
- 子非鱼|穿书:这个路人的出镜率比宁婴婴和明帆还要高,影分身都学会了!
