## Hello World
一个最简单的Hello World程序源代码如下:
```java
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](http://www.x-ways.net/winhex/),内容如下:

## 类文件结构
根据 JVM 规范 ,一个类文件结构如下:
```java
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类型 |
| L*ClassName* | reference | 类*ClassName*的引用类型 |
| S | short | short类型 |
| Z | boolean | boolean类型 |
| [ | reference | 一维数组类型 |
### 方法信息
紧接成员变量后的两个字节为`00 02 `表示方法的数量,为2。
一个方法由 `访问修饰符`,`名称`,`参数描述`,`方法属性数量`,`方法属性`组成。可根据上述同样的方法进行分析,可以得出`<init>`方法和`main`方法的方法信息。
### 附加属性
紧接方法信息后的是附加属性。这里主要记录的是源文件信息,如源文件的名称。
[官方文档](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html)

通过HelloWorld读懂Java字节码(.class)文件结构