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

相关推荐
xiaohaiAIgeo33 分钟前
【2026年】HG/T 20656-2024化工暖通空调设计规范:新版标准的变化与影响
java·前端·javascript·科普知识
weixin_4316004444 分钟前
NestJS 入门(7):生命周期钩子——构造函数和 `OnModuleInit` 差在哪?
前端·后端·学习·node.js·nest.js
小强库计算机毕业设计1 小时前
基于 Spring Boot + Vue3 的校园管理系统
java·spring boot·后端·项目实战·管理系统·技术分享·校园管理系统实战
霸道流氓气质1 小时前
Java中信号量(Semaphore):从本地到分布式
java·开发语言·分布式
怪奇云呼军1 小时前
G.711、Opus 和重采样会拖慢识别吗?闪电智能VoiceAgent 的音频入口怎么选
java·人工智能·python·算法·云计算·音视频
编程令我快乐1 小时前
maven部分构建参数讲解
java·maven
ZC跨境爬虫1 小时前
LeetCode 27. 移除元素(双指针详解 + Java Python 多解法对比)
java·python·leetcode
canonical_entropy1 小时前
关于《Mission Driver:Loop Engineering 的一种通用参考实现》的补充说明
后端·agent·ai编程
__zRainy__2 小时前
Node系列 · Node基础:Node.js 概述
后端·node.js
kisbad2 小时前
Day 036|OpenAI Agents SDK 快速开始:今天跑通第一个 Agent
java·前端·javascript