class文件
实验类:
package core;
public class Foo{
int m = 6;
public Foo{
this.show();
}
void show (){ System.out.println("father"); }
}
反汇编:

图1
涉及到的JVM指令:
aload_<n>
从局部变量表加载一个reference类型值到操作数栈中;
invokespecial
调用实例方法,专门用来调用父类方法、私有方法和实例初始化方法;
bipush
将一个byte类型数据入栈;
putfield
设置对象字段;
invokevirtual
调用实例方法,依据实例的类型进行分派;
return
方法中返回void;
getstatic
获取类的静态字段值;
ldc
从运行时常量池中提取数据并压入操作数栈;
vim -b Foo.class
:%! xxd
图2
ClassFile文件结构:
ClassFile{
u4 magic;
// Magic 魔数,固定值 0xCAFEBABE
u2 minor_version;
// 副版本号
u2 major_version;
// 主版本号
u2 constant_pool_count;
// 常量池容量计数器
cp_info constant_pool;
// 常量池
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];
// 属性表
}
根据Java虚拟机规范,Class文件格式采用类似C语言结构体的伪结构存储数据,这种结构中只有两种数据类型:无符号数和表。
无符号数属于基本的数据类型,以u1、u2、u4、u8来分别代表1个字节、2个字节、4个字节、8个字节的无符号数,无符号数可以用来描述
数字、索引引用、数量值或按照UTF-8编码构成字符串值。
表是由多个无符号数或其他表作为数据项构成的复合数据类型,所有表都以"_info"结尾。表用于描述有层次关系的复合结构的数据,整个Class
文件本质上就是一张表。
无论是无符号数还是表,当需要描述同一类型但数量不定的多个数据时,经常会使用一个前置的容器计数器加若干个连续的数据项的形式,这时
称这一系列连续的某一类型的数据为某一类型的集合。
魔数(1~4字节):确定该文件是否为一个能被虚拟机接受的Class文件。
次版本号(5~6字节):
主版本号(7~8字节):
常量池容量计数器(9~10字节):常量池中常量的数量;
常量池:常量池中主要存放两大类常量:字面量(文本字符串、声明为final的常量值等)、符号引用(类和接口的全限定名、字段的名称和
描述符、方法的名称和描述符);
常量池共有14种常量类型且均有自己的结构,因篇幅限制不赘述。
访问标志(2个字节):用于识别一些类或者接口层次的访问信息(这个Class是类还是接口;是否定义为public类型;是否定义为abstract
类型;是否被声明为final等)。
类索引(2个字节):用于确定类的全限定名;
父类索引(2个字节):用于确定类的父类的全限定名;
接口计数器(2个字节):用来描述类实现了哪些接口;
接口表(2个字节):
字段计数器(2个字节):
字段表:
方法计数器(2个字节):
方法表:
属性计数器(2个字节):
属性表:
参考:《Java虚拟机规范》、《深入Java虚拟机》
注: