Hello World
一个最简单的Hello World程序源代码如下:
package top.zysite;
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World");
}
}
通过javac -parameters -d . HelloWorld.java命令编译为HelloWorld.class 文件后,将其拖入WinHex,内容如下:

类文件结构
根据 JVM 规范 ,一个类文件结构如下:
ClassFile{
u4 magic;
u2 minor_version;
u2 major_version;
u2 constant_pool_count;
cp_info constant_pool[count];
u2 access_flags;
u2 this_class;
u2 super_class;
u2 interfaces_count;
u2 interfaces[interfaces_count];
u2 fields_count;
field_info fields[fields_count];
u2 methods_count;
method_info methods[methods_count];
u2 attributes_count;
attribute_info attributes[attributes_count];
}
注:上诉的 u4 u2 代表占用4个、2个字节。
魔数
字节码0~3个字节为魔数,也就是u4 magic部分,用于标识文件类型为class。
根据上述字节码内容,class文件魔数为CA FE BA BE。
版本
字节码4~7个字节为版本,也就是u2 minor_version u2 major_version部分,表示Java小版本号和主版本号。
根据上述字节码内容,版本为00 00 00 34。小版本为00 00,主版本为00 34(十进制为52)代表Java 8。
常量池
字节码8~9个字节为常量池长度,也就是u2 constant_pool_count部分。
根据上述字节码内容,常量池长度为00 22,十进制为34,表示常量池有1-33项(不包括第0项)。
常量池含义表:
| ConstantType | Value(十六进制) | Value(十进制) |
|---|---|---|
| CONSTANT_Class | 07 | 7 |
| CONSTANT_Fieldref | 09 | 9 |
| CONSTANT_Methodref | 0A | 10 |
| CONSTANT_InterfaceMethodref | 0B | 11 |
| CONSTANT_String | 08 | 8 |
| CONSTANT_Integer | 03 | 3 |
| CONSTANT_Float | 04 | 4 |
| CONSTANT_Long | 05 | 5 |
| CONSTANT_Double | 06 | 6 |
| CONSTANT_NameAndType | 0C | 12 |
| CONSTANT_Utf8 | 01 | 1 |
| CONSTANT_MethodHandle | 0F | 15 |
| CONSTANT_MethodType | 10 | 16 |
| CONSTANT_InvokeDynamic | 12 | 18 |
紧接着常量池长度后的内容为字符串常量池具体内容,也就是cp_info constant_pool[count]部分。
常量池的第1项,也就是00 22之后的一个字节为0A,根据上表,得知表示的是CONSTANT_Methodref类型的信息,用于描述方法,紧接其后的四个字节00 06 00 14,前两个字节00 06 (十进制为6)表示引用常量池中第6项来表示方法的所属类。后两个字节00 14(十进制为20)表示引用常量池中第20项来表示方法的方法名。
常量池的第2项,09表示的是CONSTANT_Fieldref类型的信息,用于描述成员变量,紧接其后的四个字节00 15 00 16,前两个字节00 15(十进制为21)表示引用常量池中第21项来表示所属类。后两个字节 00 16(十进制为22) 表示引用常量池中第22项来表示成员变量名。
以此类推进行分析。根据前面的信息,得知共33项。
使用WinHex还有一个好处,它会同时将十六进制对照ANSCII码进行翻译:

分析到图中选中区域时,查表得知01表示CONSTANT_Utf8类型,即utf8串,其后一个字节00 06表示串的长度(十进制为6),紧接着的6个字节即为串内容的ANSCII码值,可以看到内容为<init>。
访问标识和继承信息
紧接常量池后的是访问标识,也就是u2 access_flags部分。
访问标识含义表:
| Flag Name | Value(十六进制) | 含义 |
|---|---|---|
| ACC_PUBLIC | 0x0001 | 声明为public |
| ACC_FINAL | 0x0010 | 声明为final |
| ACC_SUPER | 0x0020 | 当被特殊指令调用时,特别对待超类方法 |
| ACC_INTERFACE | 0x0200 | 表示是一个接口而不是类 |
| ACC_ABSTRACT | 0x0400 | 声明为abstract |
| ACC_SYNTHETIC | 0x1000 | 表示人工合成,不是源代码 |
| ACC_ANNOTATION | 0x2000 | 表示为注解类型 |
| ACC_ENUM | 0x4000 | 表示为枚举类型 |
紧接常量池后的两个字节为00 21,表示由ACC_PUBLIC + ACC_SUPER相加得出,表示为一个public类。
后两个字节00 05表示以字符串常量池中第5项表示本类的全限定名。即为u2 this_class部分。
再之后两个字节00 06表示以字符串常量池中第6项表示父类的全限定名。即为u2 super_class部分。
再之后两个字节00 00表示接口的数量为0。即为u2 interfaces_count部分,由于为0,所以其后没有具体的接口相关信息。
成员变量
紧接接口信息后的两个字节为00 00表示成员变量的数量,为0。在class文件中,成员变量的类型由更简洁的字符表示,其与Java源代码中的类型对应表如下:
| Field Type(.class) | Field Type(.java) | 说明 |
|---|---|---|
| B | byte | byte类型 |
| C | char | char类型 |
| D | double | double类型 |
| F | float | float类型 |
| I | int | int类型 |
| J | long | long类型 |
| LClassName | reference | 类ClassName的引用类型 |
| S | short | short类型 |
| Z | boolean | boolean类型 |
| [ | reference | 一维数组类型 |
方法信息
紧接成员变量后的两个字节为00 02表示方法的数量,为2。
一个方法由 访问修饰符,名称,参数描述,方法属性数量,方法属性组成。可根据上述同样的方法进行分析,可以得出<init>方法和main方法的方法信息。
附加属性
紧接方法信息后的是附加属性。这里主要记录的是源文件信息,如源文件的名称。