Class类文件结构
无符号数:u1、u2、u4、u8分别代表1个字节、2个字节、4个字节、8个字节
magic:魔数,4个字节,唯一取值"0xCAFEBABE"
minjor_version、major_version:Class文件版本号
constant_pool_count:由于常量池数量不固定,在常量池入口放置一2字节类型数据,代表常量池容量计数。计数从1开始。即constant_pool_count = constant_pool长度+1 。
constant_pool:常量池。包括字面量和符号引用。字面量指java中的字符串和final类型常量等。符号引用包括类和接口的全限定名、字段的名称和描述符、方法的名称和描述符。
CONSTANT_Class_info结构里包含一个name_index,指向一个 CONSTANT_Utf8_info,为这个类的全限定名。CONSTANT_Utf8_info结构包含length,和bytes。
CONSTANT_Class_info {
u1 tag; //tag取值为7,代表CONSTANT_Class_info
u2 name_index; //name_index表示代表自己类名的字符串信息位于于常量池数组中哪一个,也就是索引
}
CONSTANT_Utf8_info {
u1 tag;
u2 length; //下面就是存储UTF8字符串的地方了
u1 bytes[length];
}
CONSTANT_NameAndType_info {
u1 tag;
u2 name_index; //方法名或域名对应的字符串索引
u2 descriptor_index; //方法信息(参数+返回值),或者成员变量的信息(类型)对应的字符串索引
}
CONSTANT_Fieldref_info {
u1 tag;
u2 class_index;
u2 name_and_type_index;
}
CONSTANT_Methodref_info {
u1 tag;
u2 class_index;
u2 name_and_type_index;
}
CONSTANT_InterfaceMethodref_info {
u1 tag;
u2 class_index;
u2 name_and_type_index;
}
access_flags:标志位。包括:这个 Class 是类还是接口,是否定义为 public 类型,abstract 类型,如果是类的话,是否声明为 final,等等。
this_class、super_class:类索引、父类索引。
interfaces、fields、methods:接口、变量、方法。
attributes:属性表。
示例:
public class TestMain{
public int x = 0;
public static void main (String [] args){
TestMain testMain = new TestMain();
testMain.test();
}
public TestMain(){
}
public void test(){
}
}
javap -verbose
public class TestMain
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Class #18 // TestMain
#2 = Methodref #1.#19 // TestMain."<init>":()V
#3 = Methodref #1.#20 // TestMain.test:()V
#4 = Methodref #6.#19 // java/lang/Object."<init>":()V
#5 = Fieldref #1.#21 // TestMain.x:I
#6 = Class #22 // java/lang/Object
#7 = Utf8 x
#8 = Utf8 I
#9 = Utf8 main
#10 = Utf8 ([Ljava/lang/String;)V
#11 = Utf8 Code
#12 = Utf8 LineNumberTable
#13 = Utf8 <init>
#14 = Utf8 ()V
#15 = Utf8 test
#16 = Utf8 SourceFile
#17 = Utf8 TestMain.java
#18 = Utf8 TestMain
#19 = NameAndType #13:#14 // "<init>":()V
#20 = NameAndType #15:#14 // test:()V
#21 = NameAndType #7:#8 // x:I
#22 = Utf8 java/lang/Object
{
public int x;
descriptor: I
flags: ACC_PUBLIC
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=1
0: new #1 // class TestMain
3: dup
4: invokespecial #2 // Method "<init>":()V
7: astore_1
8: aload_1
9: invokevirtual #3 // Method test:()V
12: return
LineNumberTable:
line 5: 0
line 6: 8
line 7: 12
public TestMain();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: aload_0
1: invokespecial #4 // Method java/lang/Object."<init>":()V
4: aload_0
5: iconst_0
6: putfield #5 // Field x:I
9: return
LineNumberTable:
line 9: 0
line 2: 4
line 11: 9
public void test();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=0, locals=1, args_size=1
0: return
LineNumberTable:
line 14: 0
}
SourceFile: "TestMain.java"
如上,在常量池中,第一个元素为Class,即CONSTANT_Class_info,它的name_index为第18个元素,即Utf8_info,内容为字符串"TestMain"。第二个元素为Methodref_info,其class_index为第一个元素,即"TestMain",name_and_type_index为第19个元素,对应第13个元素和第14个元素,V代表void。以此类推。
-
Code属性:
- stack=2,locals=2,args_size=1。结合代码,main函数确实有一个参数,而且还有一个本地变量。注意,main函数是static的。如果对于类的非static函数,那么locals的第0个元素代表this。stack表示操作数最大栈深度。locals表示局部变量所需要的空间,以slot为单位。args_size参数个数。
- stack后面接下来的就是code数组,也就是这个函数对应的执行代码。0表示code[]的索引位置。0:new:代表这个操作是new操作,此操作对应的字节码长度为3,所以下一个操作对应的字节码从索引3开始。
- LineNumberTable:也是属性的一种,用于调试,它将源码和字节码匹配了起来。比如line 5: 0这句话代表该函数字节码0那一个操作对应代码的第5行。