java虚拟机随手笔记(5)java类文件结构

本文详细解析了Java Class文件的结构,包括8位字节为基础的二进制流、无符号数与表、魔数、版本号、常量池、访问标志、类索引、父类索引、接口索引集合、字段表、方法表及属性表等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 其实在整理字段表和方法表的时候有点疑问,字段表不是也能用描述符描述方法吗(详见下面字段表的描述)?那么还需要方法表干什么呢?后来想了半天,我想到了一个可能的答案,不知道准不准确,先记下来,欢迎评论去留言补充。

字段表存储的是类中的字段,这些字段可能是一些类成员变量,也可能是调用的某个其他类的方法,那么这个方法是不是算作字段存储到字段表当中呢?可能是因为这样,暂时想不到其他的可能性了。而方法表是存储的本类的方法,因为方法表中包含可执行的代码。

class类文件的结构

1.class文件是一组以8位字节为基础单位的二进制流,各个项目数据严格按照顺序紧凑的排列在class文件中,中间没有任何分隔符。8位以上的字节数据项会按照高位在前的方式分割成若干8位字节存储。
2.class文件格式只有两种数据类型:无符号数和表;整个class文件本质上就是一张表
无符号数:基本的数据类型,以u1,u2,u3,u4表示1个字节,2个字节,4个字节等。可以用来描述数字,索引引用,数量值或者utf编码的字符串值
表:由多个无符号数或者其他表违数据项构成的复合数据类型。

3.魔数:每个class文件的头4个字节被称作魔数(magic number),它的唯一作用就是确定这个文件是否为一个能被虚拟机接受的class文件
4.版本号:紧接着魔数的4个字节是版本号,5,6个字节是次版本号,7,8个字节是主版本号
5.常量池:版本号会后的是常量池,可以理解为class文件的资源仓库。由于常量池大小不定,所以需要在开头处放一个u2大小的数据,代表常量池容量。常量池主要存放两大厂常量,字面量和符号引用。字面量如文本字符串,final的常量值等。符号引用则主要为:类和接口的全限定名,字段的名称和描述符,方法的 名称和描述符。



6. 常量池结束之后,紧接着的两个字节代表访问标志(access_flag),这个标志用于识别一些类或者接口层次的访问信息,包括:这个class是类还是接口;是否定义为public类,是否为abstract类型,是否被声明为final类等。
7.类索引,父类索引和接口索引集合:访问标志之后,就是类索引,父类索引和接口索引集合了。类索引和父类索引都是u2大小的数据,接口索引集合时若干的u2类型的数据的集合。类索引确定这个类的全限定名,父类索引用于确定这个类的父类的全限定名,接口索引记录了implements语句后面的所有的接口。同时,由于接口索引是个集合,所以接口所以开头会用一个u2的类型表示接口组的长度.
这些索引都指向一个CONSTANT_Class_info类型,这个类型又指向一个CONSTANT_Utf8_info,从而确定他们的全限定名。(这些CONSTANT都存储在常量表中)

8.字段表(field_info)集合,用于描述接口或者类中声明的变量。字段包括类级变量(static)以及实例变量,但不包括方法内部声明的局部变量。字段表中的每个字段都包含5个部分:修饰符(name_flags),名称索引(name_index),字段和方法的描述符(descriptor_index),属性数量(attribute_count)、属性(attribute),如下图。下面分别讲一下这几部分的含义。

修饰符:用来表示变量的各种信息,比如可见性(作用域,public,private还是protected),实例变量还是类变量(static),可变性(final),共享可见性(volatile),可否被序列化(transient),字段数据类型(基本数据类型,对象,数组),字段名称,见下图。


跟随标识符的是两项索引值,name_index和descriptor_index。他们都是对常量池的引用,分别代表着字段的简单名称引用以及字段和方法的描述符引用。可能指向的都是CONSTANT_Utf8_info

描述符比较复杂,详见下图:


对于数组类型,每一维度将使用一个前置的“[”字符来描述,如一个定义为“java.lang.String[][]”类型的二维数组,将被记录为:“[[Ljava/lang/String;”,一个整型数组“int[]”将被记录为“[I”。

用描述符来描述方法时,按照先参数列表,后返回值的顺序描述,参数列表按照参数的严格顺序放在一组小括号“()”之内。 如方法void inc()的描述符为“()V”,方法
java.lang.String toString()的描述符为“()Ljava/lang/String;”,方法int indexOf(char[]source,int sourceOffset,int sourceCount,char[]target,int targetOffset,int targetCount,int fromIndex)的描述符为“([CII[CIII)I”。

9、方法表:class文件中方法表的存储格式,和字段表的存储格式几乎一致,如下图所示

access_flags也几乎和上面的一样,只不过方法没有volatile和transient关键字修饰,所以只能就知识少了ACC_VOLATILE和ACC_TRANSIENT标志。

方法的代码并不存在方法表中,而是存在下面我们要介绍的名为“Code”的属性表中。

10.属性表:class文件、字段表、方法表都可以携带自己的属性表集合,用于描述某些场景专有的信息,属性表集合的限制有所宽松,不再要求属性的顺序以及长度。对于方法表的预定义属性,java se7已经增加到了21项,分别表示不同的使用位置,比如字段表内final常量的值,比如方法表内的代码等,见下图



对于属性表中的每个属性,一般都由有三个部分组成:属性名称的引用(指向常量池),属性长度,以及第三个部分的信息,第三部分长度对于不同种类的属性有不同的表示,比如Code属性如下:


attribute_name_index:指向CONSTANT_Utf8_info型常量的索引,常量值固定为Code

attribute_length:指示了属性的长度,由于属性名称和长度一共占了6个字节(前两个,也就是这个和上一个),所以属性值的长度固定位整个属性表长度减去6个字节

max_stack:代表了操作数栈的深度的最大值,在方法执行的任意时刻,操作数栈都不会超过这个深度。虚拟机运行的时候需要根据这个来分配栈帧。

max_locals嗲表了局部变量表所需的存储空间

code_length和code用来存储java源程序编译后生成的字节码指令。codelength的长度限制为u2,虽然长度为u4.可能在编译jsp文件时,会把jsp内容以及页面输出归并与一个方法之中,导致编译失败。

后面还有异常表部分的介绍,不重要不看了

再往后还有字节码等等之类的介绍(就是java的类似汇编指令集),不重要,也不看了。有时间再来看吧
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值