这个就不敲了。。。Class文件格式如下图,class文件就是用下面的的结构组织起来的
u2表示2个字节 u4表示4个字节
常量池的一些类型
各常量的结构如下图(不全)
写了一个简单的java类
package vm.c2;
public class ClassStructure {
private int liu ;
public void sumMult(){
System.out.println(999*100);
}
public int increment(){
return liu+1;
}
}
编译成class文件并且使用十六进制打开 我用的软件是 binary viewer
javap -verbose class文件 用来将字节码 展示给人看懂的结构,实际后面
谈的的解析字节流 最终就是得到这么个类型结构
0000行(16进制,1个长度4位 2个长度代表1个字节)
CA FE BA BE : 类文件结构提到了 有4个字节 即魔数 为CAFE BABE固定的,用来判断是否是class
00 00 00 34 :大版本 小版本 可以通过这个来区分java版本
00 21 :2个字节的常量池 表示有32(16*2+1)个常量 索引范围是 [1-33)不包括33
第一个常量
0A 00 07 00 13 :表示(根据常量类型)表示方法的符合引用查表得 有一个u1的tag ,u2的指向class_info索引 , u2类型的nameAndType;tag就是0A,00 07表示指向第7项常量 又指向26项常量即 java/lang/Object 只不过把包名的.改成了/,00 13表示指向第19项常量,19又分别指向常量10和11 即 <init> ()V 表示类的构造方法 并且返回值为空
第二个常量
09 00 14 00 15:查表得Filed_ref_info 有一个u1 tag,一个u2 类型指向class_info索引,一个u2类型的nameAndType
tag就是09 ,00 14表示第20项常量 又指向27 即java/lang/System,0015表示第21项常量 指向常量 28,29。意思是名字叫out对象为Ljava/io/PrintStream(后面会再 类链接阶段进行替换)
第三个常量
03 表示整型字母量 查表的 u1 tag 即03 ,剩下就是bytes java规定 int占四个字节 值为00 01 86 3C
转换为10进制就是99900也就是 编译成class文件的时候 帮我们计算出来了,这个就是java编译的优化,所以别在代码显示
去吧计算好的值填进去,直接写999*100就行了。
后面的常量 基本上都是这个套路就不一一写明了
常量池结束后就是类访问标志,
调到这个 可以看上面-verbose的输出,定位到最后一个常量完就是
这个值在0130行的00 21
按照上面的访问标志 00 21 表示acc_super和acc_public然后做|运算 可知类是public的
然后就是 this_class
u2类型 00 06 定位到常量池的第6项 即vm/c2/ClassStructure
再就是super_class
u2类型 00 07定位到常量池的第7项 java/lang/Object
再就是 interface_count
u2类型 00 00 表示无接口 就忽略interfaces
再就是fields_count
u2类型 00 01 表示 属性为个数为1
再就是字段fields
类型为field_info 的结构如下
类型 | 名称 | 数量 |
u2 | access_flags | 1 |
u2 | name_index | 1 |
u2 | decriptor_index | 1 |
u2 | arrtubutes_count | 1 |
attribute_info | attributes | attributes_count |
access_flags 00 02表示 字段为私有的
name_index 00 08 表示 名称 索引为第8项常量 liu
decriptor_index 00 09表示 简单名称 I表示int
简称对应如下(B byte,C char,D double ,F float,I int ,J long , S short , Z boolean ,L 对象类型)
[[Ljava/lang/String 表示 二维字符串数组 , toString() 简称描述符()Ljava/lang/String ()表示无参数后面是返回值
arrtubutes_count 00 00 没有额外的附加信息
下面就是method_count了
00 03表示有三个方法
方法访问标志
方法表结构
类型 | 名称 | 数量 |
u2 | access_flags | 1 |
u2 | name_index | 1 |
u2 | decriptor_index | 1 |
u2 | arrtubutes_count | 1 |
attribute_info | attributes | attributes_count |
第一个方法
00 01 访问标志 表示方法是 public
00 0A 第10项常量<init>方法
00 0B 11项常量 ()V表示返回值为空
00 01表示 属性数量为1
0C 表示第12想常量Code(表示code属性)
下面就是一个4个字节的属性长度 属性长度为整个属性表的长度-6(名称和长度占6个)
00 00 00 1D表示29个长度
00 01表示最大栈长度 1
00 01表示本地变量表的长度
00 00 00 05表示code_length长度为5 表示有5个字节码指令
分别为 0x2A 0x B7 0x 00 0x01 0xB1
这个可以查字节码指令表分别对应起来为
aload_0 第0个Slot加载操作栈,
ivokespecial 表示将栈顶的引用类型为所指向的对象,的构造方法 此指令有一个u2长度的参数为
即<init> 符合引用,
然后就是return
其他方法类似 将直接分析javap后的 比如increment方法
操作栈的大小为2, 本地变量表的大小为1 ,参数为1(this是实例方法的第一个slot)不然你代码的this哪来的
引用入操作栈,
获取liu字段的值,并且加载到栈顶
加载常量1到操作栈顶
相加栈顶两个值 并且弹栈,将结果入栈
返回一个int值