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 忽略未知类型的属性。这一要求所带来的灵
活性使得将来可以扩展属性的用法以满足其它用途,例如提供使用用户类的