Java字节码二进制解析,看懂每一个字节
基础环境:
-
idea先安装jclasslib(这个不多说)和BinEd(这个用于查看字节码文件的二进制)
-
示范代码(一个简单的HelloWorld):
package com.czh.demo; public class HelloWorld { public static void main(String[] args) { System.out.println("Hello World!!!"); } }
-
编译成字节码文件,同时我们也可以看到编译器给我们加了一个构造方法
-
BinEd查看二进制文件
抽象认识字节码
-
先看一张图,前面的u代表字节,u4就是4字节,**!**就是字节数无法确定(请仔细看这张图)
-
让我们先根据这张图片抽象分析一下二进制文件
-
我知道你的疑惑。为什么我知道常量池是哪里到哪里,方法的值是哪里到哪里,以及类属性值是哪里到哪里?
-
首先怕有些人没看出来,我这个类实现的接口个数和成员数量都是0,所以文件二进制中就没有间隔
细🔒常量池
-
首先我们看常量池大小是0x22也就是33,我们使用jclasslib看一下确实是33个
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AOc3fr59-1660785036456)(/Users/zhchen/Library/Application Support/typora-user-images/截屏2022-08-17 下午9.58.49.png)]
-
先补充一下常量池的11种类型数据的结构(很关键!!!)
-
那么我们就可以根据占一个字节的tag知道它是什么类型的常量,也就确定了字节长度,对与utf8类型的常量,我们可以根据长度再得到其占的字节数,于是就有:
细🔒方法值
-
方法值的组成:可以看到一个method_info中含有access_flags(访问控制权限),name_index(名称在常量池的index),descriptor_index(方法签名),attribute_info(属性)
-
在看attribute的组成,atrribute_name_index,attribute_length,info,其中我们知道了attribute_length便知道了attribute_info的长度
-
于是我们可以得到
-
现在我们只看第一个方法
它的name_index是07,所以我们在jclasslib里看一下编号为7的常量池,刚好就是init,也就是第一个方法的名字
-
同样是第一个方法,我们再看它的attributes_count的值是1,说明只有一个attribute,并且它的attribute_name_index值是09,那么我们在常量池里找到9号字符串常量是Code
-
jclasslib验证init方法的attribute时Code,同时也验证了attribute_length为0x2F也就是属性长度47
细🔒属性值
- 上面那张图我们可以发现,Code下面又有二个东西,LineNumberTable和LocalVariableTable,我们先看这几张图就知道了
-
看来不同的属性存储的方式也不一样,而且Code属性中还包含LineNumberTable属性(代码所在行号,这就可以解释为什么报错能精准定位了)和LocalVariableTable属性(局部变量表),于是
细说类属性值
-
和前面类似,我们可以根据attribute_name_index发现它是SourceFile,那么我已经给出了SourceFile_attribute的结构了,觉得读者应该可以自己尝试下标出最后那一行了😀
一些补充的东西
- 主版本号与次版本号