Class文件结构

题外话:

本文参考书籍:《深刻理解Java虚拟机》有兴趣的同窗也能够经过看书了解更多内容。
最近在读这本书,顺便作下知识整理。在了解了class文件结构后能够更好的了解类的加载机制。若是对此内容已经了解的同窗能够移步:类的加载机制java

如需转载请注明出处,class文件的结构linux

java语言的一句口号你们应该都很熟悉:"Write once, run anywhere." 那是由于Java虚拟机不与任何语言绑定,它只与“Class文件”这种特定的二进制文件格式所关联。 Java虚拟机也不止支持java一种语言。数组

图1

图1 Class文件的结构

从上图能够看出多种语言经过编译成*.class文件均可以在JVM上运行。 下面让咱们了解一下class文件的结构。安全

Class类文件结构

注:(此文以JDK1.4为主线讲解)post

首先看一下class文件的基本概念测试

class文件是一组以8位字节为基础单位的二进制流,各个数据项目严格按照顺序节凑的排列在Class文件中,中间没有添加任何分隔符,使得整个Class文件中从存储的内容几乎所有是程序运行时的必要数据,没有任何空隙。当遇到须要占用8位以上空间的数据项时,则会按照高位在前的方式分割为若干个8位字节进行存储。this

Class文件结构中只有两种数据类型:1,无符号数。2,表。

1,无符号数:属于基本的数据类型,以u一、u二、u四、u8来分别表明一、二、四、8个字节的无符号数,无符号数能够用来描述数字、索引引用、数量值、或者按照UTF-8编码的字符串值。
2,:由多个无符号数或者其余表做为数据项构成的复合数据类型,全部表都习惯性的以“info”结尾。Class文件本质上就是一张表
下面是一张普通class文件的表,咱们能够看下表的大致结构和具体内容都有什么。编码

class文件结构

图2 Class文件格式

图2中红字标注后面会作具体分析。3d

一,魔数和Class文件的版本

魔数

每一个Class文件的前4个字节称为魔数(Magic Number),它的惟一做用是肯定这个文件是不是一个能被虚拟机接受的Class文件。使用魔数而不使用扩展名来验证是出于安全考虑,由于扩展名能够随意改变。java语言魔数就是:CAFEBABE(十六进制数)。此 魔数也用于后面类加载中的验证。cdn

Class文件版本

Class第五、6个字节是次版本号,七、8个字节是主版本号。(主版本号就是咱们常说的JDK1.7或1.8,次版本号就是1.7后面的小版本号。)

二,常量池

常量池能够理解为Class文件的资源仓库。 因为常量池中常量的数量是不固定的,因此在常量池入口须要放置一项u2类型的数据,表明常量池容量计数值(constant_pool_count)。常量池索引从1开始。

图3 常量池结构

如上图6-3所示,魔数后是小版本号和大版本号,后面紧跟着就是常量池入口,入口前两个字节表示了常量池中常量的个数。图中都为十六进制数,0x16就是十进制的22,而常量池计数都是从1开始,不是从0开始,因此这里面表示常量池中索引范围是1~21,而非0~21,因此常量池中共有21项常量。
常量池中主要存放两大类常量:1,字面量。2,符号引用。

1,字面量

接近于Java语言层面的常量概念。如:字符串,final常量等。

2,符号引用

属于编译原理方面概念,包括下面三类:

(1)类和接口的全限定名
(2)字段的名称和描述
(3)方法的名称和描述

常量池中每一项常量都是一个表,在JDK1.7以前共有14种结构各不相同的表结构数据,1.7以后又额外增长了3种。 这14种表都有一个共同的特色,就是表开始的第一位是一个u1类型的标志位(tag),表明当前这个常量属于哪一种常量类型。
下面咱们来看下这些表的结构。
上面咱们已经说过第九个字节0x16是常量池入口,也是常量池中常量的数量,那么紧接着就是常量池的第一项常量。请看下图。

图4 常量池结构

tag是标志位,区分常量类型;name_index是一个索引,它指向常量池中一个CONSTANT_Utf8_info类型常量,此常量表明了这个类和接口的全限定名。图4中第一个常量的tag为0x07,咱们查看图5中的表来肯定是哪一种常量类型。

图5 常量池项目类型

从图5中能够看出0x07也就是十进制的7表明的类型是CONSTANT_Class_info,而后咱们在查询图6中CONSTANT_Class_info的表结构。

图6 CONSTANT_Class_info型的常量结构

这里name_index值为0x0002,指向常量池第二项常量。由图4能够看出,第二项常量的tag是0x01,再查询图5可知,第二个常量是一个CONSTANT_Utf 8_info,咱们再来查询CONSTANT_Utf 8_info型常量的结构。见图7。

图7 CONSTANT_Utf8_info型的常量结构


图7中,0x01位tag位,表明CONSTANT_Utf8_info型的常量,0x001D即10进制的29,表明了字符串的长度为29,紧接着后面括号中共29个字节,表明了29个字符,表示了类的全限定名。在图8的右侧你们也能够看一下,类的全限定名被解析出来了,即"org/fenixsoft/clazz/TestClass",有兴趣能够数一下,我数过了是29个字符。[捂脸]

图8 常量池第二项常量结构

以上是给你们举了一些class文件中常量池结构的例子,以及如何分析那一部分对应常量池中的那些内容,下面就不一一举例了。有兴趣的同窗能够查看一下官方的文档。
图8是常量池中14种常量项结构总表,在JDK1.7以前只有14种,你们能够大概了解一下,常量池就介绍到这里。

图9 常量池中14种常量项结构总表

三,访问标志

常量池紧接着两个字节表明访问标志(access_flags)。 访问标志:类或接口的层次信息,包括:这个Class是类仍是接口;是不是public;是否认义abstract;若是是类的话,是否声明final等。图10表示了访问标志具体表述的全部内容。

图10 访问标志

图11 测试类的访问标志

能够查找图10中的表测试类访问标志为0x21就是图10中ACC_PUBLIC和ACC_SUPER的结合,可见这个TestClass类就是一个普通的public的java类。
这种方式你们能够联想一下linux的权限码,我感受很相似,例如:chmod 755 + 文件名,755中三位数字分表表示文件所属用户的权限、文件所属组的权限、其余用户权限。4表明读权限,2表明写权限,1表明执行权限。755就是赋予文件全部者读、写、执行权限(4 + 2 + 1 = 7),赋予文件全部者的组读、执行的权限(4 + 1 =5),赋予其余用户也是读和执行的权限(4 + 1 = 5)。是否是与这个access_flags很是类似,用不多的字符表示多个属性。

四,类索引、父类索引与接口索引集合

访问标志后接的下来就是类索引和父类索引,类索引和父类索引都是u2类型数据,接口索引集合是一组u2类型的数据集合。Class文件中由这三项数据来肯定类的继承关系。

(1)类索引,类全限定名。

(2)父类全限定名。

(3)实现的接口。入口第一项是接口计数器,即表示索引表的容量。若是没有实现接口,计数器值为0,后面接口索引表就不占用任何字节。

图12 测试类类索引、父类索引、实现的接口

分析图12,能够看出图11后access_flags(0x21)后就是类索引、父类索引、类实现的接口信息。从图12能够分析出,this_class(当前类)指向常量池中第一个常量,super_class(父类)指向常量池中第三项,而interfaces_count(实现的接口数)为0,即这个类没有实现任何接口。咱们再来看常量池图,图13。

图13 常量池图

刚才在常量池的部分已经讲过常量池的第一个常量是CONSTANT_Class_info表明类或接口的符号引用,第一项常量又指向第二项常量CONSTANT_Utf8_info,即当前类的全限定名,因此这个类的this_class就是"org/fenixsoft/clazz/TestClass",而由图12看出super_class指向常量池中的第三项常量,从图13得出常量池第三项常量(CONSTANT_Class_info)又指向常量池中第四项常量,第四项常量tag为0x01,即CONSTANT_Class_info型,表明了super_class(父类)的全限定名,即"java/lang/Object",这个TestClass类没有继承任何类,因此父类为Object,图中也标记了this_class和super_class表明的字符串。

五,字段表集合

接下来就是字段表集合。
字段表(field_info):用于描述接口或者类中声明的变量。
你们能够想一下Java描述字段能够包含哪些信息?
包括信息有:做用域(public、private、protected)、是否static、是否final、是否volatile、是否transient、字段数据类型(基本类型、对象、数组)、字段名称。

图14 字段表结构

图15 字段访问标志(access_flags)

name_index和descriptor_index都是对常量池的引用。 name_index:字段的简单名称。例如:方法inc()和m字段,简单名称就是:inc和m。 descriptor_index:字段和方法的描述符,用来描述字段的数据类型、方法参数列表(包括数量、类型以及顺序)和返回值,见图16。

图16 描述标志符的含义

六,方法表集合

理解了上面的字段表,方法表的结构和字段表相似。

图17 方法表结构

图18 方法访问标志(access_flags)

class文件结构就先说到这里,若有兴趣的同窗能够参考《深刻理解Java虚拟机》和《Java虚拟机规范》。

看完此篇之后更容易理解:类的加载机制,若是有兴趣的同窗能够继续阅读。

欢迎若有问题欢迎留言。

相关文章
相关标签/搜索