Java类文件结构
Class文件
Class文件是一组以8个字节为单位的二进制流。如果遇到需要占用8个字节以上的数据项,按照高位在前的方式分割为多个8个字节进行存储。
类型 | 名称 | 长度 |
---|---|---|
U4 | megic | 1 |
U2 | minor_version | 1 |
U2 | major_version | 1 |
U2 | constant_pool_count | 1 |
cp_info | constant_pool | constant_pool_count -1 |
U2 | access_flags | 1 |
U2 | this_class | 1 |
U2 | super_class | 1 |
U2 | interfaces_count | 1 |
U2 | interfaces | interfaces_count |
U2 | fields_count | 1 |
field | fields | fields_count |
U2 | methods_count | 1 |
method_info | methods | methods_count |
U2 | attributes_count | 1 |
attribute_info | attributes | attributes_count |
魔数和class文件的版本
megicNum = 0xCAFEBABE
Java版本号从45.0开始。
JDK 13的可生成的最大主版本号为57.0。
高版本JDK向下兼容低版本号Class文件,但是不能运行高版本Class文件。
次版本号作用:
Class文件中使用了该版本JDK尚未列入正式特性清单中的预览功能, 则必须把次
版本号标识为65535, 以便Java虚拟机在加载类文件时能够区分出来。
常量池
常量池的大小constant_pool_count是从1开始计数,其他几个大小的数值都是从0开始。
常量池里面的内容
- 字面量
- 文本字符串
- 声明为final的常量值
- 符号引用
- 被模块导出或者开放的包
- 类和接口的全限定名
- 字段的名称和描述符
- 方法的名称和描述符
- 方法句柄和方法类型
- 动态调用点和动态常量
Java代码在Javac编译的时候,不像C或者C++有连接这个步骤,是在虚拟机加载Class文件的时候进行动态连接。
Java中每一项常量都是一个表,截止到JDK13,共有17中常量类型。
访问标志
2个字节标识一些类或者接口层次的访问信息。
标志名称 | 标志值 | 含义 |
---|---|---|
ACC_PUBLIC | 0x0001 | 是否为public类型 |
ACC_FINAL | 0x0010 | 是否被声明为final,只有类可设置 |
ACC_SUPER | 0x0020 | 是否允许使用invokespecial字节码指令的新语义,JDK 1.0.2以后都为真 |
ACC_INTERFACE | 0x0200 | 标识这是一个接口 |
ACC_ABSTRACT | 0x0400 | 是否为abstract类型,接口和抽象类这个标志为真 |
ACC_SYNTHETIC | 0x1000 | 标志这个类并非由用户代码产生 |
ACC_ANNOTATION | 0x2000 | 标识这是一个注解 |
ACC_ENUM | 0x4000 | 标识这是一个枚举 |
ACC_MODULE | 0x8000 | 标识这是一个注解 |
类索引、父类索引和接口索引集合
这三项数据来确定该类型的继承关系。
需要结合常量池来查找获取数据。数据对应的值是常量池中的index。
字段表集合
fiel_info用于藐视接口或者类中声明的变量。
包括类及变量和实例级变量,但是不包括方法内部声明的局部变量。
字段表结构
类型 | 名称 | 含义 | 数量 |
---|---|---|---|
u2 | access_flags | 访问标志 | 1 |
u2 | name_index | 字段简单名称 | 1 |
u2 | descriptor_index | 字段和方法的描述符 | 1 |
u2 | attributes_count | 属性数量 | 1 |
attribute_infor | attributes | 属性 | attributes_count |
字段访问标识
标志名称 | 标志值 | 含义 |
---|---|---|
ACC_PUBLIC | 0x0001 | 是否为public类型 |
ACC_PRIVATE | 0x0002 | 是否为private |
ACC_PROTECTED | 0x0004 | 是否protected |
ACC_STATIC | 0x0008 | 是否为static |
ACC_FINAL | 0x0010 | 是否为final |
ACC_VOLATILE | 0x0040 | 是否是volatile |
ACC_TRANSIENT | 0x0080 | 是否transient |
ACC_SYNTHETIC | 0x1000 | 字段是否由编译期自动产生 |
ACC_ENUM | 0x4000 | 字段是否enum |
方法表集合
对方法的描述与对字段的描述几乎完全一致。
方法里面的Java代码,经过Javac编译以后,成为字节码指令,存放在方法属性集合中一个名为“Code”的属性里面。
方法表结构
类型 | 名称 | 含义 | 数量 |
---|---|---|---|
u2 | access_flags | 访问标志 | 1 |
u2 | name_index | 字段简单名称 | 1 |
u2 | descriptor_index | 字段和方法的描述符 | 1 |
u2 | attributes_count | 属性数量 | 1 |
attribute_infor | attributes | 属性 | attributes_count |
方法访问标志
标志名称 | 标志值 | 含义 |
---|---|---|
ACC_PUBLIC | 0x0001 | 是否为public类型 |
ACC_PRIVATE | 0x0002 | 是否为private |
ACC_PROTECTED | 0x0004 | 是否protected |
ACC_STATIC | 0x0008 | 是否为static |
ACC_FINAL | 0x0010 | 是否为final |
ACC_SYNCHRONIZED | 0x0020 | 是否为sychronized |
ACC_BRIDGE | 0x0040 | 是否是由编译期产生的桥接方法 |
ACC_VARARGS | 0x0080 | 是否接受不定参数 |
ACC_NATIVE | 0x0100 | 是否为native |
ACC_ABSTRACT | 0x0400 | 是否为abstract |
ACC_STRICT | 0x0800 | 是否为strictfp |
ACC_SYNTHETIC | 0x1000 | 方法是否由编译期自动产生 |
属性表集合
属性是可以由编译期自定义并解析的。不认识的会被忽略。
预定义的属性
最初只定义了9项,现在Java SE12中已经达到29项。
属性表结构
类型 | 名称 | 含义 | 数量 |
---|---|---|---|
u2 | attribute_name_index | 属性名称索引 | 1 |
u4 | attribute_length | 属性表长度 | 1 |
u1 | info | 自定义的属性值 | attribute_lenth |
code属性
类型 | 名称 | 含义 | 数量 |
---|---|---|---|
u2 | attribute_name_index | 属性名称索引 | 1 |
u4 | attribute_length | 属性表长度 | 1 |
u2 | max_stack | 操作数栈深度的最大值 | 1 |
u2 | max_locals | 局部变量所需的存储空间 | 1 |
u4 | code_length | 字节码长度 | 1 |
u1 | code | 字节码 | code_lenth |
u2 | exception_table_lenth | 异常表长度 | 1 |
exception_info | exception_table | 异常表 | exception_table_lenth |
u2 | attributes_count | 属性长度 | 1 |
attribute_info | attributes | 属性 | attributes_count |
其他属性这里就暂不介绍了。