Java 自誕生之初便高舉「一次編寫,到處運行」的平台無關性大旗,而要實現這一承諾,Class 文件的結構設計至關重要。Java 體系其實包含兩套規範:Java 語言規範約束語法規則,而 Java 虛擬機規範則從跨平台角度定義了 Class 文件格式。本文將先概述 Class 文件的整體組成,再通過一個實際案例逐字節解析其結構。
在深入之前,必須明確兩個核心規則:

注意:圖中 constant_pool[constant_pool_count-1] 以數組形式描述常量池,但各常量長度並不固定,此處僅為描述方便。
前 4 字節 0xCAFEBABE,唯一標識此文件為 Class 文件。
minor_version(u2):次版本號major_version(u2):主版本號。高版本 JVM 向下兼容低版本 Class,反之則不支持。constant_pool_count(u2)表示常量數量,實際常量個數為 count-1(索引 0 保留為「不引用任何常量」)。
存儲字面量(字符串、final 常量值、屬性初始值)與符號引用(類/接口全限定名、字段/方法名及描述符)。JVM 規範定義了 14 種常量類型,每種都是一個表,通過 tag 標識類型(如 CONSTANT_Utf8_info tag=1,CONSTANT_Methodref_info tag=10 等)。
access_flags(u2)標識類/接口屬性:ACC_PUBLIC、ACC_FINAL、ACC_SUPER(JDK 1.2+ 編譯類必帶)等。
this_class:指向常量池 CONSTANT_Class_info,給出當前類全限定名super_class:指向父類 CONSTANT_Class_infointerfaces_count + interfaces[]:實現的接口列表,每項指向常量池 CONSTANT_Class_infofields_count + field_info[]。field_info 包含:訪問標誌、名稱索引、描述符索引、屬性表計數與屬性表。
描述符規則:基本類型用單字母(B=byte, I=int, J=long 等),引用類型用 L全限定名;,數組每維加一個 [(如 [[I 表示 int[][])。
methods_count + method_info[]。結構與字段表類似,但方法描述符格式為 (參數描述符)返回描述符(如 (I)I 表示 int method(int))。方法屬性表中關鍵的是 Code 屬性,存放字節碼指令。

位於文件末尾,如 SourceFile 標記源文件名。屬性表結構靈活,可嵌套於類、字段、方法、Code 屬性中。
package com.ejushang.TestClass;
public class TestClass implements Super {
private static final int staticVar = 0;
private int instanceVar = 0;
public int instanceMethod(int param) { return param + 1; }
}
interface Super { }
使用 JDK 1.6.0_37 javac 編譯,得到 TestClass.class 二進制結構:
CA FE BA BE → 確認為 Class 文件。
00 00 → minor_version = 000 32 → major_version = 50(十進制),對應 JDK 1.6。00 18 → 24,實際常量 23 個(索引 1~23)。
首個常量位於 0000000Ah,tag=0x0A (10) 為 CONSTANT_Methodref_info:
class_index = 0x0004 → 指向第 4 個常量 java/lang/Objectname_and_type_index = 0x0013 → 指向第 19 個常量 <init>:()V推斷:此為編譯器生成的實例構造器引用。完整常量池可用 javap -verbose TestClass 查看:
00 21 = ACC_PUBLIC (0x0001) | ACC_SUPER (0x0020)。
this_class = 0x0003 → 第 3 常量 com/ejushang/TestClass/TestClass 
super_class = 0x0004 → 第 4 常量 java/lang/Object 
interfaces_count = 1, interfaces[0] = 0x0005 → 第 5 常量 com/ejushang/TestClass/Super 
fields_count = 2 
字段 1:private static final int staticVar = 0 
access_flags = 0x001A = ACC_PRIVATE | ACC_STATIC | ACC_FINAL 
name_index = 0x0006 → staticVardescriptor_index = 0x0007 → I (int)attributes_count = 1:ConstantValue 屬性
,constantValue_index = 0x0009 → 第 9 常量 CONSTANT_Integer_info = 0字段 2:private int instanceVar = 0 
access_flags = 0x0002 = ACC_PRIVATEname_index = 0x000A → instanceVardescriptor_index = 0x0007 → Iattributes_count = 0methods_count = 2 
方法 1:編譯器生成的 <init>()V 
access_flags = 0x0001 = ACC_PUBLICname_index = 0x000B → <init>descriptor_index = 0x000C → ()Vattributes_count = 1:Code 屬性

Code 屬性關鍵字段:
max_stack:操作數棧最大深度max_locals:局部變量表大小(以 Slot 為單位,long/double 佔 2 Slot,Slot 可重用)code_length + code[]:字節碼指令流exception_table:異常處理表attributes:如 LineNumberTable(源碼行號與字節碼對應,-g:none 可關閉)

方法 2:public int instanceMethod(int param)

descriptor = (I)I:一個 int 參數,返回 intiload_1 (載入參數) → iconst_1 (載入常量 1) → iadd (相加) → ireturn (返回)

sourcefile_index = 0x0012 → 第 18 常量 TestClass.java。
通過逐字節拆解,我們清晰地看到 Java 源碼如何映射為 JVM 可執行的 Class 文件結構。掌握這一機制,不僅有助於理解類加載、字節碼增強、動態代理等進階主題,更是通往 JVM 內核的必經之路。