如何查看 class 文件的内容:第 1 部分 魔数和版本号
要点
u1/u2/u4 分别用来表示无符号的 1 byte/2 byte/4 byte。
class 文件的开头部分是 ⬇️
| 名称 | 类型 | 示例值 |
|---|---|---|
magic(魔数) | u4 | 0xCAFEBABE (魔数总是这个值) |
minor_version(次版本号) | u2 | 十进制的 |
major_version(主版本号) | u2 | 十进制的 |
正文
ClassFile 的结构
在 The Java® Virtual Machine Specification 中的 4.1. The ClassFile Structure 小节 里提到了 class 文件的结构 ⬇️
ClassFile {
u4 magic;
u2 minor_version;
u2 major_version;
u2 constant_pool_count;
cp_info constant_pool[constant_pool_count-1];
u2 access_flags;
u2 this_class;
u2 super_class;
u2 interfaces_count;
u2 interfaces[interfaces_count];
u2 fields_count;
field_info fields[fields_count];
u2 methods_count;
method_info methods[methods_count];
u2 attributes_count;
attribute_info attributes[attributes_count];
}
这里的 u2/u4 都是什么,还有一大堆名字都是什么意思?万事开头难,不要心急,我们一个一个来看。
在 Chapter 4. The class File Format 这一章的开头部分还有一些说明 ⬇️
为了方便阅读,我把上图绿色框里的文字复制到下方了。
This chapter describes the
classfile format of the Java Virtual Machine. Eachclassfile contains the definition of a single class, interface, or module. Although a class, interface, or module need not have an external representation literally contained in a file (for instance, because the class is generated by a class loader), we will colloquially refer to any valid representation of a class, interface, or module as being in theclassfile format.
A
classfile consists of a stream of 8-bit bytes. 16-bit and 32-bit quantities are constructed by reading in two and four consecutive 8-bit bytes, respectively. Multibyte data items are always stored in big-endian order, where the high bytes come first. This chapter defines the data typesu1,u2, andu4to represent an unsigned one-, two-, or four-byte quantity, respectively.
其中提到了 u1/u2/u4 分别用来表示无符号的 1 byte/2 byte/4 byte。至于 class 文件中出现的各个名词的含义,现在还用不到,这里先不解释,用到的时候再说。否则容易囫囵吞枣。
magic(魔数)
The Java® Virtual Machine Specification 中的 4.1. The ClassFile Structure 小节 中提到
The
magicitem supplies the magic number identifying theclassfile format; it has the value0xCAFEBABE.
所以 magic(魔数)的值总是 0xCAFEBABE。
我们可以写点代码来验证一下。请将如下代码保存为 Simple.java
public class Simple {
}
用如下命令可以编译 Simple.java
javac Simple.java
编译之后,我们会得到 Simple.class 文件。
用以下命令可以查看 Simple.class 文件每个 byte 的内容。
od -t x1 Simple.class
在上图中可以看到开始的四个字节的确是 0xCAFEBABE (对十六进制而言,大小写没有区别)。
minor_version(次版本号) 和 major_version(主版本号)
The Java® Virtual Machine Specification 中的 4.1. The ClassFile Structure 小节 中有 minor_version/major_version 的介绍。原文有点长(在下图绿色框的位置),我就不复制过来了
我们继续使用 od -t x1 Simple.class 命令来查看 ⬇️
两个红色框里分别是 minor_version 和 major_version
| 名称 | 类型 | 在 Simple.class 中的值 |
|---|---|---|
minor_version | u2 | 0x0 |
major_version | u2 | 0x45(等于十进制的 ) |
也可以用 javap -v -p Simple 命令来查看。对应的结果如下 ⬇️ (开头的 4 行这里略去了)
public class Simple
minor version: 0
major version: 69
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #7 // Simple
super_class: #2 // java/lang/Object
interfaces: 0, fields: 0, methods: 1, attributes: 1
Constant pool:
#1 = Methodref #2.#3 // java/lang/Object."<init>":()V
#2 = Class #4 // java/lang/Object
#3 = NameAndType #5:#6 // "<init>":()V
#4 = Utf8 java/lang/Object
#5 = Utf8 <init>
#6 = Utf8 ()V
#7 = Class #8 // Simple
#8 = Utf8 Simple
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 SourceFile
#12 = Utf8 Simple.java
{
public Simple();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 1: 0
}
SourceFile: "Simple.java"
在上述结果的第二行和第三行里,可以看到 ⬇️
- 次版本号是十进制的
- 主版本号是十进制的
这个结果和 od 命令展示的内容一致。
到这里,我们就把 class 文件开头的 魔数 和 版本号 介绍完了