background image

0030: 5601 0004 436f 6465 0100 046d 6169 6e01  V...Code...main.
0040: 0016 285b 4c6a 6176 612f 6c61 6e67 2f53  ..([Ljava/lang/S
0050: 7472 696e 673b 2956 0c00 0700 0807 0014  tring;)V........
0060: 0c00 1500 1601 000d 4865 6c6c 6f2c 2057  ........Hello, W
0070: 6f72 6c64 2107 0017 0c00 1800 1901 0005  orld!...........
0080: 4865 6c6c 6f01 0010 6a61 7661 2f6c 616e  Hello...java/lan
0090: 672f 4f62 6a65 6374 0100 106a 6176 612f  g/Object...java/
00a0: 6c61 6e67 2f53 7973 7465 6d01 0003 6f75  lang/System...ou
...

二进制类文件的内幕

 

清单 1 

显示的二进制类表示中首先是 cafe babe”

 

特征符,它标识 Java 二进

   

   

制类格式(并顺便作为一个永久的 ― 但在很大程度上未被认识到的 ― 礼物

 

送给努力工作的 barista

 

,他们本着开发人员所具备的精神构建 Java 平台)。

 

这个特征符恰好是一种验证一个数据块 确实

 

声明成 Java 类格式的一个实例

 

的简单方法。任何 Java 二进制类(甚至是文件系统中没有出现的类)都需要

 

以这四个字节作为开始。

该数据的其余部分不太吸引人。该特征符之后是一对类格式版本号(本例中,

 

是由 1.4.1 javac 

 

生成的次版本 0 

 

和主版本 46   

― 用十六进制表示就是 

0x2e

 

),接着是常量池中项的总数。项总数(本例中,是 26

 

,或 0x001a)

后面是实际的常量池数据。这里放着类定义所用的所有常量。它包括类名和方
法名、特征符以及字符串(您可以在十六进制转储右侧的文本解释中识别它
们),还有各种二进制值。

常量池中各项的长度是可变的,每项的第一个字节标识项的类型以及对它解
码的方式。这里我不详细探究所有这些内容的细节,如果感兴趣,有许多可用

 

的的参考资料,从实际的 JVM 规范开始。关键之处在于常量池包含对该类所
用的其它类和方法的所有引用,还包含了该类及其方法的实际定义。常量池往
往占到二进制类大小的一半或更多,但平均下来可能要少一些。

常量池后面还有几项,它们引用了类本身、其超类以及接口的常量池项。这些
项后面是有关字段和方法的信息,它们本身用复杂结构表示。方法的可执行代

 

码以包含在方法定义中的 代码属性

 

的形式出现。用 JVM 的指令形式表示该代

 

码,一般称为 字节码

 

,这是下一节要讨论的主题之一。

 

在 Java 

 

类格式中, 属性被用于几个已定义的用途,包括已提到的字节码、字

段的常量值、异常处理以及调试信息。但是属性并非只可能用于这些用途。从一
开始,JVM 

 

规范就已经要求 JVM 忽略未知类型的属性。这一要求所带来的灵

活性使得将来可以扩展属性的用法以满足其它用途,例如提供使用用户类的