字段表/方法表/属性表集合
字段表、方法和属性: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 等属性解析。
