首页
首页 教程 派聪明
  • 首页
  • 教程
  • 派聪明
  • 登录
登录技术派畅享更多权益

用户名密码登录

其他登录:
icon_GitHubCreated with sketchtool.
绑定星球,畅享VIP服务

微信扫码/长按识别登录

输入验证码
有效期五分钟 👉 手动刷新

登录即同意 用户协议 和 隐私政策

绑定二哥编程星球,畅享 VIP 尊享服务!

戳我了解如何获取星球编号,新窗口打开

添加二哥微信 itwanger 审核更快

记得备注 星球编号
我会根据星球编号进行审核
1
大白话带你认识JVM
更新时间: 2024年01月16日
星球
2
JVM是如何运行Java代码的?
更新时间: 2024年01月16日
星球
3
Java的类加载机制
更新时间: 2024年01月16日
星球
4
Java的类文件结构
更新时间: 2024年01月16日
星球
5
从javap的角度轻松看懂字节码
更新时间: 2024年01月16日
星球
6
栈虚拟机与寄存器虚拟机
更新时间: 2024年01月16日
星球
7
字节码指令详解
更新时间: 2024年01月16日
星球
8
深入理解JVM的栈帧结构
更新时间: 2024年01月16日
星球
9
深入理解JVM的运行时数据区
更新时间: 2024年01月16日
星球
10
深入理解JVM的垃圾回收机制
更新时间: 2024年01月16日
星球
11
深入理解 JVM 的垃圾收集器
更新时间: 2024年01月16日
星球
12
Java 创建的对象到底放在哪?
更新时间: 2024年01月16日
星球
13
深入理解JIT(即时编译)
更新时间: 2024年01月16日
星球
14
JVM 性能监控之命令行篇
更新时间: 2024年01月16日
星球
15
JVM 性能监控之可视化篇
更新时间: 2024年01月16日
星球
16
阿里开源的 Java 诊断神器 Arthas
更新时间: 2024年01月16日
星球
17
内存溢出排查优化实战
更新时间: 2024年01月16日
星球
18
CPU 100% 排查优化实践
更新时间: 2024年01月16日
星球
19
JVM 核心知识点总结
更新时间: 2024年01月16日
星球
20
K个一组翻转链表
更新时间: 2024年02月07日
星球
关注公众号
原创
第四节:Java类文件结构

大家好,我是二哥呀,今天我拿了一把小刀,准备带大家解剖一下 Java 的类文件结构,也就是 .class 文件的内容结构,虽然它实际上是一串连续的二进制,由 0 和 1 组成,但我们仍然可以借助一些工具来看清楚它的真面目。

类文件结构=.class文件的结构=Class文件结构,这三个说法都是一个意思,.class是从文件后缀名的角度来说的,Class是从Java类的角度来说的,类文件结构就是 Class 的中文译名。

---这部分内容前面其实已经讲过,但为了保持这篇内容的完整性,就暂时保留了下来,已经掌握的同学可以略过 start----

计算机的世界里流传着这么一句话,“计算机科学领域的任何问题都可以通过增加一个中间层来解决”。对于 Java 来说,JVM 就是这么一个产物,“Write once, Run anywhere”之所以能实现,靠得就是 JVM,它能在不同的操作系统下运行同一份源代码编译后的 class 文件。

Java 是跨平台的,JVM 作为中间层,自然要针对不同的操作系统提供不同的实现。拿 JDK 11 来说,它的实现就有上图中提到的这么多种(目前最新版本已经是 JDK 21 了)。

通过不同操作系统的 JVM,我们的源代码就可以不用根据不同的操作系统编译成不同的二进制可执行文件了,跨平台的目标也就实现了。

那这个 class 文件到底是什么玩意呢?它是怎么被 JVM 识别的呢?

我们用 IDEA 编写一段简单的 Java 代码,文件名为 Hello.java。

package com.itwanger.jvm;
class Hello {
    public static void main(String[] args) {
        System.out.println("Hello!");
    }
}

点击编译按钮后(也不用主动点,IDEA 会自动编译),IDEA 会帮我们生成一个名为 Hello.class 的文件,在 target/classes 的对应包目录下。直接双击打开后长下面这样子:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.itwanger.jvm;

class Hello {
    Hello() {
    }

    public static void main(String[] args) {
        System.out.println("Hello!");
    }
}

看起来和源代码很像,只是多了一个空的构造方法,对吧?它是 class 文件被 IDEA 自带的反编译工具 Fernflower 反编译后的样子。那真实的 class 文件长什么样子呢?

可以在终端中通过 xxd Hello.class 命令来查看(前面我们已经讲过了,大家可以戳这个链接回看)。

这就是 class 文件的十六进制形式。

---这部分内容前面其实已经讲过,但为了保持这篇内容的完整性,就暂时保留了下来,已经掌握的同学可以略过 end----

类文件的内容通常可以分为下面这几部分,见下图。

01、魔数

回看 class 文件的十六进制形式截图。

第一行中有一串特殊的字符 cafebabe,它就是一个魔数,是 JVM 识别 class 文件的标志,JVM 会在验证阶段检查 class 文件是否以该魔数开头,如果不是则会抛出 ClassFormatError。

魔数 cafebabe 的中文意思显而易见,咖啡宝贝,再加上 Java 的图标本来就是一个热气腾腾的咖啡,可见 Java 与咖啡的渊源有多深。

02、版本号

紧跟着魔数后面的四个字节 0000 0037 分别表示副版本号和主版本号。也就是说,主版本号为 55(0x37 的十进制),也就是 Java 11 对应的版本号,副版本号为 0。

上一个 LTS 版本是 Java 8,对应的主版本号为 52,也就是说 Java 9 是 53,Java 10 是 54,只不过 Java 9 和 Java 10 都是过渡版本,下一个 LTS 版本是 Java 17,预计 2021 年 9 月份推出(从这里大家可以推断出这篇内容的初稿时间,哈哈哈)。

那现在是 2023年12月14日,Java 21 已经发布了。通过上面的方法,大家可以查看一下 Java 21 对应的版本号是多少,这个小作业就留给大家了,动动手,你会发现不一样的世界。

03、常量池

紧跟在版本号之后的是常量池,它包含了类、接口、字段和方法的符号引用,以及字符串字面量和数值常量。这些信息在编译时被创建,并在运行时被Java虚拟机(JVM)使用。

相当于一个资源仓库,主要存放量大类型常量:

  • 字面量(Literals):字面量是不变的数据,主要包括数值(如整数、浮点数)和字符串字面量。例如,一个整数100或一个字符串"Hello World",在源代码中直接赋值,编译后存储在常量池中。
  • 符号引用(Symbolic References):符号引用是对类、接口、字段、方法等的引用,它们不是由字面量值给出的,而是通过符号名称(如类名、方法名)和其他额外信息(如类型、签名)来表示。这些引用在类文件中以一种抽象的方式存在,它们在类加载时被虚拟机解析为具体的内存地址。

(这部分内容我们前面讲过,戳链接回顾一下)

好,接下来,我们通过实际的代码示例来看一下常量池到底是什么。

Java 定义了 boolean、byte、short、char 和 int 等基本数据类型,它们在常量池中都会被当做 int 来处理。我们来通过一段简单的 Java 代码了解下。

public class ConstantTest {
    public final boolean bool = true;
    public final char aChar = 'a';
    public final byte b = 66;
    public final short s = 67;
    public final int i = 68;
}

布尔值 true 的十六进制是 0x01、字符 a 的十六进制是 0x61,字节 66 的十六进制是 0x42,短整型 67 的十六进制是 0x43,整型 68 的十六进制是 0x44。所以编译生成的整型常量在 class 文件中的位置如下图所示。

第一个字节 0x03 表示常量的类型为 CONSTANT_Integer_info,是 JVM 定义的 14 种常量类型之一,对应的还有 CONSTANT_Float_info、CONSTANT_Long_info、CONSTANT_Double_info 等,它们对应的标识分别是 0x04、0x05、0x06。

我用表格来简单表示下:

常量类型标识符描述符
CONSTANT_Integer_info0x03int 类型字面量
CONSTANT_Float_info0x04float 类型字面量
CONSTANT_Long_info0x05long 类型字面量
CONSTANT_Double_info0x06double 类型字面量

对于 int 和 float 来说,它们占 4 个字节;对于 long 和 double 来说,它们占 8 个字节。来个 long 型的最大值观察下。

public class ConstantTest {
    public final long ong = Long.MAX_VALUE;
}

来看一下它在 class 文件中的位置。05 开头,7f ff ff ff ff ff ff ff 结尾,果然占 8 个字节,以前知道 long 型会占 8 个字节,但没有直观的感受,现在有了(😁)。

接下来,我们再来看一段代码。

class Hello {
    public final String s = "hello";
}

“hello”是一个字符串,它的十六进制为 68 65 6c 6c 6f,我们来看一下它在 class 文件中的位置。

已加入二哥编程星球,即刻绑定星球编号解锁🔐

该文档仅「二哥编程星球」的VIP用户可见

二哥的编程星球内容包括:

1. 付费文档: 技术派、MYDB 等项目配套的 120+篇教程查看权限

2. 面试指南: 校招、社招的 40 万+字面试求职攻略

3. 智能助手: 无限期使用派聪明 AI 助手,已对接讯飞星火和 ChatGPT双通道,不用花 1 分钱

4. 专属问答: 向二哥 1v1 发起提问,内容不限于 offer 选择、学习路线、职业规划等

5. 简历修改: 提供简历修改服务,附赠星球 100+优质简历模板可供参考

6. 学习环境: 打造一个沉浸式的学习环境,有一种高考冲刺、大学考研的氛围


二哥的星球

》步骤①:微信扫描上方二维码,点击「加入知识星球」按钮

》步骤②:访问星球置顶帖球友必看: https://t.zsxq.com/11rEo9Pdu,获取项目配套文档的语雀访问地址和密码

已加入星球,绑定星球编号
删除提醒

确定删除《第四节:Java类文件结构》吗

真诚点赞 诚不我欺

回复