class文件的结构(一)
Alzhu.San
本文介绍.class文件的总体组织结构及常量池的相关内容。
一、总体结构
先看一个例子:
//A.java文件
class A{} A.java文件定义了类A,类A没有定义任何字段及方法,它默认的继承至类java.lang.Object。
下面是它编译生成的.class文件,我们的叙述也由此开始。
0 1 2 3 4 5 6 7 8 9 A B C D E F
00000000: CA FE BA BE 00 00 00 32 00 0D 0A 00 03 00 0A 07 ;漱壕...2........
00000010: 00 0B 07 00 0C 01 00 06 3C 69 6E 69 74 3E 01 00 ;..........
00000020: 03 28 29 56 01 00 04 43 6F 64 65 01 00 0F 4C 69 ;.()V...Code...Li
00000030: 6E 65 4E 75 6D 62 65 72 54 61 62 6C 65 01 00 0A ;neNumberTable...
00000040: 53 6F 75 72 63 65 46 69 6C 65 01 00 06 41 2E 6A ;SourceFile...A.j
00000050: 61 76 61 0C 00 04 00 05 01 00 01 41 01 00 10 6A ;ava........A...j
00000060: 61 76 61 2F 6C 61 6E 67 2F 4F 62 6A 65 63 74 00 ;ava/lang/Object.
00000070: 20 00 02 00 03 00 00 00 00 00 01 00 00 00 04 00 ; ...............
00000080: 05 00 01 00 06 00 00 00 1D 00 01 00 01 00 00 00 ;................
00000090: 05 2A B7 00 01 B1 00 00 00 01 00 07 00 00 00 06 ;.*?.?.........
000000A0: 00 01 00 00 00 01 00 01 00 08 00 00 00 02 00 09 ;................
上面的文件用色块分成了10部分,一份.class文件就由这10部分组成。
下面分别介绍这10部分:
- 魔数,用于确定此文件是否是一份.class文件,它占有4字节。值为0xCAFEBABE,很像是一个咖啡的牌子。
- 版本号,.class文件的版本号,它占有4字节,前两字节为次版本号,后两字节为主版本号。以上文件是由JDK1.6生成的,主版本号为0x32(50)。JDK1.5生成的主版本号为0x31(49),如果JVM不支持你的.class文件,可以尝试修改版本号。
- 常量池,参见后文。
- 类修饰符,占有2字节,按位运算如下
0000 0000 0000 0001b 表示public
0000 0000 0000 0010b 表示private
0000 0000 0000 0100b 表示protect
0000 0000 0000 1000b 表示static
0000 0000 0001 0000b 表示final
0000 0000 0010 0000b 表示super
0000 0000 0100 0000b 表示volatile
0000 0000 1000 0000b 表示tramsient
0000 0001 0000 0000b 表示interface
0000 0010 0000 0000b 表示abstract
- this类,指向本类的类名及描述,占有2字节
- super类,指向超类的类名及描述,占有2字节
- 接口列表,本例中没有实现接口,所以只占有2字节以说明其大小
- 字段列表,本例中没有字段,所以只占有2字节以说明其大小
- 方法列表,本例中有一个由编译器提供的初始化方法
- 属性列表,属性列表较为复杂,拟以另文叙述,本例中的属性列表表示有一个SourceFile属性其值为A.java
二、常量池
顾名思意放常量的地方,把文件所需要的常量都放在这里。注意这里所说的常量是文件常量,与java文件中用final定义的量不同,在java文件中定义的是类的字段。文件常量就是这个文件要用到的类名、方法名、字段名、属性名、属性值……
常量池的前两个字节说明后面共有几个常量,如上例这个数字是0x000d有13个常量,实际上这个数值减去1(即12)是所包含常量的最大值。也就是说这个数字本身占用一个序号,其后的常量也因为类型不同可能占用多余一个的序号。如double,和long类型占用两个序号。
从第3字节开始表示不同的常量,每一项都由一字节无符号整数开始表示常量类型。
- 0x01 常量字串
- 0x03 int
- 0x04 float
- 0x05 long
- 0x06 double
- 0x07 class
- 0x08 String
- 0x09 字段引用
- 0x0A 方法引用
- 0x0B 接口引用
- 0x0C 名称及描述符
每种类型所引导的字节数目也不同:
- 常量字串
- 后接一个4字节的整数表示除此以外还引导多少字节(以字节为单位)
- int float
- long double
- 引导8字节表示其字面值,每数过一个这样类型的常量,常量的序号加2
- String
- 引导2字节,代表表示String值的常量字串在常量池中的序号
- 字段引用 方法引用 接口引用
- 引导4字节,前两字节表示应用来源的类(代表保存类名的常量字串在常量池中的序号)
- 后两字节表示引用的字段(方法、接口)的名称及描述符(代表保存名称及描述符的名称及描述符常量在常量池中的序号)
- 名称及描述符
- 引导4字节,前两字节指向保存名称的常量字串,后两字节指向保存描述符的常量字串
上例中的常量池表述了如下信息
- 方法引用 引用的类在常量3中,名称及描述在常量10中
- class 名称在常量11中
- class 名称在常量12中
- 常量字串 长度为6 值为“”
- 常量字串 长度为3 值为“()v”
- 常量字串 长度为4 值为“Code”
- 常量字串 长度为15 值为“LineNumberTable”
- 常量字串 长度为10 值为“SourceFile”
- 常量字串 长度为6 值为“A.java”
- 名称及描述 名称在常量4中,描述符在常量5中
- 常量字串 长度为1 值为“A”
- 常量字串 长度为16 值为“java/lang/Object"
综合以上这段代码引用了
void java.lang.Object.<init>()方法
类名为A
父类类名为
java.lang.Object 源文件被保存在名为a.java的文件中
没有实现接口
没有包含字段
有一个函数
关于本文未涉及的内容将在下一篇文章中陈述。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/15822571/viewspace-466049/
,如需转载,请注明出处,否则将追究法律责任。