Java Class文件结构

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

1. 无符号数

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

2. 表

  • 由多个无符号数或者其他表作为数据项构成的复合数据类型,以“_info”结尾。表用于描述有层次关系的复合结构的数据,整个Class文件本质上就是一张表。由以下数据项构成
类型名称数量
u4magic1
u2minor_version1
u2major_version1
u2constant_pool_count1
cp_infoconstant_poolconstant_pool_count-1
u2access_flag1
u2this_class1
u2super_class1
u2interfaces_count1
u2interfacesinterfaces_count
u2fields_count1
field_infofieldsfields_count
u2methods_count1
method_infomethodsmethods_count
u2attributes_count1
attribute_infoattributesattributes_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_info1UTF-8编码的字符串
CONSTANT_Integer_info3整型字面量
CONSTANT_Float_info4浮点型字面量
CONSTANT_Long_info5长整型字面量
CONSTANT_Double_info6双精度浮点型字面量
CONSTANT_Class_info7类或接口的符号引用
CONSTANT_String_info8字符串类型字面量
CONSTANT_Fieldref_info9字段的符号引用
CONSTANT_Methodref_info10类中方法的符号引用
CONSTANT_InterfaceMethodref_info11接口中方法的符号引用
CONSTANT_NameAndType_info12字段或方法的部分符号引用
CONSTANT_MethodHandle_info15表示方法句柄
CONSTANT_MethodType_info16标识方法类型
CONSTANT_InvokeDynamic_info18标识一个动态方法调用点

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

常量项目类型描述
CONSTANT_Utf8_infotag u1值为1
length u2UTF-8编码的字符串占用的字节数
bytesu1长度为length的UTF-8编码的字符串
CONSTANT_Integer_infotagu1值为3
bytesu4按照高位在前存储的int值
CONSTANT_Float_infotagu1值为4
bytesu4按照高位在前存储的float值
CONSTANT_Long_infotagu1值为5
bytesu8按照高位在前存储的long值
CONSTANT_Double_infotagu1值为6
bytesu8按照高位在前存储的double值
CONSTANT_Class_infotagu1值为7
indexu2指向全限定名常量项的索引
CONSTANT_String_infotagu1值为8
indexu2指向字符串字面量的索引
CONSTANT_Fieldref_infotagu1值为9
indexu2指向声明字段的类型或者接口描述符CONSTANT_Class_info的索引项
indexu2指向字段描述符CONSTANT_NameAndType_info的索引项
CONSTANT_Methodref_infotagu1值为10
indexu2指向声明方法的类描述符CONSTANT_Class_info的索引项
indexu2指向名称及类型描述符CONSTANT_NameAndType_info的索引
CONSTANT_InterfaceMethodref_infotagu1值为11
indexu2指向声明方法的接口描述符CONSTANT_Class_info的索引项
indexu2指向名称及类型描述符CONSTANT_NameAndType_info的索引项
CONSTANT_NameAndType_infotagu1值为12
indexu2指向该字段或方法名称常量项的索引
indexu2指向该字段或方法描述符常量项的索引
CONSTANT_Method_Handle_infotagu1值为15
reference_kindu1值必须在1~9之间(包括1和9),它决定了方法句柄的类型。方法句柄类型的值表示方法句柄字节码的行为
reference_indexu2值必须是对常量池的有效索引
CONSTANT_Method_Type_infotagu1值为16
descriptor_indexu2值必须是对常量池的有效索引,常量池在该索引处的项必须是CONSTANT_Utf8_info结构,表示方法的描述符
CONSTANT_Invoke_Dynamic_infotagu1值为18
bootstrap_method-attr_indexu2值必须是对当前Class文件中引导方法表的bootstrap——method[]数组的有效索引
name_and_type_indexu2值必须是对常量池的有效索引,常量池在该索引处的项必须是CONSTANT_NameAndType_info结构,表示方法名和方法描述符
  • 注:分析Class文件字节码命令:javap -v 编译后Class文件名

2.4 类访问标志

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

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

访问标志标志值含义
ACC_PUBLIC0X0001是否为public类型
ACC_FINAL0X0010是否被声明为final,只有类可设置
ACC_SUPER0X0020是否允许使用invokespecial字节码指令
ACC_INTERFACE0X0200标识为一个接口
ACC_ABSTRACT0X0400是否为abstract类型,对于接口或抽象类而言,此标志值为真,其他类值为假
ACC_SYNTHETIC0X1000标识这个类并非由用户代码产生的
ACC_ANNOTATION0X2000标识这是一个注解
ACC_ENUM0X4000标识这是一个枚举

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)包括类级变量以及实例级变量,但不包括在方法内部声明的局部变量。字段叫什么名字、字段被定义为什么数据类型,这些都是无法固定的,只能引用常量池中的常量来描述。

字段表结构

类型名称数量备注
u2access_flags1字段访问标志
u2name_index1字段的简单名称(对常量池的引用)
u2descriptor_index1字段和方法的描述符(对常量池的引用)
u2attributes_count1
attribute_infoattributesattributes_count

字段访问标志

标志名称标志值含义
ACC_PUBLIC0x0001字段是否public
ACC_PRIVATE0x0002字段是否private
ACC_PROTECTED0x0004字段是否protected
ACC_STATIC0x0008字段是否static
ACC_FINAL0x0010字段是否final
ACC_VOLATILE0x0040字段是否volatile
ACC_TRANSIENT0x0080字段是否transient
ACC_SYNTHETIC0x1000字段是否由编译器自动产生的
ACC_ENMU0x4000字段是否enmu

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

描述符标识字符含义

标识字符含义标识字符含义
B基本类型byteJ基本类型long
C基本类型charS基本类型short
D基本类型doubleZ基本类型boolean
F基本类型floatV基本类型void
I基本类型intL对象类型,如Ljava/lang/Object
对于数组类型,每一个一维度将使用一个前置的“[”字符来描述,如一个定义为 java.lang.String[][]类型的二维数组,将被记录为:[[Ljava/lang/String。

##2.7 方法表集合

方法表结构

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

方法访问标志

标志名称标志值含义
ACC_PUBLIC0x0001方法是否为public
ACC_PRIVATE0x0002方法是否为private
ACC_PROTECTED0x0004方法是否为protected
ACC_STATIC0x0008方法是否为static
ACC_FINAL0x0010方法是否为final
ACC_SYNCHRONIZED0x0020方法是否为synchronized
ACC_BRIDGE0x0040方法是否是由编译器产生的 桥接方法
ACC_VARARGS0x0080方法是否接受不定参数
ACC_NATIVE0x0100方法是否为native
ACC_ABSTRACT0x0400方法是否为abstract
ACC_STRICTFP0x0800方法是否为strictfp
ACC_SYNTHETIC0x1000方法是否是由编译器自动产生的
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值