本问主要介绍class字节码文件,即实例解析class字节码。
1.class文件
1.1 class文件结构
1.2 class文件实例子
class文件内容为16进制表示,1个字节占8位,能表示两个16进制数,即两个16进制数为1个字节,括号中为注释内容,(代表结构名称,占子节大小)
源码:
public class ByTeCodeTest1 {
public static String str="abc";
public int a = 1;
public String demo(String name) {
return name;
}
public static void main(String[] args) {
ByTeCodeTest1 byTeCodeTest1 = new ByTeCodeTest1();
int b = byTeCodeTest1.a;
}
}
class文件
(魔数,4)cafe babe (次版本号,2)0000 (主版本号,2)0034(常量池大小,2)0025 (常量池内容) (第一个常量)0a00 0700 1f(第二个常量)09
0003 0020 0700 210a 0003 001f 0800 2209
0003 0023 0700 2401 0003 7374 7201 0012
4c6a 6176 612f 6c61 6e67 2f53 7472 696e
673b 0100 0161 0100 0149 0100 063c 696e
6974 3e01 0003 2829 5601 0004 436f 6465
0100 0f4c 696e 654e 756d 6265 7254 6162
6c65 0100 124c 6f63 616c 5661 7269 6162
6c65 5461 626c 6501 0004 7468 6973 0100
254c 636f 6d2f 6a74 2f6c 6561 726e 2f62
7974 6563 6f64 652f 4279 5465 436f 6465
5465 7374 313b 0100 0464 656d 6f01 0026
284c 6a61 7661 2f6c 616e 672f 5374 7269
6e67 3b29 4c6a 6176 612f 6c61 6e67 2f53
7472 696e 673b 0100 046e 616d 6501 0004
6d61 696e 0100 1628 5b4c 6a61 7661 2f6c
616e 672f 5374 7269 6e67 3b29 5601 0004
6172 6773 0100 135b 4c6a 6176 612f 6c61
6e67 2f53 7472 696e 673b 0100 0d62 7954
6543 6f64 6554 6573 7431 0100 0162 0100
083c 636c 696e 6974 3e01 000a 536f 7572
6365 4669 6c65 0100 1242 7954 6543 6f64
6554 6573 7431 2e6a 6176 610c 000c 000d
0c00 0a00 0b01 0023 636f 6d2f 6a74 2f6c
6561 726e 2f62 7974 6563 6f64 652f 4279
5465 436f 6465 5465 7374 3101 0003 6162
630c 0008 0009 (最后一个常量)0100 106a 6176 612f 6c61
6e67 2f4f 626a 6563 74 (类的访问控制权限)00 21(类名)00 03(父类名)00 07(实现接口个数)00
00(成员属性数量)00 02(第一个成员属性)00 0900 0800 0900 00(第二个成员属性)00 0100 0a00
0b00 00(方法数量)00 04(方法内容)00 0100 0c00 0d00 0100 0e00
0000 3800 0200 0100 0000 0a2a b700 012a
04b5 0002 b100 0000 0200 0f00 0000 0a00
0200 0000 0e00 0400 1000 1000 0000 0c00
0100 0000 0a00 1100 1200 00(第一个方法结束)00 0100 1300
1400 0100 0e00 0000 3600 0100 0200 0000
022b b000 0000 0200 0f00 0000 0600 0100
0000 1300 1000 0000 1600 0200 0000 0200
1100 1200 0000 0000 0200 1500 0900 0100
0900 1600 1700 0100 0e00 0000 5400 0200
0300 0000 0ebb 0003 59b7 0004 4c2b b400
023d b100 0000 0200 0f00 0000 0e00 0300
0000 1700 0800 1800 0d00 1900 1000 0000
2000 0300 0000 0e00 1800 1900 0000 0800
0600 1a00 1200 0100 0d00 0100 1b00 0b00
0200 0800 1c00 0d00 0100 0e00 0000 1e00
0100 0000 0000 0612 05b3 0006 b100 0000
0100 0f00 0000 0600 0100 0000 0f00 0100
1d00 0000 0200 1e
1.3 手动解释class文件字节码
class文件的字节码由上1.1表所示部分组成,如class文件结构表中,常量池大小和接口数量各占两个子节,所以最多只能有65535个常量和最多能实现65535个接口。
1.3.1 魔数
魔数占4个子节且是固定的,在上面字节码文件中找到4个字节:CAFEBABY,它的唯一作用是确定这个文件是否能被虚拟机接受。
1.3.1.1 大端小端
表示从内存中读取数据的方式不同:
大端:高地址存数据的低位,低地址存数据的高位
小端:高地址存数据的高位,低地址存数据的低位
1.3.2 主次版本号
如下表中主次版本号代表的jdk版本
在class文件中找到16进制表示:
次版本号:0000,转为十进制:0;
主版本号:0034,转为十进制:52,
所以该class文件实在jdk1.8下编译生成的。
1.3.3 常量池大小
常量池大小占2子节即:
在class文件中找到为:0025 转为十进制:37
由jclasslib可以看出实际常量池是从所以为1开始的,所以实际只有36个常量。
1.3.4 常量池内容
常量池数据类型:
由表中可知常量池各类型第一个子节表示类型,
解析常量
第一个常量:
tag:0a -> 10 -> CONSTANT_Methodref_info
index:00 07 -> #7 符号引用
index:00 1f -> #31 符号引用
第二个常量:
tag:09 -> 9 -> CONSTANT_Fieldref_info
index:0003 -> #3 符号引用
index:0020 -> #32 符号引用
。。。。。
最后一个常量
tag:01 -> 1 -> CONSTANT_utf8_info
length:0010 -> 16
bytes:6a 6176 612f 6c61 6e67 2f4f 626a 6563 74 转成字符串 java/lang/Object
1.3.5 类的访问控制权限
00 21 -> ACC_PUBLIC | ACC_SUPER
1.3.6 类名
00 03 -> #3 符号引用指向常量池中索为3的常量 -> com/jt/learn/bytecode/ByTeCodeTest1
1.3.7 父类名
00 07 -> #7 符号引用指向常量池中索为7的常量 -> java/lang/Object
1.3.8 实现接口个数
00 00 未实现接口,占用两个字节所以最多能实现65535个接口
1.3.9 接口信息
未实现接口所以此部分不占用字节
1.3.10 成员属性数量
00 02 由2个成员属性,占用两个字节所以最多有65535属性
1.3.11 成员属性
字段访问权限和基本属性:
字段描述符解释表:
成员属性数据结构:
属性的属性数据结构
第一个成员属性:
access_flags(表示属性的访问控制和基本属性,查看上面的字段访问权限和属性标志表):00 09 -> 00 01 | 00 08 -> public static
name_index(名称再常量池的索引):00 08 -> #8 -> str
descriptor_index(字段描述符常量池索引):00 09 -> #9 -> Ljava/lang/String;
attributes_count(属性的属性数量):00 00 -> 0
第二个成属性:
access_flags: 00 01 -> public
name_index: 00 0a -> #10 ->a
descriptor_index: 00 0b ->#9 -> I
attributes_count: 00 00 -> 0
1.3.12 成员方法数量
00 04 -> 4个方法
该类中方法:jvm生成的静态代码块,无参构造方法,成员方法,main方法
1.3.13 成员方法内容
方法访问权限和属性标志
方法数据结构:
方法属性数据结构:
code属性数据结构
LineNumberTable数据结构:
LocalVariableTable数据结构:
解析方法
access_flags:00 01 -> public
name_index:00 0c -> #12 -> <init>
descriptor_index:00 0d -> #13 -> ()V
attributes_count:00 01 -> 1
attribute_info
attribute_name_index:00 0e -> #14 -> Code
attribute_length:00 00 00 38 -> 56
max_stack:00 02
max_locals:00 01
code_length:00 0000 0a -> 10
code:2a b7 00 01 2a 04 b5 00 02 b1
exception_table_length:00 00
attributes_count:00 02 -> 2
attribute_info:
attribute_name_index:00 0f -> #15 -> LineNumberTable
attribute_length:00 00 00 0a -> 10
line_number_table_length:00 02 ->2
start_pc:00 00
line_number:00 0e
start_pc:00 04
line_number:00 10
attribute_name_index:00 10 -> #16 -> LocalVariableTable
attribute_length:00 00 00 0C -> 12
local_variable_table_length:00 01
start_pc: 00 00
length: 00 0a
name_index: 00 11 -> #17 -> this
descriptor_index: 00 12->#18 -> Lcom/jt/learn/bytecode/ByTeCodeTest1;
index: 00 00
第一个方法到此结束。
该字节码文件解析到此,后面不一一解析,有兴趣的可自行解析。