Java虚拟机三:Class类文件的结构

Posted 刘镓旗

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java虚拟机三:Class类文件的结构相关的知识,希望对你有一定的参考价值。

我们知道我们编写的Java代码最终会被编译成.class字节码文件,其实就是一组二进制码。是以8位字节为基础的,如果遇到了超过8字节以上的数据时,会按照高位在前的方式分割成若干个8位字节的方式存储。
Java虚拟机规范的规定,class文件格式采用了一种类似于C语言结构体的伪结构体来存储数据,这种伪结构中只有两种数据类型:无符号数和表。

无符号数:属于基本的数据类型,以u1,u2,u4,u8来代表1个字节、两个字节、4个字节和8个字节的无符号数,无符号数可以用来描述数组、索引引用、数量值或者utf-8编码的字符串。

表:是由多个无符号数或者其他的表组成的复合型类型,所有的表都习惯性的以“_info”结尾。

简单理解:我们java是面向对象的,那么我们也可以把无符号数简单理解为是我们一个对象中的字段,而表则可以简单理解为一个对象

我们先来看一张表 : 这张表就是Class文件的组成部分了,我们下面会一一对每一个解释

Java严格规定Class的结构无论是顺序、数量、数据存储的字节都要严格按上图中的顺序来,绝对不运行改变。

上图中类型这一行带结尾带_info的就是表,不带的代表的是占用几个字节的无符号数。名称中有的出现的xxx_count是因为当出现了同一类型的,但是数量又不定的数据时,会采用一个容器计数器加上若干个连续的形式来表示,这时称这一系列连续的同一类型的数据叫某类型的集合。数量中代表的这种类型的个数。

先大概说一下每一个代表什么意思,然后我们再对每一个详细的说。

magic: 魔数

minor_version:class文件的次版本号

major_version : class文件的主版本号

constant_pool : class常量池

access_flags : 访问标志

this_class : 本类索引

super_class : 父类索引

interfaces : 接口索引

field_info : 字段表

method_info : 方发表

attribute_info : 属性表

如果我们用WinHex打开一个Class文件是这样的:开始说的其实就是一组二进制码

详细解释:

1.magic : 魔数

很多类型的文件,其起始的几个字节的内容是固定的(或是有意填充,或是本就如此)。根据这几个字节的内容就可以确定文件类型,因此这几个字节的内容被称为魔数 (magic number),只是用来判断是否是一个有效Class文件,Class文件的魔数是0xCAFEBABE,也就是最前面的4个自己,可以看一下上面的图。

2.minor_version 和 major_version :

用来代表标示class文件的版本,版本号的作用在于使得虚拟机能够认识当前加载class的文件格式。从而准确的提取class文件信息。在魔数后面的第5个和第6个字节代表次版本号(minor_version),第7个和第8个字节代表主版本号(major_version ),我们看一下上面的图,我们上图的是33代表是jdk1.7版本,可以被jdk1.7以上的虚拟机执行,高版本的jdk可以像下兼容。

jdk版本号:
JDK 1.8 = 十进制 52 = 十六进制 34
JDK 1.7 = 十进制 51 = 十六进制 33
JDK 1.6 = 十进制 50 = 十六进制 32
JDK 1.5 = 十进制 49 = 十六进制 31
JDK 1.4 = 十进制 48 = 十六进制 30
JDK 1.3 = 十进制 47 = 十六进制 2F
JDK 1.2 = 十进制 46 = 十六进制 2E
JDK 1.1 = 十进制 45 = 十六进制 2D

3.constant_pool : class 常量池

首先我们要明确一点的是,这个常量池和第一篇中说的运行时常量池不是一回事。可以看 Java虚拟机一:Java运行时内存区域及对象的创建。class常量池用来存储在“编译期间”产生的字面量和符号引用,字面量可以理解为Java层面的常量,比如文本字符串、声明为final的常量等。符号引用属于编译原理方面的概念,主要包括三种:类和接口的全限定名,字段的名称和描述符,方法的名称和描述符。

4.access_flags : 访问标志

占用两个字节,用来标示一些类或者接口的访问信息,例如这个文件是Class还是接口,是否是public的,是否为abstract的,是否声明为final的等等,下图是访问标志的取值。

5.this_class 、super_class、interfaces : 类索引、父类索引、接口索引

Class文件中于这三个数据来确定这个类的继承关系。类索引用来确定这个类的全限定名,父类索引用来确定父类的权限定名,接口索引用来确定接口的全限定名。类索引和父类索引都是一个两字节的数据,而接口索引确实一组两字节的数据,因为接口是可以多实现的。

6.field_info : 字段表

字段表,这是一个表示由多个无符号数或者其他表组成,用于描述接口或者类中声明的变量。用来描述自动被哪种修饰符修饰(public、private、protected)、是否是static的、是否是final的、是否是volatile的等,其中字段名称和数据类型因为不是固定的,所以引用常量池中的常量来描述。

字段表结构

字段修饰符放在access_flags中,他和类中的access_flags很像,但是取值却不一样。
字段表中access_flags的取值

name_index和descriptor_index它们是对常量池的引用,代表字段的名称和字段的描述符,描述符可以理解为数据类型。
描述符含义:

attribute_info属性表中用来存放,如果是final修饰的字段的常量值

7.method_info : 方法表

方发表和字段表结构基本一样,就是取值可能不一样,方法中的代码存放在方发表中的属性表中。

方发表结构

方法表中access_flags的取值

attribute_info属性表中用来存放,存放java代码生成的字节码指令。

8.attribute_info : 属性表

用来描述某些场景下的专有信息,例如在字段表中是用来存放final的常量值,在方发表中用来存放java代码生成的字节码指令。

以上是关于Java虚拟机三:Class类文件的结构的主要内容,如果未能解决你的问题,请参考以下文章

深入理解Java虚拟机——Class类文件的结构

Java虚拟机 - Class类文件结构

Java虚拟机-类文件结构

深入理解Java虚拟机04--类结构文件

深入Java虚拟机之二:Class类文件结构

深入Java虚拟机之二:Class类文件结构