详解 Java 虚拟机的字节码 —— Class 文件的结构
本文所有图片均为 1920 × 1080
的高清图片,如果看不清,请点击放大哦!
一 Java 虚拟机
相信你在阅读这篇文章之前,一定或多或少对 Java 有所了解,你肯定经常听到 “ Java 是一门跨平台的语言” 、 “ Java 程序由 Java 虚拟机负责执行” 之类的话。
我们会选择一个开发环境来编写 Java 代码,例如我们使用 IntelliJ IDEA
编写了一个类文件 Demo.java
,点击 “运行” 按钮就可以执行该文件。但是,如果没有 IntelliJ IDEA
之类的开发环境,我们应该如何编译并运行 Java 程序呢?
如图 1.1 所示,我们编写了一个类文件 Demo.java
,当然,你可以使用任何文本编辑软件编写,例如 IntelliJ IDEA
、 Eclipse
、 Vim
、 Notepad
等都可以, Demo.java
称为源文件。接下来,我们需要使用 Java 编译器编译源文件,使用 javac Demo.java
命令即可编译源文件 Demo.java
,并生成字节码文件 Demo.class
。注意,如果你的源文件里有静态错误, Java 编译器会报错,这时你需要修改源文件再重新编译,直到 Java 编译器检查到源文件没有错误,才能完成编译并生成 .class
字节码文件。
我们可以使用 javap Demo.class
命令来反编译字节码文件。该命令将字节码文件的二进制数据格式化成我们能读懂的文本信息。本文的目的就是解析 .class
字节码文件的结构,在读完本文后,你可以胜任 javap
命令的任务,成为一个 “手动 Java 反编译器” ,嘿嘿!
我们可以使用 java Demo
命令运行,注意这里不需要加后缀名 .class
了,效果和点击开发环境中的 “运行” 按钮是一样的。
如果你有多个 .class
文件,可以使用 jar
命令将这些字节码文件打包成一个 .jar
包文件(JAR,Java Archive File),我们依然可以使用 java
命令来运行 .jar
包文件。 .jar
包文件的好处是有利于网络传输和服务器部署,例如 Minecraft 的服务器程序就打包成了一个 .jar
包文件。你可以将 .jar
包文件理解成一个文件夹,里面包含了很多的 .class
字节码文件。
当你编译生成了一个 .class
字节码文件,或者打包了一个 .jar
包文件,你就可以在任何安装了 Java 虚拟机的设备上运行。如图 1.2 所示,你可以在包括但不限于 Windows 、 Linux 、 Mac 、Android 等平台上运行 .class
或 .jar
文件。也就是说, Java 虚拟机(JVM,Java Virtual Machine)解决了系统层面的差异,并为 Java 程序提供了一个统一的运行平台。每个操作系统都有一套自己的 API 接口,我们需要根据不同的操作系统,去调用相应的接口。但是 JVM 替我们解决了不同操作系统的接口差异,并提供了统一的 Java 接口。我们在编写 Java 程序时,只要调用这些 Java 接口就行了。所以说 “ Java 是一门跨平台的语言” 。
想要深入了解 JVM 的运行原理,你可以访问 Java SE Specifications ,下载 Oracle 编写的 Java 语言和 Java 虚拟机的官方文档。如图 1.3 所示,是 Java 14 虚拟机的官方文档(The Java Virtual Machine Specification, Java SE 14 Edition)。
二 Class 文件
我们以下面的 Java 代码为例,详细阐述 .class
文件的结构,我们将下面的代码写入文件 Demo.java
,并使用 javac Demo.java
命令编译生成字节码文件 Demo.class
。
public class Demo {
private int n = 5;
private int add(int a, int b) {
int c = a + b;
return c;
}
protected int minus(int a, int b) {
int c = a - b;
return c;
}
}
此时,我们可以用 javap -v -p Demo.class
命令反编译 Demo.class
,其中参数 -v -verbose
表示输出附加信息, -p -private
表示输出所有的类和成员,否则默认不输出私有的类和成员。如果不加 -p
参数,则不会输出私有的 add()
方法。输出的内容如下所示,这就是我们最终解析出来的内容。内容有点长,不过不要怕,掌握了方法就很简单了,加油!
Classfile /home/administrator/Projects/IdeaProjects/src/Demo.class
Last modified Dec 15, 2020; size 335 bytes
SHA-256 checksum 0f05381de59579fe48b4a25541383c27a7941dc40d581599ba7c105474c13417
Compiled from "Demo.java"
public class Demo
minor version: 0
major version: 58
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #8 // Demo
super_class: #2 // java/lang/Object
interfaces: 0, fields: 1