Java最显著的特点就是"Write Once, Run Anywhere", 这全是因为虚拟机JVM的存在,使得Java代码的运行可以不受操作系统的限制。不论是Java语言的代码还是其他语言的代码,最终都可以编译成字节码.Class文件,虚拟机并不关心文件来自什么语言,只要符合Class文件的格式,可以在虚拟机中运行就行。
Class文件中只要两种数据机构:无符号数和表;无符号数u1,u2,u4,u8分别代表1个字节,2个字节,4个字节和8个字节;表是由多个无符号数或其他表构成的复合数据类型,和C语言中的结构体类似。整个Class文件就是由以下的数据项构成的:
| 类型 | 名称 | 数量 | 
| u4 | magic | 1 | 
| u2 | minor_version | 1 | 
| u2 | major_version | 1 | 
| u2 | constant_pool_count | 1 | 
| cp_info | constant_pool | constant_pool_count - 1 | 
| u2 | access_flags | 1 | 
| u2 | this_class | 1 | 
| u2 | super_class | 1 | 
| u2 | interfaces_count | 1 | 
| u2 | interfaces | interfaces_count | 
| u2 | fields_count | 1 | 
| field_info | fields | fields_count | 
| u2 | methods_count | 1 | 
| method_info | methods | methods_count | 
| u2 | attribute_count | 1 | 
| attribute_info | attributes | attributes_count | 
下面是十六进制编辑器wxmedit打开的一个Class文件

前四个字节CA FE BA BE 称为魔数(Magic Number)对应u4类型的magic,魔数是用来判定当前的文件是否为Class文件
第五和第六个字节 00 00 代表次版本号(Minor Version),第七和第八个字节00 34代表主版本号(Major Version)52,对应JDK52.0
主版本号之后就是常量池(Constant Pool)的入口,是占用Class文件最大的项目,常量池的大小是不固定的,所以在常量池入口处首先看到的是常量池的计数值(constant_pool_count)00 36 ,但常量池的容量不是从0而是从1开始计数的,所以对应常量池有54-1=53个常量项
常量池中主要存放两大类常量:字面量和符号引用;字面量好比Java中的文本字符串和被声明的final常量,符号引用主要包括了三类常量:类和接口的全限定名,字段的名称和描述符,方法的名称和描述符
之所以存在常量池是因为Java代码在编译的时候没有"连接"
  C 语言: 编译->链接->.exe->执行
    函数A调用函数B,在链接时会直接在函数A中记录函数B的地址。
  Java : 编译->.class ->装载执行
    类 Employee 中使用了另外一个类 Department,在Employee.class 中只保存类Department的名称, 而不会保留类Department的“地址”。虚拟机运行的时候需要从常量池获得对应的符号引用,在类创建的时候翻译并解析到具体的内存之中。
下面是常量池中包含的项目类型
| 常量池中数据项类型 | 类型标志 | 类型描述 | 
| CONSTANT_Utf8 | 1 | UTF-8编码的Unicode字符串 | 
| CONSTANT_Integer | 3 | int类型字面值 | 
| CONSTANT_Float | 4 | float类型字面值 | 
| CONSTANT_Long | 5 | long类型字面值 | 
| CONSTANT_Double | 6 | double类型字面值 | 
| CONSTANT_Class | 7 | 对一个类或接口的符号引用 | 
| CONSTANT_String | 8 | String类型字面值 | 
| CONSTANT_Fieldref | 9 | 对一个字段的符号引用 | 
| CONSTANT_Methodref | 10 | 对一个类中声明的方法的符号引用 | 
| CONSTANT_InterfaceMethodref | 11 | 对一个接口中声明的方法的符号引用 | 
| CONSTANT_NameAndType | 12 | 对一个字段或方法的部分符号引用 | 
常量池的第一项常量,标志位tag = 07,对应 CONSTANT_Class 类型
| 类型 | 名称 | 数量 | 
| u1 | tag | 1 | 
| u2 | name_index | 1 | 
标志位之后是name_index = 00 02,指向常量池中的第二项, tag = 01,表示CONSTANT_Utf8
| 类型 | 名称 | 数量 | 
| u1 | tag | 1 | 
| u2 | length | 1 | 
| u1 | bytes | length | 
第二项常量中 00 33 十进制表示51,即length=51,即此UTF8编码的字符串长度是51个字节

第三项常量tag = 07, 可知是CONSTANT_Class 类型 ,name_index = 00 04, 指向第四项常量
第四项常量tag = 01, 可知是CONSTANT_Utf8类型,length = 16 (00 10)转换而来
其他项的解析和之前的类似,对照具体的常量的结构来分析
| 
 常量  | 
 项目  | 
 类型  | 
 描述  | 
| 
 
 CONSTANT_Utf8_info  | 
 tag  | 
 u1  | 
 值为1  | 
| 
 length  | 
 u2  | 
 UF-8编码的字符串占用的字节数  | 
|
| 
 bytes  | 
 u1  | 
 长度为length的UTF-8编码的字符串  | 
|
| 
 
 CONSTANT_Integer_info  | 
 tag  | 
 u1  | 
 值为3  | 
| 
 bytes  | 
 u4  | 
 按照高位在前存储的int值  | 
|
| 
 
 CONSTANT_Float_info  | 
 tag  | 
 u1  | 
 值为4  | 
| 
 bytes  | 
 u4  | 
 按照高位在前存储的float值  | 
|
| 
 
 CONSTANT_Long_info  | 
 tag  | 
 u1  | 
 值为5  | 
| 
 bytes  | 
 u8  | 
 按照高位在前存储的long值  | 
|
| 
 
 CONSTANT_Double_info  | 
 tag  | 
 u1  | 
 值为6  | 
| 
 bytes  | 
 u8  | 
 按照高位在前存储的double值  | 
|
| 
 
 CONSTANT_Class_info  | 
 tag  | 
 u1  | 
 值为7  | 
| 
 index  | 
 u2  | 
 指向全限定名常量项的索引  | 
|
| 
 
 CONSTANT_String_info  | 
 tag  | 
 u1  | 
 值为8  | 
| 
 index  | 
 u2  | 
 指向字符串字面量的索引  | 
|
| 
 
 CONSTANT_Fieldref_info  | 
 tag  | 
 u1  | 
 值为9  | 
| 
 index  | 
 u2  | 
 指向声明字段的类或接口描述符CONSTANT_Class_info的索引项  | 
|
| 
 index  | 
 u2  | 
 指向字段名称及类型描述符CONSTANT_NameAndType_info的索引项  | 
|
| 
 
 CONSTANT_Methodref_info  | 
 tag  | 
 u1  | 
 值为10  | 
| 
 index  | 
 u2  | 
 指向声明方法的类描述符CONSTANT_Class_info的索引项  | 
|
| 
 index  | 
 u2  | 
 指向方法名称及类型描述符CONSTANT_NameAndType_info的索引项  | 
|
| 
 
 CONSTANT_InrerfaceMethodref_info  | 
 tag  | 
 u1  | 
 值为11  | 
| 
 index  | 
 u2  | 
 指向声明方法的接口描述符CONSTANT_Class_info的索引项  | 
|
| 
 index  | 
 u2  | 
 指向方法名称及类型描述符CONSTANT_NameAndType_info的索引项  | 
|
| 
 
 CONSTANT_NameAndType_info  | 
 tag  | 
 u1  | 
 值为12  | 
| 
 index  | 
 u2  | 
 指向字段或方法名称常量项目的索引  | 
|
| 
 index  | 
 u2  | 
 指向该字段或方法描述符常量项的索引  | 
原文:http://www.cnblogs.com/HarryHook/p/6682439.html