详解 Java 虚拟机的字节码 —— Class 文件的结构

本文所有图片均为 1920 × 1080 的高清图片,如果看不清,请点击放大哦!

一   Java 虚拟机

相信你在阅读这篇文章之前,一定或多或少对 Java 有所了解,你肯定经常听到 “ Java 是一门跨平台的语言” 、 “ Java 程序由 Java 虚拟机负责执行” 之类的话。

我们会选择一个开发环境来编写 Java 代码,例如我们使用 IntelliJ IDEA 编写了一个类文件 Demo.java ,点击 “运行” 按钮就可以执行该文件。但是,如果没有 IntelliJ IDEA 之类的开发环境,我们应该如何编译并运行 Java 程序呢?

如图 1.1 所示,我们编写了一个类文件 Demo.java ,当然,你可以使用任何文本编辑软件编写,例如 IntelliJ IDEAEclipseVimNotepad 等都可以, 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 字节码文件。

JVM-1

图 1.1   Java 程序的编译、反编译、打包、运行

当你编译生成了一个 .class 字节码文件,或者打包了一个 .jar 包文件,你就可以在任何安装了 Java 虚拟机的设备上运行。如图 1.2 所示,你可以在包括但不限于 Windows 、 Linux 、 Mac 、Android 等平台上运行 .class.jar 文件。也就是说, Java 虚拟机(JVM,Java Virtual Machine)解决了系统层面的差异,并为 Java 程序提供了一个统一的运行平台。每个操作系统都有一套自己的 API 接口,我们需要根据不同的操作系统,去调用相应的接口。但是 JVM 替我们解决了不同操作系统的接口差异,并提供了统一的 Java 接口。我们在编写 Java 程序时,只要调用这些 Java 接口就行了。所以说 “ Java 是一门跨平台的语言” 。

JVM-2

图 1.2   Java 虚拟机

想要深入了解 JVM 的运行原理,你可以访问 Java SE Specifications ,下载 Oracle 编写的 Java 语言和 Java 虚拟机的官方文档。如图 1.3 所示,是 Java 14 虚拟机的官方文档(The Java Virtual Machine Specification, Java SE 14 Edition)。

JVM-3

图 1.3   JVM 官方文档

二   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
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值