接上文
------------------------------------
二、类文件结构
虚拟机不关注Class的来源是什么语言,它只要符合Class文件应有的结构就可以在Java虚拟机中运行。
1.Class类文件结构
Class文件是一组8位字节为基础的二进制流,各个数据项目严格按照顺序紧凑排列在Class文件中,中间没有添加任何分隔符。当遇到需要占用8位字节以上空间的数据项时,会按照高位在前的方式分割成若干个8位字节进行存储。
Class文件格式采用一种类似于C语言结构体的伪结构来存储,这种伪结构中只有2种数据类型:无符号数和表。
无符号数:属于基本数据类型,以u1、u2、u4、u8来分别代表1个字节、2个字节、4个字节和8个字节的无符号数。无符号数可以用来描述数字、索引引用、数量值、或按UTF-8编码构成的字符串值。
表:由多个无符号数或其他表作为数据项构成的复合数据类型,所有表都习惯性的以_info结尾。表用于描述有层次关系的复合结构的数据,整个Class文件本质上就是一张表。
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_flags 数量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
(1)魔数与Class文件版本:
每个Class文件头4个字节称为魔数,唯一作用是确定这个文件是否能被虚拟机接受的Class文件。
因为使用文件扩展名可以很随意地被改动,使用魔数而不是扩展名来进行识别主要是基于安全考虑,只要这个魔数值还没有被广泛用过而引起混淆即可。
Class文件的魔数值为:0xCAFEBABE
紧接着魔数的4个字节存储的是Class文件版本号:第5和第6字节是次版本号,第7和第8字节是主版本号。
高版本JDK可以向下兼容以前版本的Class文件,但不能运行以后版本的Class文件,即使文件格式并未发生变化。
JDK1.5 主版本号 49 \ JDk1.6 主版本号 50
(2)常量池constant_pool:
紧接着主次版本号的是常量池入口,常量池是Class文件结构中与其它项目关系最多的数据类型,也是占用Class文件空间最大的数据项目之一,同时是Class文件中第一个出现的表类型数据。
由于常量池的数量不固定,所以在常量池入口需放置一项u2类型的数据,代表常量池容量计数值。这个容器计数是从1而不是从0开始的。Class文件格式规范将第0项常量空出来是有特殊考虑的,这样为了满足后面某些指向常量池的索引值的数据在特定情况下需要表达"不引用任何一个常量池项目"的意思。
只有常量池容量计数是从1开始的,对于其他集合类型,包括接口索引集合、字段表集合、方法表集合等的容量计数都与一般习惯相同,是从0开始的。
常量池主要存放2大类常量:字面量和符号引用。
字面量:如文本字符串、被声明为final的常量值。
符号引用:类和接口全限定名、字段名称和描述符、方法名称和描述符。
虚拟机运行时,需要从常量池获得对应的符号引用,再在类创建时或运行时解析并翻译到具体的内存地址之中。
常量池中每一项常量都是一个表,共有11种结构各不相同的表结构数据,者11种表都有一个共同的特点。即使表开始的第一位是一个u1类型的标志位(取值为1-12,缺少标志为2的数据类型),代表当前这个常量属于哪种常量类型。
在JDK的bin目录中,有一个专门用于分析Class文件的字节码的工具javap:
javap -verbose TestClass
(3)访问标志access_flags:
在常量池结束之后,紧接着的2个字节代表访问标志,这个标志用于识别一些类或接口层次的访问信息。包括:这个Class是类还是接口,是否定义为public类型,是否定义为abstract类型。如果是类的话,是否被声明为final,等等。
(4)类索引、父类索引与接口索引集合:
类索引和父类索引都是一个u2类型的数据,而接口索引集合是一组u2类型的数据的集合,Class文件中由这3项数据来确定这个类的继承关系。
Java不允许多重继承,所以父类索引只有一个,除了java.lang.Object之外,所有Java类都有父类,父类的索引都不为0。接口索引集合就用来描述这个类实现了哪些接口,这些被实现的接口将按implements语句后的接口顺序从左到右排列在接口的索引集合中。
类索引、父类索引和接口索引集合都按顺序排列在访问标志之后,类索引和父类索引用两个u2类型的索引值表示。对于接口索引,入口第一项为u2类型的数据为接口计数器,表示索引表的容量。如果该类没有实现任何接口,那么该计数器值为0。,后面接口的索引表不再占用任何字节。
(5)字段表集合:
字段表用于描述接口或类中声明的变量。
字段包括了类级变量或实例级变量,但不包括在方法内部声明的变量。
(6)方法表集合。
标签:文件,常量,笔记,学习,索引,接口,JVM,Class,字节 From: https://blog.51cto.com/u_6978506/7469660