[Java] 如何查看 class 文件的内容:第 1 部分 魔数和版本号

84 阅读3分钟

如何查看 class 文件的内容:第 1 部分 魔数和版本号

要点

u1/u2/u4 分别用来表示无符号的 1 byte/2 byte/4 byte

class 文件的开头部分是 ⬇️

名称类型示例值
magic(魔数)u40xCAFEBABE (魔数总是这个值)
minor_version(次版本号)u2十进制的 00
major_version(主版本号)u2十进制的 6969

正文

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 这一章的开头部分还有一些说明 ⬇️

image.png

为了方便阅读,我把上图绿色框里的文字复制到下方了。

This chapter describes the class file format of the Java Virtual Machine. Each class file 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 the class file format.

class file 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 types u1u2, and u4 to 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 magic item supplies the magic number identifying the class file format; it has the value 0xCAFEBABE.

所以 magic(魔数)的值总是 0xCAFEBABE。 我们可以写点代码来验证一下。请将如下代码保存为 Simple.java

public class Simple {
}

用如下命令可以编译 Simple.java

javac Simple.java

编译之后,我们会得到 Simple.class 文件。 用以下命令可以查看 Simple.class 文件每个 byte 的内容。

od -t x1 Simple.class

image.png

在上图中可以看到开始的四个字节的确是 0xCAFEBABE (对十六进制而言,大小写没有区别)。

minor_version(次版本号) 和 major_version(主版本号)

The Java® Virtual Machine Specification 中的 4.1. The ClassFile Structure 小节 中有 minor_version/major_version 的介绍。原文有点长(在下图绿色框的位置),我就不复制过来了

image.png

我们继续使用 od -t x1 Simple.class 命令来查看 ⬇️

image.png

两个红色框里分别是 minor_versionmajor_version

名称类型Simple.class 中的值
minor_versionu20x0
major_versionu20x45(等于十进制的 6969)

也可以用 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"

在上述结果的第二行和第三行里,可以看到 ⬇️

  • 次版本号是十进制的 00
  • 主版本号是十进制的 6969

这个结果和 od 命令展示的内容一致。 到这里,我们就把 class 文件开头的 魔数版本号 介绍完了

参考资料