java字节码划分区域
图中u4、u2等表示的是字节u4就是4个字节u2就是2个字节以此类推
idea查看class的十六进制的方式
看这里 https://blog.csdn.net/weixin_45112292/article/details/115609491
开始分析准备解析
其中魔术就是magic它表示的是字节码的开头CAFEBABE这个单词
然后次版本号也就是minor version
然后主版本号majorversion
然后常量池。。。现在应该会看图了后面的不写了
对应的次版本号与主版本号
然后常量池类型图 先看一个简陋的图
描述符
* byte B // 这个B就是描述符 描述的是byte类型 下面都是同理* char C* short S* int I* float F* long J 注意,不是L* double D* void V** 引用类型 Ljava/lang/StringLjava/lang/String; 这个引用类型是L* 数组 [[[[I => 一维int类型的数组 数组是[ 一个[是一维数组两个是二维数组也就是[[这样N* 维数组就是N个[* * [Ljava/lang/String; 这样就表示一个String类型的数组 String[] 这样的
描述符图
修饰符的标记
大端小端 就是数据在内存中的样子可能是反着的
描述的列值为1 这个下面看字节码
上面字节码对应的代码
public class Am {public static void main(String[] args) {System.out.println("我是狗 我是狗");}
}
然后开始根据字节码获取它的常量池 字节码是十六进制的
首先字节码的前八个数也就是CA FE BA BE 00 00 00 34
CA FE BA BE是魔术
00 00 是次版本号
00 34是主版本号 翻译成十进制就是52是jdk1.8
然后有两个字节的常量池大小 也就是00 22 表示常量池大小是44(22的十进制是44)如果不确定可以下载idea的插件jclasslib进行查看 jclasslib显示的常量池是33个它不显示第0个位置上的当前主流解释第0个位置是存放的this
然后我们开始读取常量池常量池的大小用两个字节表示然后是0A 十进制的是10 也就是找常量池值为10的也就是
CONSTANT_Methodref_info它的作用就是指向类的全限定名最终会指向CONSTANT_Utf8_info这
个会记录一个字符串这个字符串就是全类名我这里的是<java/lang/Object>这个Object的
然后CONSTANT_Methodref_info还会指向字段或方法名与方法的描述符或字段的描述符 描述符据图的参考上面的前置知识常量池类型图
然后后面00 06这里就表示读取第六个常量池中的值这个00 06就是符号引用它会用#6表示
上图是用jclasslib看到的可以跟字节码对应上
然后读取00 14 十进制就是20 表示的是方法名 与 返回值 与参数参数是() 表示没有参数 、V 表示返回值 这里的是voidinit 方法名
然后第一个常量池就解析完了 然后就是下一个这时读取两个字节也就是09 看上面的图既可以明白后面的常量池也是很第一个一样的套路 看看上面常量池类型的图中的介绍就懂了
然后解析一个方法
从选中的00 07开始就是方法了它前面的00 01是方法的访问权限public为1private为2 再前面的00 02是成员方法的个数 成员属性的也跟它同理
通过jclasslib找到的
然后先看方法在字节码中的布局
这是字段
这是方法
方法与属性中的数据类型
从这开始上面是前置知识 前置知识还有个进制 class使用十六进制编码不懂进制百度一下
00 07是方法的名字
00 08是方法的参数返回值等信息
后面是00 01是对应的attributes_count这个表示有一个attributes(attributes表示一个属性)然后后面是00 09这时就进入Code_attribute中了00 09对应Code_attribute中的attributes_name_index然后它指向的是第九个常量池
然后
对应的就是code
然后往后读就是u4也就是读取4个字节也就是 00 00 00 2F转换为十进制就是47
jclasslib中也是47很显然这不是巧合 然后就表示后面47个字节都是code的也就是对应Code_attribute的attribute_length然后继续往下00 001表示栈的深度这里是1对应Code_attribute中的max_stack然后继续往后读00 01对应Code_attribute的max_locals表示局部变量表的深度在这里是1
然后读取4个字节00 00 00 05这里对应字节码指令的长度
2A B7 00 01 B1这5个
2A是指令名字
2A B7 00 01 B1
这个00 01表示操作数这里操作的是#1
就是调用初始化方法
B7与B1可以理解为开始与结束
然后往后读两个字节是
00 00 表示异常数这里是0 这里对应exception_table_length
然后往后读是 00 02这里表示一个属性的长度对应attributes_count
然后读取两个字节是00 0A 十进制就是10然后找第十个常量池
是LineNumberTable 这个LineNumberTable是用来记录行号的 00 0A就以及进入LineNumberTable的结构了00 0A表示的是attribute_name_index
然后它的结构上面有
然后进入LineNumberTable_attribute中
读取四个字节
长度是00 00 00 06 这个好像没有用
然后又是两个字节长度
00 01对应attribute_name_index
然后是 00 00 对应的谁看下图
然后是 00 01 对应的谁看下图 line_number是java代码的行号的索引
jclasslib中
然后继续往后读是 00 0B 对应的是LocalVariableTable(局部变量表)
往后读4字节00 00 00 0C转为十进制是12 这个表示的是局部变量表的长度
jclasslib中
然后再读取两个字节 00 01 是local_variable_table_length 局部变量的长度 这里的局部变量是this
往后读就是00 00 对应start_pc
往后读就是 00 05 对应length
start_pc与length之间称为变量的作用域 这是0-5
init代码是0-4也就是执行完return 这个this就无效了
然后继续往后读两个字节00 0C
这里是this然后往后读两个字节00 0D转为十进制就是13 然后指向第13个常量池
jclasslib中的结果
到这字节码就解析完了 通过步骤跟上面的图做对比就能全明白了
上面的步骤只解析了常量池与init方法其他方法也是跟init解析方式一样
套着上面图中的描述一步一步的走就可以