我只是一个喜欢开发的测试,喜欢测试的开发,喜欢每天都知道得更多一些,更深入一些的感觉。。。。。
分类: Java
2013-08-30 07:43:10
随便拿了一个之前写自定义MBean例子时HelloWorldMBean的实现类HelloWorld编译后的class文件HelloWorld.class。先把HelloWorld.java代码罗列一下,以便我们之后对照着看。
嗯,非常简单,网上到处是这样的例子代码。好了,让我们开始吧!
将HelloWorld.class拉到UltraEdit里面,是下面这样的:
对于class文件结构,首先要知道以下几点:
1) Class文件中所有数据项紧密排列,各数据项之间不存在任何分隔符或填充对齐符;
2) 各个数据项长度,要么是固定的,要么就是在其之前有个固定长度的项指定该项的长度;
3) Class文件中只有两种数据类型:无符号数和表;
4) Class文件可以按数据项内容分为8个部分。
先知道这么多吧,下面我们从头开始,一个数据项一个数据项来分析。
1. 魔数(Magic)
class文件的头4个字节0xCAFEBABE,魔数,身份的标志,我是class文件,与生俱来,血统纯正!
2. 版本信息(minor_version & major_version)
5、6两个字节是次版本,此为0x0000,即0
7、8两个字节是主版本,此为0x0032,即50,JDK1.1为45,之后每次升级加1,因此(1.1+(50-45)*0.1)=1.6,而我所使用的正是jdk1.6.0_31。
3. 常量池(constant_pool)
9、10两个字节是常量表长度,0x0036为54,即53个常量。
class文件中的常量除了字面量,如代码中的“Hello World, ”,以及声明为final的常量外,还包括符号引用,诸如类/接口的全限定名、字段/方法的名称及描述符,之所以包括这些,是因为需要在JVM运行的时候,根据这些信息动态连接到内存中的类。
常量表开始,读取第一个表项,class文件中一共有11种类型的常量表表项,每种表项的第一个字节都是一个标志字段tag,用来标识是什么类型的表项,然后根据不同的类型来解析接下来的字节,因为每种表项的结构是不尽相同的。
第11字节为0x07,表示该表项是个类/接口符号引用,该类型的结构为第2、3两个字节为索引值,此为0x0002,即指向常量池中第二个表项。
我们来看第二个表项,首先tag为0x01,UTF-8字符串类型,因此其后两个字节0x0020是字符串长度,内容即紧接下来的32字节“com/momo/javatips/jmx/
HelloWorld”。
对照源代码,除了包名和关键字之外,首先出现的HelloWorld类,其全限定名正是“com.momo.javatips.jmx.HelloWorld”。
通过以上对第一、二两个表项的详细说明,我们已经对怎么解析常量表表项有所了解,接下来对其余的表项就不再一一这样详细地分析了,我只将每个表项的原始值及分析结果在下表中列出:
表项编号 |
偏移量 |
原始值 |
分析结果 |
#1 |
0000 000a |
0x07 |
类符号引用 |
0000 000b |
0x0002 |
指向第2个表项——其全限定名com/momo/javatips/jmx/HelloWorld |
|
#2 |
0000 000d |
0x01 |
UTF-8字符串 |
0000 000e |
0x0020 |
长度32字节 |
|
0000 0010 |
0x63 6F……6C 64 |
com/momo/javatips/jmx/HelloWorld |
|
#3 |
0000 0030 |
0x07 |
类符号引用 |
0000 0031 |
0x0004 |
指向第4个表项——其全限定名java/lang/Object |
|
#4 |
0000 0033 |
0x01 |
UTF-8字符串 |
0000 0034 |
0x0010 |
长度16字节 |
|
0000 0036 |
0x6A 61……63 74 |
java/lang/Object |
|
#5 |
0000 0046 |
0x07 |
接口符号引用 |
0000 0047 |
0x0006 |
指向第6个表项——其全限定名com/momo/javatips/jmx/HelloWorldMBean |
|
#6 |
0000 0049 |
0x01 |
UTF-8字符串 |
0000 004a |
0x0025 |
长度37字节 |
|
0000 004c |
0x63 6F……61 6E |
com/momo/javatips/jmx/HelloWorldMBean |
|
#7 |
0000 0071 |
0x01 |
UTF-8字符串 |
0000 0072 |
0x0004 |
长度4字节 |
|
0000 0074 |
0x6E 61 6D 65 |
name |
|
#8 |
0000 0078 |
0x01 |
UTF-8字符串 |
0000 0079 |
0x0012 |
长度18字节 |
|
0000 007b |
0x4C 6A…… 67 3B |
Ljava/lang/String; |
|
#9 |
0000 008d |
0x01 |
UTF-8字符串 |
0000 008e |
0x0006 |
长度6字节 |
|
0000 0090 |
0x3C 69…… 74 3E |
|
|
#10 |
0000 0096 |
0x01 |
UTF-8字符串 |
0000 0097 |
0x0003 |
长度3字节 |
|
0000 0099 |
0x28 29 56 |
()V |
|
#11 |
0000 009c |
0x01 |
UTF-8字符串 |
0000 009d |
0x0004 |
长度4字节 |
|
0000 009f |
0x43 6F 64 65 |
Code |
|
#12 |
0000 00a3 |
0x0A |
方法符号引用 |
0000 00a4 |
0x0003 |
指向第3个表项——其所属类Object |
|
0000 00a6 |
0x000D |
指向第13个表项——其符号引用 |
|
#13 |
0000 00a8 |
0x0C |
方法名称及描述符符号引用 |
0000 00a9 |
0x0009 |
指向第9个表项——其名称常量 |
|
0000 00ab |
0x000A |
指向第10个表项——其描述符常量()V |
|
#14 |
0000 00ad |
0x01 |
UTF-8字符串 |
0000 00ae |
0x000F |
长度15字节 |
|
0000 00b0 |
0x4C 69…… 6C 65 |
LineNumberTable |
|
#15 |
0000 00bf |
0x01 |
UTF-8字符串 |
0000 00c0 |
0x0012 |
长度18字节 |
|
0000 00c2 |
0x4C 6F…… 6C 65 |
LocalVariableTable |
|
#16 |
0000 00d4 |
0x01 |
UTF-8字符串 |
0000 00d5 |
0x0004 |
长度4字节 |
|
0000 00d7 |
0x74 68 69 73 |
this |
|
#17 |
0000 00db |
0x01 |
UTF-8字符串 |
0000 00dc |
0x0022 |
长度34字节 |
|
0000 00de |
0x4C 63……64 3B |
Lcom/momo/javatips/jmx/HelloWorld; |
|
#18 |
0000 0100 |
0x01 |
UTF-8字符串 |
0000 0101 |
0x0007 |
长度7字节 |
|
0000 0103 |
0x67 65……6D 65 |
getName |
|
#19 |
0000 010a |
0x01 |
UTF-8字符串 |
0000 010b |
0x0014 |
长度20字节 |
|
0000 010d |
0x28 29……67 3B |
()Ljava/lang/String; |
|
#20 |
0000 0121 |
0x09 |
字段符号引用 |
0000 0122 |
0x0001 |
指向第1个表项——所属类HelloWorld |
|
0000 0124 |
0x0015 |
指向第21个表项——其描述符 |
|
#21 |
0000 0126 |
0x0C |
方法名称及描述符符号引用 |
0000 0127 |
0x0007 |
指向第7个表项——其名称常量name |
|
0000 0129 |
0x0008 |
指向第8个表项——其描述符常量 Ljava/lang/String; |
|
#22 |
0000 012b |
0x01 |
UTF-8字符串 |
0000 012c |
0x0007 |
长度7字节 |
|
0000 012e |
0x73 65……6D 65 |
setName |
|
#23 |
0000 0135 |
0x01 |
UTF-8字符串 |
0000 0136 |
0x0015 |
长度21字节 |
|
0000 0138 |
0x28 4C……29 56 |
(Ljava/lang/String;)V |
|
#24 |
0000 014d |
0x01 |
UTF-8字符串 |
0000 014e |
0x000A |
长度10字节 |
|
0000 0150 |
0x70 72……6C 6F |
printHello |
|
#25 |
0000 015a |
0x09 |
字段符号引用 |
0000 015b |
0x001A |
指向第26个表项——所属类System |
|
0000 015d |
0x001C |
指向第28个表项——其描述符 |
|
#26 |
0000 015f |
0x07 |
类符号引用 |
0000 0160 |
0x001B |
指向第27个表项——其全限定名java/lang/System |
|
#27 |
0000 0162 |
0x01 |
UTF-8字符串 |
0000 0163 |
0x0010 |
长度16字节 |
|
0000 0165 |
0x6A 61……65 6D |
java/lang/System |
|
#28 |
0000 0175 |
0x0C |
字段名称及描述符符号引用 |
0000 0176 |
0x001D |
指向第29个表项——其名称常量out |
|
0000 0178 |
0x001E |
指向第30个表项——其描述符常量Ljava/io/PrintStream; |
|
#29 |
0000 017a |
0x01 |
UTF-8字符串 |
0000 017b |
0x0003 |
长度3字节 |
|
0000 017d |
0x6F 75 74 |
out |
|
#30 |
0000 0180 |
0x01 |
UTF-8字符串 |
0000 0181 |
0x0015 |
长度21字节 |
|
0000 0183 |
0x4C 6A……6D 3B |
Ljava/io/PrintStream; |
|
#31 |
0000 0198 |
0x07 |
类符号引用 |
0000 0199 |
0x0020 |
指向第32个表项——其全限定名java/lang/StringBuilder |
|
#32 |
0000 019b |
0x01 |
UTF-8字符串 |
0000 019c |
0x0017 |
长度23字节 |
|
0000 019e |
0x6A 61……65 72 |
java/lang/StringBuilder |
|
#33 |
0000 01b5 |
0x08 |
字符串类型字面量 |
0000 01b6 |
0x0022 |
指向第34个表项——其值Hello World, |
|
#34 |
0000 01b8 |
0x01 |
UTF-8字符串 |
0000 01b9 |
0x000D |
长度13字节 |
|
0000 01bb |
0x48 65……2C 20 |
Hello World, |
|
#35 |
0000 01c8 |
0x0A |
方法符号引用 |
0000 019 |
0x001F |
指向第31个表项——其所属类StringBuilder |
|
0000 01b |
0x0024 |
指向第36个表项——其符号引用 |
|
#36 |
0000 01cd |
0x0C |
方法名称及描述符符号引用 |
0000 01ce |
0x0009 |
指向第9个表项——其名称常量 |
|
0000 01d0 |
0x0017 |
指向第23个表项——其描述符常量 (Ljava/lang/String;)V |
|
#37 |
0000 01d2 |
0x0A |
方法符号引用 |
0000 01d3 |
0x001F |
指向第31个表项——其所属类StringBuilder |
|
0000 01d5 |
0x0026 |
指向第38个表项——其符号引用 |
|
#38 |
0000 01d7 |
0x0C |
方法名称及描述符符号引用 |
0000 01d8 |
0x0027 |
指向第39个表项——其名称常量append |
|
0000 01da |
0x0028 |
指向第40个表项——其描述符常量(Ljava/lang/String;)Ljava/lang/StringBuilder; |
|
#39 |
0000 01dc |
0x01 |
UTF-8字符串 |
0000 01dd |
0x0006 |
长度6字节 |
|
0000 01df |
0x61 70……6E 64 |
append |
|
#40 |
0000 01e5 |
0x01 |
UTF-8字符串 |
0000 01e6 |
0x002D |
长度45字节 |
|
0000 01e8 |
0x28 4C……72 3B |
(Ljava/lang/String;)Ljava/lang/StringBuilder; |
|
#41 |
0000 0215 |
0x0A |
方法符号引用 |
0000 0216 |
0x001F |
指向第31个表项——其所属类StringBuilder |
|
0000 0218 |
0x002A |
指向第42个表项——其符号引用 |
|
#42 |
0000 021a |
0x0C |
方法名称及描述符符号引用 |
0000 021b |
0x002B |
指向第43个表项——其名称常量toString |
|
0000 021d |
0x0013 |
指向第19个表项——其描述符常量()Ljava/lang/String; |
|
#43 |
0000 021f |
0x01 |
UTF-8字符串 |
0000 0220 |
0x0008 |
长度8字节 |
|
0000 0222 |
0x74 6F……6E 67 |
toString |
|
#44 |
0000 022a |
0x0A |
方法符号引用 |
0000 022b |
0x002D |
指向第45个表项——其所属类PrintStream |
|
0000 022d |
0x002F |
指向第47个表项——其符号引用 |
|
#45 |
0000 022f |
0x07 |
类符号引用 |
0000 0230 |
0x002E |
指向第46个表项——其全限定名java/io/PrintStream |
|
#46 |
0000 0232 |
0x01 |
UTF-8字符串 |
0000 0233 |
0x0013 |
长度19字节 |
|
0000 0235 |
0x6A 61……61 6D |
java/io/PrintStream |
|
#47 |
0000 0248 |
0x0C |
方法名称及描述符符号引用 |
0000 0249 |
0x0030 |
指向第48个表项——其名称常量println |
|
0000 024b |
0x0017 |
指向第23个表项——其描述符常量 (Ljava/lang/String;)V |
|
#48 |
0000 024d |
0x01 |
UTF-8字符串 |
0000 024e |
0x0007 |
长度7字节 |
|
0000 0250 |
0x70 72……6C 6E |
println |
|
#49 |
0000 0257 |
0x08 |
字符串类型字面量 |
0000 0258 |
0x0032 |
指向第50个表项——其值Hello , |
|
#50 |
0000 025a |
0x01 |
UTF-8字符串 |
0000 025b |
0x0008 |
长度8字节 |
|
0000 025d |
0x48 65……2C 20 |
Hello , |
|
#51 |
0000 0265 |
0x01 |
UTF-8字符串 |
0000 0266 |
0x0007 |
长度7字节 |
|
0000 0268 |
0x77 68……6D 65 |
whoName |
|
#52 |
0000 026f |
0x01 |
UTF-8字符串 |
0000 0270 |
0x000A |
长度10字节 |
|
0000 0272 |
0x53 6F……6C 65 |
SourceFile |
|
#53 |
0000 027c |
0x01 |
UTF-8字符串 |
0000 027d |
0x000F |
长度15字节 |
|
0000 027f |
0x48 65……76 61 |
HelloWorld.java |
在我们这个HelloWorld.class文件的常量表中只涉及到了UTF-8字符串(0x01)、类或接口(0x07)、字符串类型字面量(0x08)、字段符号引用(0x09)、方法符号引用(0x0A)、方法或字段名称及描述符符号引用(0x0C)等6种常量类型,其余的5种等以后涉及了再讨论。