Skip to content

字段表/方法表/属性表集合

字段表、方法和属性:Class 文件的核心内容

字段表和方法表是 Class 文件中最重要的两个集合,它们描述了类的所有字段和方法。

字段表集合(fields)

字段表结构

java
field_info {
    u2  access_flags;         // 访问标识
    u2  name_index;           // 字段名(常量池索引)
    u2  descriptor_index;     // 描述符(常量池索引)
    u2  attributes_count;     // 属性数量
    attribute_info  attributes[attributes_count];  // 属性表
}

完整字段表示例

java
public class User {
    private static final int MAX_NAME_LENGTH = 100;
    private String name;
    private int age;
    private transient String password;
}

对应的字段表:

fields_count = 4

field[0] - MAX_NAME_LENGTH:
  access_flags: ACC_PRIVATE | ACC_STATIC | ACC_FINAL
  name_index: → "MAX_NAME_LENGTH"
  descriptor_index: → "I"(int)
  attributes_count: 1
  attributes[0]: ConstantValue (value = 100)

field[1] - name:
  access_flags: ACC_PRIVATE
  name_index: → "name"
  descriptor_index: → "Ljava/lang/String;"
  attributes_count: 0

field[2] - age:
  access_flags: ACC_PRIVATE
  name_index: → "age"
  descriptor_index: → "I"
  attributes_count: 0

field[3] - password:
  access_flags: ACC_PRIVATE | ACC_TRANSIENT
  name_index: → "password"
  descriptor_index: → "Ljava/lang/String;"
  attributes_count: 0

字段的 ConstantValue 属性

static final 修饰的基本类型或字符串常量,会有 ConstantValue 属性:

java
private static final int MAX = 100;

// 字节码中的 ConstantValue 属性:
//   ConstantValue: <int 100>

方法表集合(methods)

方法表结构

java
method_info {
    u2  access_flags;         // 访问标识
    u2  name_index;           // 方法名(常量池索引)
    u2  descriptor_index;     // 描述符(常量池索引)
    u2  attributes_count;     // 属性数量
    attribute_info  attributes[attributes_count];  // 属性表
}

方法表的特殊性

方法表中的每个方法不一定对应源码中的一个方法——编译器可能会添加额外的方法:

添加的方法原因
<init>实例初始化方法(构造函数)
<clinit>类初始化方法(静态块)
<clinit>桥接方法(泛型擦除)
<clinit>内部类访问外部类的方法

编译器添加的方法示例

java
// 泛型擦除后的桥接方法
public class Container<T> {
    private T value;
    public void set(T value) { this.value = value; }
    public T get() { return value; }
}

编译后实际有 4 个方法:

// 源码中的方法
public void set(T value)   // descriptor: (Ljava/lang/Object;)V
public T get()             // descriptor: ()Ljava/lang/Object;

// 编译器添加的桥接方法
public synthetic bridge void set(Object value)  // JVM 自动添加
public synthetic bridge Object get()              // JVM 自动添加

// synthetic 标志表示这是编译器生成的,不是源码写的

查看方法表

bash
javap -verbose Container.class

# 输出:
#   public void set(java.lang.Object);
#     descriptor: (Ljava/lang/Object;)V
#     flags: ACC_PUBLIC
#     Code:
#       stack=2, locals=2, args_size=2
#          0: aload_0
#          ...
#
#   public java.lang.Object get();
#     descriptor: ()Ljava/lang/Object;
#     flags: ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
#       ↑
#       ACC_BRIDGE + ACC_SYNTHETIC → 桥接方法
#     Code:
#       ...

属性表集合(attributes)

属性表结构

java
attribute_info {
    u2  attribute_name_index;   // 属性名(常量池 Utf8 索引)
    u4  attribute_length;        // 属性长度(不含前 6 字节)
    u1  info[attribute_length];  // 属性内容
}

属性表通用结构

所有属性的通用格式:

┌──────────────────────────────────────┐
│  attribute_name_index (u2)          │ ← 属性名索引
├──────────────────────────────────────┤
│  attribute_length (u4)               │ ← 属性数据长度
├──────────────────────────────────────┤
│  info[] (u1*)                        │ ← 属性数据
└──────────────────────────────────────┘

Code 属性:方法字节码

Code 属性是方法表中最重要、最复杂的属性,它包含方法的字节码:

java
Code_attribute {
    u2  attribute_name_index;       // "Code"
    u4  attribute_length;
    u2  max_stack;                  // 操作数栈最大深度
    u2  max_locals;                 // 局部变量表最大槽数
    u4  code_length;                // 字节码长度
    u1  code[code_length];          // 字节码指令序列
    u2  exception_table_length;     // 异常表长度
    exception_table {
        u2  start_pc;               // 起始 pc
        u2  end_pc;                  // 结束 pc
        u2  handler_pc;              // 处理器的 pc
        u2  catch_type;              // 捕获的类型(常量池索引,0 表示 finally)
    } exception_table[exception_table_length];
    u2  attributes_count;           // 属性数量
    attribute_info  attributes[attributes_count];
}

Code 属性的关键字段

java
public int add(int a, int b) {
    return a + b;
}

对应的 Code 属性:

max_stack = 2    ← 操作数栈最大深度(a + b 需要 2 个操作数)
max_locals = 3   ← 局部变量表大小(this + a + b = 3)
code_length = 3 ← 字节码长度

字节码:
0: iload_1      ← 把局部变量 1(a)入栈
1: iload_2      ← 把局部变量 2(b)入栈
2: iadd         ← 弹出两个 int,相加,结果入栈
3: ireturn      ← 返回 int

查看 Code 属性

bash
javap -c -verbose User.class

# 输出:
# public int add(int, int);
#   descriptor: (II)I
#   flags: ACC_PUBLIC
#   Code:
#     stack=2, locals=3, args_size=3
#        0: iload_1
#        1: iload_2
#        2: iadd
#        3: ireturn

异常表(Exception Table)

异常表描述方法中的 try-catch-finally:

java
public void readFile() {
    try {
        // 可能有异常的代码
    } catch (IOException e) {
        // 处理 IOException
    } catch (Exception e) {
        // 处理其他异常
    } finally {
        // 清理代码
    }
}

对应的异常表:

exception_table_length = 3

exception_table[0]: IOException
  start_pc = 0      ← try 块起始
  end_pc = 20       ← try 块结束
  handler_pc = 50   ← IOException 处理器的位置
  catch_type = → CONSTANT_Class "java/io/IOException"

exception_table[1]: Exception
  start_pc = 0
  end_pc = 20
  handler_pc = 70
  catch_type = → CONSTANT_Class "java/lang/Exception"

exception_table[2]: finally
  start_pc = 0      ← 整个方法
  end_pc = 100
  handler_pc = 80   ← finally 处理器的位置
  catch_type = 0    ← 0 表示 finally

本节小结

字段表、方法表、属性表的核心要点:

集合内容关键属性
fields所有字段ConstantValue(常量值)
methods所有方法Code(字节码)
attributes附加信息Code、LineNumberTable、LocalVariableTable

Code 属性是字节码的核心,包含:

  • max_stack:操作数栈最大深度
  • max_locals:局部变量表槽数
  • code[]:字节码指令序列
  • exception_table:异常处理表

下一节,我们来看 Code/LineNumberTable 等属性解析

基于 VitePress 构建