DAY4:扩充--类文件的结构

437 阅读7分钟

相信进入到本篇的同学一定是精益求精的,追根究底的人,话不多说直接开始吧。

为了尽量保证字段的完整性,我们重新写一个类,来分析这三块 这个类继承了父类TestClass,实现了ExtendTest接口

public class ExtendTestClass  extends TestClass implements ExtendTest{

  private  int sum;

  private static final String str = "学习使我快乐~";

  @Override
  public int inc() {
    return sum + 1;
  }

  @Override
  public int reduce() {
    return sum - 1;
  }
}

一、快速过一下前面5个模块

1.魔数字和版本号

不说了

2.常量池26个常量如下

3.访问标志位

依旧还是0x0021这里就不再多说了

4.类索引、父类索引与接口索引集合

类引用0x0003,父类引用0x0004 接口集合计数器0x0001因为我们这里让他实现了ExtendTest接口。

二、字段表、方法表和属性表

1. 字段表集合

字段表(field_info)用于描述接口或者类中声明的变量。Java语言中的“字段”(Field)包括类级变量以及实例级变量,但不包括在方法内部声明的局部变量。

字段的7个修饰符:

1.字段的作用域

2.static修饰符

3.final可变性

4.可见性volatile

5.可否序列化transient

6.字段数据类型

7.字段名称

其中字段作用域、static修饰符、final修饰符、volatile、transient这几项是很确定的,要么有要么没有,所以拿标志位来描述最适合不过。而字段类型和字段名称都是自定义的,java类是无穷无尽的,字段名称是千奇百怪的,所以这两项我们是通过引用常量池里面的常量来描述的。

字段表结构

access_flags查询表

descriptor_index查询表

下面解析一下本例中字段表集合

(1) 常量m

00 02 00 06 00 07 00 00

按照字段表结构一个字节一个字节来解释给大家看一下

  1. **u2 access_flags:**0x0002 表示访问标志是private

  2. **u2 name_index:**0x0006 表示简单名称sum,查询常量池#6

  3. **u2 descriptor_index:**0x0007,表示类型描述 I 查询,查询常量池#7

  1. u2 attributes_count: 0x0000 表示属性表集合计数器,0x0000表示没有属性表集合这项描述

(2) 常量str

00 1A 00 08 00 09 00 01 00 0A 00 00 00 02 00 0B

按照字段表结构一个字节一个字节来解释给大家看一下

  1. **u2 access_flags:**0x001A 表示访问标志是private static final三个值的位与运算 0x0002|0x0008|0x0010 = 0x001A

  2. **u2 name_index:**0x0008 表示简单名称str,查询常量池#8

  3. **u2 descriptor_index:**0x0009,表示类型描述 Ljava/lang/String,查询常量池#9

  1. u2 attributes_count: 0x0001 表示属性表集合的计数器为1。

  2. 为了本小节的完整性,把属性表结构提到这边先讲一下

u2 attribute_name_index:0x000A 属性表名称,对应到常量池的#10

Constant 属性的字段结构如下:

具体是什么含义我们在第三小节会简单介绍。

u4 attribute_length:0x00 00 00 02 表示长度为2

u2 Constantvalue_index :0x000B

到这里包含两个常量的常量表集合讲完了。下面是更为复杂的方法表集合。

2. 方法表集合

这个活有点费篇幅,为了让内容看起来不那么拖沓冗长,这边就举例第一个方法,后面两个方法需要同学们自己跟着第一个方法的思路来解析。

从0x0003开始是方法表集合计数器的开始,表明这里一共是3个方法我们结合class文件就能清楚看到

所以我们这边讲的是构造函数这个方法

方法表的结构如下

第一个参数access_flags是方法的访问控制符,和字段表的作用是一样的,但是其中的成分稍微有些不同

方法表集合解析举例

1.紧跟方法表集合计数器的是第一个方法了,按照方法表结构来套就行

2.第一个u2 access_flags :0x0001表示是ACC_PUBLIC

3.第二个u2 name_index :0x000C 10进制表示是12,对应的常量表值为

也就是初始化方法

4.第三个 u2 descriptor_index:0x000D,10进制为13

5.和字段表一样,紧接着的一个u2 attributes_count为属性表计数器。这里为0x0001,表示属性表有一个值,对应的常量表索引值为0x000E,也就是10进制的14

这是一个code属性的属性值

6.code属性的属性值的结构如下

7.我们一个参数一个参数来解释一下

00 0E 00 00 00 1D 00 01 00 01 00 00 00 05 2A B7 00 01 B1 00 00 00 01 00 0F 00 00 00 06 00 01 00 00 00 08

u2 attribute_name_index:0x000E

u4 attribute_length:0x0000001D

u2 max_stack:0x0001

u2 max_locals:0x0001

u4 code_legth:0x00000005

u1 code:0x2AB70001B1

u2 exception_table_length:0x00 00

exception_info exception_table: 由于上一个参数计数为0所以这一项参数不存在

u2 attribute_count:0x00 01 这里表明又有一个属性表

所以属性表集合里面套属性表也是存在的,这就是方法表集合相对比较复杂的地方。

继续追踪到属性表集合 u2 attribute_name_index:0x000F 对照常量表

这是是一个LineNumberTable属性 u2 attribute_name_index:0x000F

u4 attribute_length:0x00 00 00 06

u2 line_number_table_length:0x0001

line_number_info line_number_table 每一个line_number_table又包含一个u2的start_pc和一个u2的line_number,所以值为0x00 08

到这里第一个方法也已经解析完成了,剩下两个方法,这里留给大家自己去操作一下,这里给出两个方法所包含的16进制

第二个方法
00 01 00 10 00 11 00 01 00 0E 00 00 00 1F 00 02 00 01 00 00 00 07 2A B4 00 02 04 60 AC 00 00 00 01 00 0F 00 00 00 06 00 01 00 00 00 10

第三个方法
00 10 00 01 00 12 00 11 00 01 00 0E 00 00 00 1F 00 02 00 01 00 00 00 07 2A B4 00 02 04 64 AC 00 00 00 01 00 0F 00 00 00 06 00 01 00 00 00 15

3. 属性表集合

属性表集合除了在字段表集合和方法表集合中存在以外,在class字节码的最后两个类型字段就是单独用来描述属性表集合的。

方法表集合解析完成之后最后还剩余一个小尾巴

00 01 00 13 00 00 00 02 00 14

u2 attribute_count:0x0001

表明属性表计数器为1

u2 attribute_name_index:0x0013 对应10进制为19,在常量池中找到

这是一个叫SourceFile的属性表集合 u4 attribute_length:0x00000002 u2 sourcefile_index:0x0014

到这里我们已经把class文件的所有字节码都已经解析出来了,这里留给大家的课后作业就是剩余两个方法表的解析需要大家自己去操作一下~

感谢大家 点赞👍➕关注