[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 中的

相关推荐
Java小白笔记5 小时前
Java 实现 ZIP 压缩包生成方案
java·开发语言·网络·7-zip
霸道流氓气质6 小时前
Spring AI 多模态开发指南:图片理解与语音合成
java·人工智能·spring
en.en..6 小时前
Linux mmap 内存映射深度解析:基于帧缓冲 /dev/fb0
java·服务器·前端
geovindu6 小时前
CSharp: Command Pattern
开发语言·后端·c#·.net·.netcore·命令模式·行为模式
洋不写bug6 小时前
链表补充练习,双链表的模拟实现
java·数据结构·链表·双链表·底层实现
后台模板学习6 小时前
用 IM 即时聊天项目一次讲清消息去重算法的踩坑与解决方案
java·数据库·spring
秋名RG6 小时前
2026/6/15 系统故障复盘与整改方案
java·架构
今天要早睡_6 小时前
C++ 核心语法速过:命名空间、引用、函数重载与 nullptr 深度解析
android·java·c++
南讯股份Nascent6 小时前
2026年CRM技术选型观察:当“数据打通“成为第一需求,电商CRM的架构价值在哪里
java·大数据·用户运营
心易行者6 小时前
Python自动化测试7步落地法:用python在线运行省掉90%环境配置时间
java·开发语言·人工智能·python·log4j·ai编程