.class文件结构解析
工具
我们可以使用javap -verbose,配合记事本来对照着看字节码文件
.class文件结构
class文件通过记事本打开,可以得到16进制的一个文件,其中结构如下:
魔数
4个字节,魔数固定为cafe baby
副版本号
2个字节,表示jdk的次版本号
主版本号
主版本号两个字节,假设为00 34,它的10进制为52,52代表的是1.8,51代表的是1.7,依此类推
常量池计数器
记录常量池数据区中常量的数量
常量池数据区
常量池数据区的数据数量是常量池计数器的数量-1。例如:常量池计数器为00 19转成10进制即为25,这个时候常量池数据区应该有24个常量。原因是第一个常量池JVM存了null
常量池结构
u1,u2,u4,u8分别代表1个字节,2个字节,4个字节,8个字节的无符号数
常量池一般存放两种常量,字面量和符号引用
描述信息
在JVM规范中,每个字段或者变量都有描述信息,描述信息的主要作用是 数据类型,方法参数列表,返回值类型等.
1)基本参数类型和void类型都是用一个大写的字符来表示,对象类型是通过一个大写L加全类
名表示,这么做的好处就是在保证jvm能读懂class文件的情况下尽量的压缩class文件体积.
基本数据类型表示:
B---->byte
C---->char
D---->double
F----->float
I------>int
J------>long
S------>short
Z------>boolean
V------->void
对象类型:前面加一个L,然后把.改为/
String------>Ljava/lang/String;(后面有一个分号)
对于数组类型: 每一个唯独都是用一个前置 [ 来表示
比如: int[] ------>[ I,
String [][]------>[[Ljava.lang.String;
用描述符来描述方法的,先参数列表,后返回值的格式,参数列表按照严格的顺序放在()中
比如源码 String getUserInfoByIdAndName(int id,String name) 的方法描述符号
(I,Ljava/lang/String;)Ljava/lang/String;
访问标识
解析我们的class文件是类还是接口,是否定义为public的,是否是abstract,是否被final修饰
两个访问修饰符直接通过位运算来实现的。
例如现在有一个class文件访问标识为00 21。那么实际它代表的是0x0021 = 0x0020 位运算 0x0001
,即它代表这个class的访问权限是ACC_PUBLIC和ACC_SUPER
类索引
描述当前所属的类,它存储着常量池中的位置,例如00 03,就代表这个索引指着常量池中第三个常量,那么第三个常量的名称就是这个类的名称。
父类索引
当前类的父类名字,同类索引,存的也是常量池中的位置。
接口计数器
计数,记录实现了几个接口
接口信息计数区
会存位于常量池中的索引,同类索引和父类索引
字段信息
同样的,前两位为字段的数量,后面就是n个字段,它的作用是描述类和接口中声明的变量,包括类变量和实例变量,但是不包括方法的局部变量
字段的结构如下:
假设有个字段在class文件中为00 02 00 05 00 06 00 00
所以00 02 表示访问修饰符号为ACC_PRIVATE
所以00 05 表示的是字段的名称 指向的是常量池中第五个常量
所以00 06是我们的字段的描述符: 指向的是常量池中第六个常量
00 00 表示是属性表的个数 这里为0表示后面是没有属性表集合
方法信息
方法与字段类似,其结构如下:
假设有个方法在class文件中为 00 01 00 07 00 08 00 01
00 01:表示的是方法的修饰符 表示的是acc_public
00 07:表示的是方法的名称 表示指向常量池中第7个常量,表示方法的名称
00 08:方法的描述符号,表示指向常量池第八个常量
00 01表示有一个方法属性的个数
注意: