声明:本博客只是把读《深入理解JAVA虚拟机》中“JAVA类文件结构”章节的过程记录下来,很多文字描述与图都是书中原文的内容。
1、任何一个类的class文件都对应这唯一一个类或者接口的定义信息。但反过来说类和接口不一定得定义在文件里,也可以由类加载器直接生成。
2、Class文件是以8位字节(byte)为基础单位的二进制文件。根据虚拟机规范的规定,Class文件格式采用一种类似与C语言结构体的伪结构来存储数据,这种伪结构只存储两种数据类型:无符号数和表。
无符号数:基本数据类型,以u1,u2,u4,u8来分别代表1个字节,2个字节,4个字节和8个字节的无符号数。而其中,无符号数可以用来描述数字,索引引用,数值量或者按照UTF-8编码构造成字符串。
表:由多个无符号数构成或者由多个表构成,所有的表都以"_info"结尾。整个class文件本质上就是一张表。
上表已经看到了class文件的结构,之后会介绍各种数据类型
3、魔数(magic)与class文件的版本
magic是每个Class文件的头四个字节,起作用只有一个:确定这个文件是否为一个能被虚拟机接受的Class文件。
许多的文件都用魔数进行身份识别,譬如图片(gif,jpeg)都存在魔数。其值为0xCAFEBABE(四个字节)
紧接着魔数的两个字节是minor_version(此版本号),再接着两个字节是major_version(主版本号)。JAVA虚拟机必须拒绝之宗超过其版本号的Class文件
JDK1.1对应版本号:45.0-45.65535 JDK1.2则能支持46.0-46.65535的Class文件
JDK1.7可生成的主版本号最大为51.0
4、常量池(constant_pool_count(u2)与constantpool (cp_info) )
constant_pool_count从1开始计数,不是从0开始。设计者将0空出来,是为了满足后面某些指向常量池的索引值的数据在特定的情况下需要表达“不引用任何一个常量池项目”。
常量池中存放两大类常量:字面量(literal)与符号引用(symbolic reference)
字面量:文本字符串,声明为final的常量值
符号引用:包含三大类常量:
类和接口的全限定名 (Fully Qualified Name)、字段的名称和描述符、方法的名称和描述符
当虚拟机运行时,需要从常量池获得对应的符号引用,再在类创建或运行时解析、翻译到具体的内存地址中。
常量池的每一项常量都是一个表(constant_pool是由表组成的表)
常量池中的表的种类如上图,这些表在开始的第一位是一个u1类型的标志位,用来表示这个常量所属的类型
每一种常量类型都有它特定的数据结构:比如CONSTANT_Class_info
再如CONSTANT_uTF8_INFO类型:
通过javap -verbose可以帮助我们查看类文件结构。
某些没有出现过的常量在字段表、方法表、属性表中会被引用。
5、访问标志
常量池结束后,接着的两个字节代表访问标志,用于识别一些类或者接口层次的访问信息
access_flag中一共有16个标志位,但目前只定义了8个,没有使用到的标志位要求为0.
6、类索引、父类索引、接口索引集合
类索引(this class) u2
父类索引(super class) u2(只允许继承一个类)
interfaces_count u2(允许实现多个接口)
interfaces interfaces_count
可以了解到,java中除了object类,其他所有类都是有父类的。
7、字段表集合
字段表(field_info)用于描述接口或者类中声明的变量,包括类级变量以及实例级变量,不包括方法中的局部变量。
其中的access_flags:
以下两项都是对常量池的引用
name_index:字段的简单名称以及字段 (简单名称:没有类型和参数修饰的方法或者字段名称)
descriptor_index:方法的描述符
对于数组用前置的"["来描述每个维度
如java.lang.String[][]表示成[[Ljava/lang/String
全限定名:如org/fenz/calss/test/class
简单名称:没有类型和参数修饰的方法或者字段名称
描述符:用来描述字段或者方法的数据类型,方法的参数列表和返回值。
8、方法表集合
9、属性表集合