Java Class文件结构

Class文件是一组以8位字节为基础单位的二进制流。当遇到需要占用8位字节以上空间的数据项时,则会按照【高位在前】的方式分割成若干个8位字节进行存储,按照Java虚拟机规范的规定,Class文件结构只有两种数据类型:无符号数和表

1. 无符号数

  • 无符号数属于基本的数据类型,以u1、u2、u4、u8来分别代表1个字节,2个字节,4个字节,8个字节的无符号数。
  • 无符号数可用来描述数字、索引引用、数量值或者按照UTF-8编码构成字符串值。

2. 表

  • 由多个无符号数或者其他表作为数据项构成的复合数据类型,以“_info”结尾。表用于描述有层次关系的复合结构的数据,整个Class文件本质上就是一张表。由以下数据项构成
类型 名称 数量
u4 magic 1
u2 minor_version 1
u2 major_version 1
u2 constant_pool_count 1
cp_info constant_pool constant_pool_count-1
u2 access_flag 1
u2 this_class 1
u2 super_class 1
u2 interfaces_count 1
u2 interfaces interfaces_count
u2 fields_count 1
field_info fields fields_count
u2 methods_count 1
method_info methods methods_count
u2 attributes_count 1
attribute_info attributes attributes_count

2.1 魔数

  • 每个Class文件的头4个字节成为魔数(Magic Number),它的唯一作用是确定这个文件是否为一个能被虚拟机接受的Class文件,为0xCAFEBABE

2.2 版本号

  • 紧接着魔数的这个字节存储的是Class文件的版本号:
    • 第5和第6个字节是次版本号(Minor Version)
    • 第7和第8个字节是主版本号(Major Version)
  • Java的版本号是从45开始的

2.3 常量池

  • 占用Class文件空间最大的数据项目之一,同时也是Class文件中第一个出现的表类型数据项目;

  • 在常量池的入口处存在一项u2类型的数据,代表常量池容量计数值(constant_pool_count),这个容量计数是从1开始的。第0项常量为空的作用是为了表达“不引用任何一个常量池项目”

  • 常量池中主要存放两大类常量:字面量(Literal)和符号引用(Symbolic Reference):

    • 字面量比较接近Java语言的常量概念,如文本字符串、声明为final的常量值等;

    • 符号引用,包括了三类常量:类和接口的全限定名、字段的名称和描述符、方法的名称和描述符。

  • 常量池中每一项常量都是一个表,这14种表都有一共同的特点,就是表开始的第一位是一个u1类型的标志位(tag,取值见下表“标志”列),代表当前这个常量属于哪种常量类型

常量池的项目类型

类型 标志 描述
CONSTANT_Utf8_info 1 UTF-8编码的字符串
CONSTANT_Integer_info 3 整型字面量
CONSTANT_Float_info 4 浮点型字面量
CONSTANT_Long_info 5 长整型字面量
CONSTANT_Double_info 6 双精度浮点型字面量
CONSTANT_Class_info 7 类或接口的符号引用
CONSTANT_String_info 8 字符串类型字面量
CONSTANT_Fieldref_info 9 字段的符号引用
CONSTANT_Methodref_info 10 类中方法的符号引用
CONSTANT_InterfaceMethodref_info 11 接口中方法的符号引用
CONSTANT_NameAndType_info 12 字段或方法的部分符号引用
CONSTANT_MethodHandle_info 15 表示方法句柄
CONSTANT_MethodType_info 16 标识方法类型
CONSTANT_InvokeDynamic_info 18 标识一个动态方法调用点

常量池中14种常量项的结构总表

常量 项目 类型 描述
CONSTANT_Utf8_info tag u1 值为1
length u2 UTF-8编码的字符串占用的字节数
bytes u1 长度为length的UTF-8编码的字符串
CONSTANT_Integer_info tag u1 值为3
bytes u4 按照高位在前存储的int值
CONSTANT_Float_info tag u1 值为4
bytes u4 按照高位在前存储的float值
CONSTANT_Long_info tag u1 值为5
bytes u8 按照高位在前存储的long值
CONSTANT_Double_info tag u1 值为6
bytes u8 按照高位在前存储的double值
CONSTANT_Class_info tag u1 值为7
index u2 指向全限定名常量项的索引
CONSTANT_String_info tag u1 值为8
index u2 指向字符串字面量的索引
CONSTANT_Fieldref_info tag u1 值为9
index u2 指向声明字段的类型或者接口描述符CONSTANT_Class_info的索引项
index u2 指向字段描述符CONSTANT_NameAndType_info的索引项
CONSTANT_Methodref_info tag u1 值为10
index u2 指向声明方法的类描述符CONSTANT_Class_info的索引项
index u2 指向名称及类型描述符CONSTANT_NameAndType_info的索引
CONSTANT_InterfaceMethodref_info tag u1 值为11
index u2 指向声明方法的接口描述符CONSTANT_Class_info的索引项
index u2 指向名称及类型描述符CONSTANT_NameAndType_info的索引项
CONSTANT_NameAndType_info tag u1 值为12
index u2 指向该字段或方法名称常量项的索引
index u2 指向该字段或方法描述符常量项的索引
CONSTANT_Method_Handle_info tag u1 值为15
reference_kind u1 值必须在1~9之间(包括1和9),它决定了方法句柄的类型。方法句柄类型的值表示方法句柄字节码的行为
reference_index u2 值必须是对常量池的有效索引
CONSTANT_Method_Type_info tag u1 值为16
descriptor_index u2 值必须是对常量池的有效索引,常量池在该索引处的项必须是CONSTANT_Utf8_info结构,表示方法的描述符
CONSTANT_Invoke_Dynamic_info tag u1 值为18
bootstrap_method-attr_index u2 值必须是对当前Class文件中引导方法表的bootstrap——method[]数组的有效索引
name_and_type_index u2 值必须是对常量池的有效索引,常量池在该索引处的项必须是CONSTANT_NameAndType_info结构,表示方法名和方法描述符
  • 注:分析Class文件字节码命令:javap -v 编译后Class文件名

2.4 类访问标志

  • 用于识别一些类或接口层次的访问信息,包括:这个Class是类还是接口;是否定义为public类型;是否定义为abstract类型;如果是类的话,是否被声明为final等.

具体的标志位以及标志的含义见表

访问标志 标志值 含义
ACC_PUBLIC 0X0001 是否为public类型
ACC_FINAL 0X0010 是否被声明为final,只有类可设置
ACC_SUPER 0X0020 是否允许使用invokespecial字节码指令
ACC_INTERFACE 0X0200 标识为一个接口
ACC_ABSTRACT 0X0400 是否为abstract类型,对于接口或抽象类而言,此标志值为真,其他类值为假
ACC_SYNTHETIC 0X1000 标识这个类并非由用户代码产生的
ACC_ANNOTATION 0X2000 标识这是一个注解
ACC_ENUM 0X4000 标识这是一个枚举

2.5 类索引、父类索引与接口索引集合

  • 类索引(this_class)和父类索引(super_class)都是一个u2类型的数据,而接口索引集合(interfaces)是一组u2类型的数据的集合,Class文件中由这三项数据来确定这个类的继承关系。

  • 类索引用于确定这个类的全限定名,父类索引用于确定这个类的父类的全限定名。(除了java.lang.Object外,所有Java类的父类索引都不为0);接口索引集合用来描述这个类实现了哪些接口。

  • 类索引、父类索引和接口索引集合都按顺序排列在访问标志后面,类索引和父类索引用两个u2类型的索引值表示,它们各自指向一个类型为CONSTANT_Class_info的类描述符常量,通过CONSTANT_Class_info类型的常量中的索引值可以找到定义在CONSTANT_Utf8_info类型的常量中的全限定名字符串。

  • 接口索引集合,入口的第一项——u2类型的数据为接口计数器(interfaces_count),表示索引表的容量。如果该类没有实现任何接口,则该计数器值为0,后面的接口索引表不占用任何字节。

##2.6 字段表集合
字段表(field_info)用于描述接口或类中声明的表量。字段(field)包括类级变量以及实例级变量,但不包括在方法内部声明的局部变量。字段叫什么名字、字段被定义为什么数据类型,这些都是无法固定的,只能引用常量池中的常量来描述。

字段表结构

类型 名称 数量 备注
u2 access_flags 1 字段访问标志
u2 name_index 1 字段的简单名称(对常量池的引用)
u2 descriptor_index 1 字段和方法的描述符(对常量池的引用)
u2 attributes_count 1
attribute_info 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_ENMU 0x4000 字段是否enmu

描述符的作用是用来描述字段的数据类型、方法的参数列表(包括数量、类型以及顺序)和返回值。根据描述符规则,基本数据类型(byte、char、double、float、int、long、short、boolean)以及代表无返回值的void类型都是用大写字符来表示,而对象;类型则用字符L加对象的全限定名来表示。详见下表:

描述符标识字符含义

标识字符 含义 标识字符 含义
B 基本类型byte J 基本类型long
C 基本类型char S 基本类型short
D 基本类型double Z 基本类型boolean
F 基本类型float V 基本类型void
I 基本类型int L 对象类型,如Ljava/lang/Object
对于数组类型,每一个一维度将使用一个前置的“[”字符来描述,如一个定义为 java.lang.String[][]类型的二维数组,将被记录为:[[Ljava/lang/String。

##2.7 方法表集合

方法表结构

类型 名称 数量 备注
u2 access_flags 1 字段访问标志
u2 name_index 1 字段的简单名称(对常量池的引用)
u2 descriptor_index 1 字段和方法的描述符(对常量池的引用)
u2 attributes_count 1
attribute_info attributes attributes_count
  1. 因为volatile关键字和transient关键字不能修饰方法,所以在方法表的访问标志中没有了ACC_VOLATILE标志和ACC_TRANSIENT标志
  2. synchronized、native、strictfp和abstract关键字可以修饰方法,所以方法表的访问标志中增加了

方法访问标志

标志名称 标志值 含义
ACC_PUBLIC 0x0001 方法是否为public
ACC_PRIVATE 0x0002 方法是否为private
ACC_PROTECTED 0x0004 方法是否为protected
ACC_STATIC 0x0008 方法是否为static
ACC_FINAL 0x0010 方法是否为final
ACC_SYNCHRONIZED 0x0020 方法是否为synchronized
ACC_BRIDGE 0x0040 方法是否是由编译器产生的 桥接方法
ACC_VARARGS 0x0080 方法是否接受不定参数
ACC_NATIVE 0x0100 方法是否为native
ACC_ABSTRACT 0x0400 方法是否为abstract
ACC_STRICTFP 0x0800 方法是否为strictfp
ACC_SYNTHETIC 0x1000 方法是否是由编译器自动产生的

你可能感兴趣的:(Java基础知识)