[Java] 如何理解 class 文件中字段的 descriptor?

背景

在日常开发过程中,我们有时候需要查看 class \text{class} class 文件的内容。如果我们对 class \text{class} class 文件的结构有基本的了解,那么就会事半功倍。由于这个话题很大,而且我自己的水平也有限,所以每次只写一个很小的主题。本文的主题是理解 class \text{class} class 文件中字段的 descriptor \text{descriptor} descriptor。

正文

示例代码

请将以下代码保存为 Main.java \text{Main.java} Main.java

java 复制代码
import java.util.List;
import java.util.Map;

public class Main {
  // base type examples
  byte f1;
  char f2;
  double f3;
  float f4;
  int f5;
  long f6;
  short f7;
  boolean f8;

  // array examples
  int[] f9;
  double[][] f10;
  long[][][][] f11;
  
  // class type examples
  Boolean f12;
  Long f13;
  Object f14;
  List<Integer> f15;
  Map<String, List<Long>> f16;
  List<?> f17;
  Void f18;
  
  // more array examples
  Boolean[] f19;
  Integer[][][] f20;
}

编译

使用以下命令可以编译 Main.java \text{Main.java} Main.java

bash 复制代码
javac Main.java

编译之后,执行 tree . 会看到当前目录下多了一个 Main.class \text{Main.class} Main.class 文件 ⬇️

text 复制代码
.
├── Main.class
└── Main.java

1 directory, 2 files

查看 class \text{class} class 文件的内容

使用以下命令可以查看 Main.class \text{Main.class} Main.class 文件的详细内容

bash 复制代码
javap -v -p Main

完整的结果有点长,我把本文关心的部分复制到的下方 ⬇️ (与本文无关的内容用 ... 表示)

text 复制代码
...
public class Main
   ...
{
  byte f1;
    descriptor: B
    flags: (0x0000)

  char f2;
    descriptor: C
    flags: (0x0000)

  double f3;
    descriptor: D
    flags: (0x0000)

  float f4;
    descriptor: F
    flags: (0x0000)

  int f5;
    descriptor: I
    flags: (0x0000)

  long f6;
    descriptor: J
    flags: (0x0000)

  short f7;
    descriptor: S
    flags: (0x0000)

  boolean f8;
    descriptor: Z
    flags: (0x0000)

  int[] f9;
    descriptor: [I
    flags: (0x0000)

  double[][] f10;
    descriptor: [[D
    flags: (0x0000)

  long[][][][] f11;
    descriptor: [[[[J
    flags: (0x0000)

  java.lang.Boolean f12;
    descriptor: Ljava/lang/Boolean;
    flags: (0x0000)

  java.lang.Long f13;
    descriptor: Ljava/lang/Long;
    flags: (0x0000)

  java.lang.Object f14;
    descriptor: Ljava/lang/Object;
    flags: (0x0000)

  java.util.List<java.lang.Integer> f15;
    descriptor: Ljava/util/List;
    flags: (0x0000)
    Signature: #40                          // Ljava/util/List<Ljava/lang/Integer;>;

  java.util.Map<java.lang.String, java.util.List<java.lang.Long>> f16;
    descriptor: Ljava/util/Map;
    flags: (0x0000)
    Signature: #43                          // Ljava/util/Map<Ljava/lang/String;Ljava/util/List<Ljava/lang/Long;>;>;

  java.util.List<?> f17;
    descriptor: Ljava/util/List;
    flags: (0x0000)
    Signature: #45                          // Ljava/util/List<*>;

  java.lang.Void f18;
    descriptor: Ljava/lang/Void;
    flags: (0x0000)

  java.lang.Boolean[] f19;
    descriptor: [Ljava/lang/Boolean;
    flags: (0x0000)

  java.lang.Integer[][][] f20;
    descriptor: [[[Ljava/lang/Integer;
    flags: (0x0000)

  public Main();
    ...
}
...

观察后,会发现 Main.java \text{Main.java} Main.java 中的 20 20 20 个字段在上面的结果中都出现了。每个字段都有对应的 descriptor \text{descriptor} descriptor 和 flags \text{flags} flags,有的字段还有 Signature \text{Signature} Signature。本文只关心 descriptor \text{descriptor} descriptor 部分。

理解 descriptor \text{descriptor} descriptor

descriptor \text{descriptor} descriptor,字面意思是描述符。我觉得可以简单理解成字段的类型信息。在 The Java® Virtual Machine Specification 中的 4.3.2. Field Descriptors 小节 有关于字段的 descriptor \text{descriptor} descriptor 的描述

从中可以看到

FieldDescriptor \text{FieldDescriptor} FieldDescriptor 中的内容是 FieldType \text{FieldType} FieldType。

FieldType \text{FieldType} FieldType 中是以下三种内容之一

  1. BaseType \text{BaseType} BaseType
  2. ClassType \text{ClassType} ClassType
  3. ArrayType \text{ArrayType} ArrayType

1. BaseType \text{BaseType} BaseType 分类

BaseType \text{BaseType} BaseType 中一共有 8 \text{8} 8 种情况,对应 8 \text{8} 8 种基本类型。 4.3.2. Field Descriptors 小节 中提供了如下的表格 (表格的名称是 Table 4.3-A. Interpretation of field descriptors ),表格中的 B, C, D, F, I, J, S, Z \text{B, C, D, F, I, J, S, Z} B, C, D, F, I, J, S, Z 对应 BaseType \text{BaseType} BaseType 分类下的 8 \text{8} 8 种情况

FieldType term Type
B byte
C char
D double
F float
I int
J long
L ClassName ; Named class or interface type
S short
Z boolean
[ ComponentType Array of given component type

我们看一下 Main.java \text{Main.java} Main.java 中属于 BaseType \text{BaseType} BaseType 分类的字段在 Main.class \text{Main.class} Main.class 文件中的 descriptor \text{descriptor} descriptor ⬇️

2. ClassType \text{ClassType} ClassType 分类

ClassType \text{ClassType} ClassType 分类下,对应的内容由以下三部分组成

我们来看几个例子 ⬇️

全限定类名 ➡️ ClassName \text{ClassName} ClassName ➡️ L ClassName \text{ClassName} ClassName ;
java.lang.Object java/lang/Object Ljava/lang/Object;
java.lang.Boolean java/lang/Boolean Ljava/lang/Boolean;
java.util.List java/util/List Ljava/util/List;

我们看一下 Main.java \text{Main.java} Main.java 中属于 ClassType \text{ClassType} ClassType 分类的字段在 Main.class \text{Main.class} Main.class 文件中的 descriptor \text{descriptor} descriptor ⬇️

3. ArrayType \text{ArrayType} ArrayType 分类

ArrayType \text{ArrayType} ArrayType 分类下,对应的内容由以下三部分组成

  • [
  • ComponentType \text{ComponentType} ComponentType (它的内容是 $\text{FieldType},所以这里出现了递归的情况)

我们来看几个例子 ⬇️

java \text{java} java 代码中的类型 ➡️ ComponentType \text{ComponentType} ComponentType ➡️ [ ComponentType \text{ComponentType} ComponentType
int[] I [I
java.lang.Boolean[] Ljava/lang/Boolean; [Ljava/lang/Boolean;
java.lang.Boolean[][] [Ljava/lang/Boolean; [[Ljava/lang/Boolean;
java.lang.Boolean[][][] [[Ljava/lang/Boolean; [[[Ljava/lang/Boolean;

我们看一下 Main.java \text{Main.java} Main.java 中属于 ArrayType \text{ArrayType} ArrayType 分类的字段在 Main.class \text{Main.class} Main.class 文件中的 descriptor \text{descriptor} descriptor ⬇️

至此,相信您已经了解了 class \text{class} class 文件中字段的 descriptor \text{descriptor} descriptor 的格式。

参考资料

The Java® Virtual Machine Specification 中的

相关推荐
子兮曰21 分钟前
Agency-Agents 深度解析:400+ AI 专家的"梦之队"如何重塑开发工作流
前端·后端·vibecoding
用户8356290780511 小时前
Python 实现 PDF 文件加密与解密方法
后端·python
小满zs1 小时前
Go语言第二章(小无相功)
后端·go
用户8356290780511 小时前
使用 Python 冻结与拆分 Excel 窗格教程
后端·python
karry_k1 小时前
MyBatis批量insert-select踩坑:useGeneratedKeys=true 可能让PostgreSQL返回大量插入结果
java·后端
妙码生花1 小时前
从 PHP 到 AI + Golang,程序员自救转型手记(十九):点选验证码代码逐行目检
前端·后端·go
贰先生1 小时前
Xiuno BBS X版 用户封禁系统
后端
karry_k1 小时前
PostgreSQL 在 MyBatis 中执行正常 SQL 失效:一次 DELETE USING 踩坑记录
java·后端
ServBay2 小时前
不会写代码也能建站?AI 时代,非技术创始人如何从零搭建自己的 Web 项目
后端·mcp
Moladev2 小时前
如何在 Electron 中接入 OpenAI 兼容的大模型 API:Snaptium 的主进程代理实践
后端