很多时候,我们都是从代码层面去学习如何编程,却很少去看看一个个 Java 代码背后到底是什么。今天就让我们从一个最简单的 Hello World 开始看一看 Java 的类文件结构。
在开始之前,我们先写一个最简单的入门 Hello World。
public class Demo{
public static void main(String args[]){
System.out.println("Hello World.");
}
}
接着在命令行运行javac Demo.java命令编译这个类,这时会生成一个 Demo.class 文件。
接着我们用纯文本编辑器打开生成的 Demo.class 文件。
cafe babe 0000 0034 001d 0a00 0600 0f09
0010 0011 0800 120a 0013 0014 0700 1507
0016 0100 063c 696e 6974 3e01 0003 2829
5601 0004 436f 6465 0100 0f4c 696e 654e
756d 6265 7254 6162 6c65 0100 046d 6169
6e01 0016 285b 4c6a 6176 612f 6c61 6e67
2f53 7472 696e 673b 2956 0100 0a53 6f75
7263 6546 696c 6501 0009 4465 6d6f 2e6a
6176 610c 0007 0008 0700 170c 0018 0019
0100 0b48 656c 6c6f 2057 6f72 6c64 0700
1a0c 001b 001c 0100 0444 656d 6f01 0010
6a61 7661 2f6c 616e 672f 4f62 6a65 6374
0100 106a 6176 612f 6c61 6e67 2f53 7973
7465 6d01 0003 6f75 7401 0015 4c6a 6176
612f 696f 2f50 7269 6e74 5374 7265 616d
3b01 0013 6a61 7661 2f69 6f2f 5072 696e
7453 7472 6561 6d01 0007 7072 696e 746c
6e01 0015 284c 6a61 7661 2f6c 616e 672f
5374 7269 6e67 3b29 5600 2100 0500 0600
0000 0000 0200 0100 0700 0800 0100 0900
0000 1d00 0100 0100 0000 052a b700 01b1
0000 0001 000a 0000 0006 0001 0000 0001
0009 000b 000c 0001 0009 0000 0025 0002
0001 0000 0009 b200 0212 03b6 0004 b100
0000 0100 0a00 0000 0a00 0200 0000 0300
0800 0400 0100 0d00 0000 0200 0e
可以看到我们简单的 5 行代码到最后就浓缩成了上面那一长串数字字母组成的十六进制符号。而当我们运行该 Java 类时,控制台能准确地输出「Hello World」,所以们可以断定这一长串的符号必定遵守着某种规则,而这个规则其实就是:Java虚拟机规范。
Java虚拟机规范Java 虚拟机规范中规定了 Java 虚拟机结构、Class 类文件结构、字节码指令等内容,其中对于软件开发人员来说,类文件结构是有必要了解的一个内容。
Java 虚拟机的类文件结构是一组以 8 位字节为基础的二进制流,各数据项目严格按照顺序紧凑地排列在 Class 文件之中,中间没有添加任何分隔符,这使得整个 Class 文件中存储的内容几乎全都是程序需要的数据,没有空隙存在。
Java 虚拟机说完了 Java 虚拟机规范,就需要了解一下 Java 虚拟机这个概念。
其实 Java 虚拟机就是一个虚拟的计算机。与真实的计算机一样,Java 虚拟机有自己完善的硬件体系,如处理器、堆栈、寄存器,还有相应的指令集系统。虚拟机与我们的电脑唯一的区别是:虚拟机的处理器、内存堆栈是用软件虚拟出来的,而我们电脑的处理器和内存则是真真实实的。
虽然名字是叫 Java 虚拟机,但 Java 虚拟机与 Java 语言没有直接关系,它只按照 Java 虚拟机规范去读取 Class 文件,并按照规定去解析、执行字节码指令,仅此而已。
如果你够牛逼,你完全可以写一个编译器,将 C 语言代码编译成符合 Java 虚拟机规范的字节码文件,那么 Java 虚拟机也是可以执行的。
准确地说,Java 虚拟机与字节码文件(Class文件)绑定。
Java类文件结构Java 虚拟机规范中定义了许多规范,其中有一部分定义了字节码的结构和规范。Java 虚拟机规范定义了两种数据类型来表示 Class 文件格式,分别是:无符号数和表。
无符号数属于最基本的数据类型,以 u1、u2、u4、u8 六七分别代表 1 个字节、2 个字节、4 个字节、8 个字节的无符号数,无符号数可以用来描述数字、索引引用、数量值或者按照 UTF-8 编码构成的字符串值。例如下表中第一行中的 u4 表示 Class 文件前 4 个字节表示该文件的魔数,第二行的 u2 表示该 Class 文件第 5-6 个字节表示该 JDK 的次版本号。
表是由多个无符号数或者其他表作为数据项构成的复合数据类型,所有表都习惯性地以“_info”结尾,表用于描述有层次关系的复合结构的数据。例如下表第 5 行表示其实一个类型为 cp_info 的表(常量池),这里面存储了该类的所有常量。
整个 Class 文件本质上就是一张表,它由表下表所示的数据项构成。
上面的表其实可以划分为以下七个部分,这七个部分组成了一个完整的 Class 字节码文件:
魔数与Class文件版本
常量池
访问标志
类索引、父类索引、接口索引
字段表集合
方法表集合
属性表集合
魔数与Class文件版本