在 字节码(一)中,我们介绍了字节码的基本信息。本节内容我们将深入分析字节码文件。其中涉及到的字节码例子详细内容详见 字节码(一)1.2 字节码查看
一、字节码文件
字节码看似杂乱,其实不然,它有自己的数据组织方式;
+---------------+---------------+---------------+
| 魔数(u4) | 小版本(u2) | 大版本(u2) |
+---------------+---------------+---------------+
| 常量池大小(u2) | 常量池数据 |
+---------------+---------------+---------------+
| 访问标记(u2) | 当前类(u2) | 父类(u2) |
+---------------+---------------+---------------+
| 接口数量(u2) | 接口 |
+---------------+-------------------------------+
| 字段数量(u2) | 字段 |
+---------------+-------------------------------+
| 方法数量(u2) | 方法 |
+---------------+-------------------------------+
| 属性数量(u2) | 属性 |
+---------------+-------------------------------+
u1代表1个字节;
u2代表2个字节;
u4代表4个字节;
u8代表8个字节;
u代表Unsigned,无符号
二、 常量池
常量池中存储两类常量:字面量与符号引用。字面量为代码中声明为final
的常量值,符号引用如类和接口的全局限定名、字段的名称和描述符、方法的名称和描述符。常量池整体上分为两部分:常量池长度以及常量池数据区。
2.1、常量池长度
- 字节码文件第
9、10
字节; - 例: 0x0021表示有33个常量(常量池计数器是从
1
开始计数的,将第 0 项常量空出来是有特殊考虑的,索引值为 0 代表“不引用任何一个常量池项”);- 详细内容详见 字节码(一)1.2 字节码查看
- 详细内容详见 字节码(一)1.2 字节码查看
2.2、常量池内容
+---------------+---------------+---------------+---------------+
| 常量类型tag(u1)| 常量值 |常量类型tag(u1)| 常量值 |
+---------------+---------------+---------------+---------------+
| 常量类型tag(u1)| 常量值 |
+---------------+---------------+---------------+---------------+
……
+---------------+---------------+---------------+---------------+
| 常量类型tag(u1)| 常量值 |常量类型tag(u1)| 常量值 |
+---------------+---------------+---------------+---------------+
2.2.1常量类型
常量类型 | tag | 描述 |
---|---|---|
CONSTANT_Utf8_info | 1 | UTF-8编码字符串 |
CONSTANT_Integer_info | 3 | 整型字面量 |
CONSTANT_Float_info | 4 | 浮点型字面量 |
CONSTANT_Long_info | 5 | 长整形字面量 |
CONSTANT_Double_info | 6 | 双精度浮点型字面量 |
CONSTANT_Class_info | 7 | 类或接口的符号引用 |
CONSTANT_String_info | 8 | 字符串类型字面量 |
CONSTANT_Fieldref_info | 9 | 字段的符号引用 |
CONSTANT_Methodref_info | 10 | 类中方法的符号引用 |
CONSTANT_InterfaceMethodref_info | 11 | 接口中方法的符号引用 |
CONSTANT_NameAndType_info | 12 | 字段与字段类型或方法与方法类型的符号引用 |
CONSTANT_MethodHandle_info | 15 | 表示方法句柄 |
CONSTANT_MethodType_info | 16 | 表示方法类型 |
CONSTANT_InvokeDynamic_info | 18 | 表示一个动态方法调用点 |
2.2.2 常量内容
- 不同的常量类型,其内容结构不一样,内容长度也不一样;下面提到的
idx
均为常量池中的有效索引(常量池计数器是从1
开始计数) - 例子详细内容详见 字节码(一)1.2 字节码查看
2.2.2.1 CONSTANT_String_info
+---------------+
| 字符串idx(u2) |
+---------------+
Eg:
#13 = String #14 // ByteCode
2.2.2.2 CONSTANT_Class_info
+---------------+
| 类名idx(u2) |
+---------------+
Eg:
#16 = Class #18 // java/io/PrintStream
2.2.2.3 CONSTANT_Fieldref_info 、CONSTANT_Methodref_info、CONSTANT_InterfaceMethodref_info
+---------------+----------------+
| 类名idx(u2) |名字和类型idx(u2) |
+---------------+----------------+
Eg:
#7 = Fieldref #8.#9 // java/lang/System.out:Ljava/io/PrintStream;
#1 = Methodref #2.#3 // java/lang/Object."<init>":()V
2.2.2.4 CONSTANT_Integer_info、CONSTANT_Float_info
+---------------+
| 数字(u4) |
+---------------+
2.2.2.5 CONSTANT_Long_info、CONSTANT_Double_info
+---------------+---------------+
| 数字高位(u4) | 数字低位(u4)|
+---------------+---------------+
2.2.2.6 CONSTANT_Utf8_info
+---------------+---------------+
| 长度(u2) | 具体内容 |
+---------------+---------------+
Eg:
#14 = Utf8 ByteCode
2.2.2.7 CONSTANT_NameAndType_info
+---------------+---------------+
| 名字Idx(u2) | 描述Idx(u2) |
+---------------+---------------+
Eg:
#3 = NameAndType #5:#6 // "<init>":()V
2.2.3 描述符
常量池中 #28 = Utf8 ([Ljava/lang/String;)V
是个啥?
例子详细内容详见 字节码(一)1.2 字节码查看
- 1、图中
([Ljava/lang/String;)V
为方法描述符对应void main(java.lang.String[])
;我们来拆解下:
([Ljava/lang/String;) V
------------------- -
方法形参字段描述符 ^
|
返回类型描述
- 2、图中
[Ljava/lang/String;
为字段描述符,对应java.lang.String[]
;我们来拆解下:
[ L java/lang/String ;
- - --------------- -
^ ^ 引用类型类名(2) 引用类型结束标识(3)
| |
| 引用类型标识符开始标识(1)
数组
- 3、描述符速查表
标识符 | 标识类型 | 备注 |
---|---|---|
B | byte | |
C | char | |
D | double | |
F | float | |
I | int | |
J | long | L被对象类型用了,换用J |
L ClassName ; | reference | 对象类型 |
S | short | |
Z | boolean | B被byte用了,换用Z |
[ | reference | 一维数组标识,二维‘[[’,以此类推 |
V | void | 返回void |
问:Object m(int i, double d, Thread t) {...}
对应的方法描述符是啥?
答:(IDLjava/lang/Thread;)Ljava/lang/Object;