我只是一个喜欢开发的测试,喜欢测试的开发,喜欢每天都知道得更多一些,更深入一些的感觉。。。。。
分类: Java
2013-08-30 08:17:50
4. 访问标志(access_flags)
访问标志在紧接着常量表后面的两个字节,本例中常量表的最后一个字节偏移量是0x0000028d,因此0x0000 028e、0x0000 028f两个字节即访问标志。
0x0021是(0x0001 | 0x0020),即ACC_PUBLIC和ACC_SUPER标志为真。
5. 索引(this_class , super_class & interfaces)
访问标志后的两个字节是类索引,0x0001表示指向常量表的第一个表项,查之前我们整理的常量表各表项解析结果可知,该表项为一个CONSTANT_Class_info类型的表项,其全限定名为“com/momo/javatips/jmx/HelloWorld”。
接着的两个字节是父类索引,0x0003,常量表的第三个表项,全限定名“java/lang/Object”。
接口索引部分,因为Java运行一个类实现多个接口,因此可能存在多个接口索引,所以这部分是一个表格,头两个字节是表格长度,说明有几个接口,在此0x0001即HelloWorld实现了一个接口,这个接口的索引是0x0005,常量表的第五个表项,全限定名“com/momo/javatips/jmx/HelloWorldMBean”。
可以看出这三个索引是为了说明类的继承关系的,本类继承自哪个父类,又实现了哪些接口。
6. 字段表(fields)
字段表,显然是用来说明类中的成员字段的,包括静态的和非静态的。
上图是本例中的字段表部分,首先是字段表长度0x0001,只有一个字段,接着是各字段的具体信息:
偏移量 |
信息项 |
信息值 |
分析结果 |
0000 029a |
访问标志 |
0x0002 |
ACC_PRIVATE,为private字段 |
0000 029c |
字段名索引 |
0x0007 |
指向常量表第7个字符串表项,值为name |
0000 029e |
描述符索引 |
0x0008 |
指向常量表第8个字符串表项,值为Ljava/lang/String; |
0000 02a0 |
属性表长度 |
0x0000 |
无属性 |
而代码中确实只有一个String类型的private字段name。
7. 方法表(methods)
方法表起始两个字节为方法表长度,0x0005即有5个方法,让我们来逐个解析。
1)
偏移量 |
信息项 |
信息值 |
分析结果 |
||
0000 02a4 |
访问标志 |
0x0001 |
ACC_PUBLIC,为public方法 |
||
0000 02a6 |
方法名索引 |
0x0009 |
指向常量表第9个字符串表项,值为 |
||
0000 02a8 |
描述符索引 |
0x000A |
指向常量表第10个字符串表项,值为()V |
||
0000 02aa |
属性表长度 |
0x0001 |
一个属性 |
||
0000 02ac |
Code |
属性名索引 |
0x000B |
指向常量表第11个字符串表项,值为Code |
|
0000 02ae |
属性长度 |
0x0000002F |
47字节 |
||
0000 02b2 |
max_stack |
0x0001 |
操作数栈在运行执行的任何时间点的最大深度为1 |
||
0000 02b4 |
max_locals |
0x0001 |
1个局部变量 |
||
0000 02b6 |
code_length |
0x00000005 |
方法代码编译后的字节码为5个字节 |
||
0000 02ba |
code[] |
0x2AB7000CB1 |
|||
0000 02bf |
异常处理表长度 |
0x0000 |
无异常处理 |
||
0000 02c1 |
属性表长 |
0x0002 |
2个属性 |
||
0000 02c3 |
属性1 |
属性名索引 |
0x000E |
指向常量表第14个字符串表项,值为LineNumberTable |
|
0000 02c5 |
属性长度 |
0x00000006 |
6字节 |
||
0000 02c9 |
属性值 |
0x00 01……00 04 |
|||
0000 02cf |
属性2 |
属性名索引 |
0x000F |
指向常量表第15个字符串表项,值为LocalVariableTable |
|
0000 02d1 |
属性长度 |
0x0000000C |
12字节 |
||
0000 02d5 |
属性值 |
0x00 01……00 00 |
2) public java.lang.String getName();
偏移量 |
信息项 |
信息值 |
分析结果 |
||
0000 02e1 |
访问标志 |
0x0001 |
ACC_PUBLIC,为public方法 |
||
0000 02e3 |
方法名索引 |
0x0012 |
指向常量表第18个字符串表项,值为getName |
||
0000 02e5 |
描述符索引 |
0x0013 |
指向常量表第19个字符串表项,值为()Ljava/lang/String; |
||
0000 02e7 |
属性表长度 |
0x0001 |
一个属性 |
||
0000 02e9 |
Code |
属性名索引 |
0x000B |
指向常量表第11个字符串表项,值为Code |
|
0000 02eb |
属性长度 |
0x0000002F |
47字节 |
||
0000 02ef |
max_stack |
0x0001 |
操作数栈在运行执行的任何时间点的最大深度为1 |
||
0000 02f1 |
max_locals |
0x0001 |
1个局部变量 |
||
0000 02f3 |
code_length |
0x00000005 |
方法代码编译后的字节码为5个字节 |
||
0000 02f7 |
code[] |
0x2AB40014B0 |
|||
0000 02fc |
异常处理表长度 |
0x0000 |
无异常处理 |
||
0000 02fe |
属性表长 |
0x0002 |
2个属性 |
||
0000 0300 |
属性1 |
属性名索引 |
0x000E |
指向常量表第14个字符串表项,值为LineNumberTable |
|
0000 0302 |
属性长度 |
0x00000006 |
6字节 |
||
0000 0306 |
属性值 |
0x00 01……00 08 |
|||
0000 030c |
属性2 |
属性名索引 |
0x000F |
指向常量表第15个字符串表项,值为LocalVariableTable |
|
0000 030e |
属性长度 |
0x0000000C |
12字节 |
||
0000 0312 |
属性值 |
0x00 01……00 00 |
3) public void setName(java.lang.String);
偏移量 |
信息项 |
信息值 |
分析结果 |
||
0000 031e |
访问标志 |
0x0001 |
ACC_PUBLIC,为public方法 |
||
0000 0320 |
方法名索引 |
0x0016 |
指向常量表第22个字符串表项,值为setName |
||
0000 0322 |
描述符索引 |
0x0017 |
指向常量表第23个字符串表项,值为(Ljava/lang/String;)V |
||
0000 0324 |
属性表长度 |
0x0001 |
一个属性 |
||
0000 0326 |
Code |
属性名索引 |
0x000B |
指向常量表第11个字符串表项,值为Code |
|
0000 0328 |
属性长度 |
0x0000003E |
62字节 |
||
0000 032c |
max_stack |
0x0002 |
操作数栈在运行执行的任何时间点的最大深度为2 |
||
0000 032e |
max_locals |
0x0002 |
2个局部变量t |
||
0000 0330 |
code_length |
0x00000006 |
方法代码编译后的字节码为6个字节 |
||
0000 0334 |
code[] |
0x2A2BB50014B1 |
|||
0000 033a |
异常处理表长度 |
0x0000 |
无异常处理 |
||
0000 033c |
属性表长 |
0x0002 |
2个属性 |
||
0000 033e |
属性1 |
属性名索引 |
0x000E |
指向常量表第14个字符串表项,值为LineNumberTable |
|
0000 0340 |
属性长度 |
0x0000000A |
10字节 |
||
0000 0344 |
属性值 |
0x00 02……00 0D |
|||
0000 034e |
属性2 |
属性名索引 |
0x000F |
指向常量表第15个字符串表项,值为LocalVariableTable |
|
0000 0350 |
属性长度 |
0x00000016 |
22字节 |
||
0000 0354 |
属性值 |
0x00 02……00 01 |
4) public void printHello();
偏移量 |
信息项 |
信息值 |
分析结果 |
||
0000 036a |
访问标志 |
0x0001 |
ACC_PUBLIC,为public方法 |
||
0000 036c |
方法名索引 |
0x0018 |
指向常量表第24个字符串表项,值为printHello |
||
0000 036e |
描述符索引 |
0x000A |
指向常量表第10个字符串表项,值为(Ljava/lang/String;)V |
||
0000 0370 |
属性表长度 |
0x0001 |
一个属性 |
||
0000 0372 |
Code |
属性名索引 |
0x000B |
指向常量表第11个字符串表项,值为Code |
|
0000 0374 |
属性长度 |
0x00000048 |
72字节 |
||
0000 0378 |
max_stack |
0x0004 |
操作数栈在运行执行的任何时间点的最大深度为4 |
||
0000 037a |
max_locals |
0x0001 |
1个局部变量 |
||
0000 037c |
code_length |
0x0000001A |
方法代码编译后的字节码为26个字节 |
||
0000 0380 |
code[] |
0xB2 00……2C B1 |
|||
0000 039a |
异常处理表长度 |
0x0000 |
无异常处理 |
||
0000 039c |
属性表长 |
0x0002 |
2个属性 |
||
0000 039e |
属性1 |
属性名索引 |
0x000E |
指向常量表第14个字符串表项,值为LineNumberTable |
|
0000 03a0 |
属性长度 |
0x0000000A |
10字节 |
||
0000 03a4 |
属性值 |
0x00 02……00 11 |
|||
0000 03ae |
属性2 |
属性名索引 |
0x000F |
指向常量表第15个字符串表项,值为LocalVariableTable |
|
0000 03b0 |
属性长度 |
0x0000000C |
12字节 |
||
0000 03b4 |
属性值 |
0x00 01……00 00 |
5) public void printHello(java.lang.String);
偏移量 |
信息项 |
信息值 |
分析结果 |
||
0000 03c0 |
访问标志 |
0x0001 |
ACC_PUBLIC,为public方法 |
||
0000 03c2 |
方法名索引 |
0x0018 |
指向常量表第24个字符串表项,值为printHello |
||
0000 03c4 |
描述符索引 |
0x0017 |
指向常量表第23个字符串表项,值为(Ljava/lang/String;)V |
||
0000 03c6 |
属性表长度 |
0x0001 |
一个属性 |
||
0000 03c8 |
Code |
属性名索引 |
0x000B |
指向常量表第11个字符串表项,值为Code |
|
0000 03ca |
属性长度 |
0x0000004F |
79字节 |
||
0000 03ce |
max_stack |
0x0004 |
操作数栈在运行执行的任何时间点的最大深度为4 |
||
0000 03d0 |
max_locals |
0x0002 |
2个局部变量 |
||
0000 03d2 |
code_length |
0x00000017 |
方法代码编译后的字节码为23个字节 |
||
0000 03d6 |
code[] |
0xB2 00……B1 00 |
|||
0000 03ed |
异常处理表长度 |
0x0000 |
无异常处理 |
||
0000 03ef |
属性表长 |
0x0002 |
2个属性 |
||
0000 03f1 |
属性1 |
属性名索引 |
0x000E |
指向常量表第14个字符串表项,值为LineNumberTable |
|
0000 03f3 |
属性长度 |
0x0000000A |
10字节 |
||
0000 03f7 |
属性值 |
0x00 02……00 15 |
|||
0000 0401 |
属性2 |
属性名索引 |
0x000F |
指向常量表第15个字符串表项,值为LocalVariableTable |
|
0000 0403 |
属性长度 |
0x00000016 |
22字节 |
||
0000 0407 |
属性值 |
0x00 02……00 01 |
8. 属性表(attributes)
属性表开始2个字节的值0x0001说明只有一个属性,接着是这个属性的名称在常量表中的索引0x0034,指向第52个表项,值为“SourceFile”;0x00000002属性长度为两个字节;0x0035,指向第53个表项,值为“HelloWorld.java”,即这个属性说明了这个class的源文件是HelloWorld.java。
小结
本文中,我们只是粗浅地将一个简单的class文件的各个部分过了一遍,并没有进一步去挖掘更多更深一些的内涵,把各个部分串起来,用JDK自身提供的工具Javap –verbose选项可以看到更多的信息,但我希望能自己摸索一下以便对class文件结构有更多的理解,让我们在后续慢慢深入吧,这一篇就先到这儿。
参考文献
1. 《深入理解Java虚拟机:JVM高级特性与最佳实践》 周志明 著,机械工业出版社,2011
2. 《Java虚拟机规范(Java SE 7版)》 Tim Lindholm,Frank Yellin,Gilad Bracha,Alex Buckley 著,周志明,吴璞渊,冶秀刚
译,2011
3. 《深入java虚拟机(第二版)》 [美]Bill Venners 著, 曹晓钢,蒋靖 译,机械工业出版社,2003
4.