背景
在日常开发过程中,我们有时候需要查看 <math xmlns="http://www.w3.org/1998/Math/MathML"> class \text{class} </math>class 文件的内容。如果我们对 <math xmlns="http://www.w3.org/1998/Math/MathML"> class \text{class} </math>class 文件的结构有基本的了解,那么就会事半功倍。由于这个话题很大,而且我自己的水平也有限,所以每次只写一个很小的主题。本文的主题是理解 <math xmlns="http://www.w3.org/1998/Math/MathML"> class \text{class} </math>class 文件中字段的 <math xmlns="http://www.w3.org/1998/Math/MathML"> descriptor \text{descriptor} </math>descriptor。
正文
示例代码
请将以下代码保存为 <math xmlns="http://www.w3.org/1998/Math/MathML"> Main.java \text{Main.java} </math>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;
}
编译
使用以下命令可以编译 <math xmlns="http://www.w3.org/1998/Math/MathML"> Main.java \text{Main.java} </math>Main.java
bash
javac Main.java
编译之后,执行 tree . 会看到当前目录下多了一个 <math xmlns="http://www.w3.org/1998/Math/MathML"> Main.class \text{Main.class} </math>Main.class 文件 ⬇️
text
.
├── Main.class
└── Main.java
1 directory, 2 files
查看 <math xmlns="http://www.w3.org/1998/Math/MathML"> class \text{class} </math>class 文件的内容
使用以下命令可以查看 <math xmlns="http://www.w3.org/1998/Math/MathML"> Main.class \text{Main.class} </math>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();
...
}
...
观察后,会发现 <math xmlns="http://www.w3.org/1998/Math/MathML"> Main.java \text{Main.java} </math>Main.java 中的 <math xmlns="http://www.w3.org/1998/Math/MathML"> 20 20 </math>20 个字段在上面的结果中都出现了。每个字段都有对应的 <math xmlns="http://www.w3.org/1998/Math/MathML"> descriptor \text{descriptor} </math>descriptor 和 <math xmlns="http://www.w3.org/1998/Math/MathML"> flags \text{flags} </math>flags,有的字段还有 <math xmlns="http://www.w3.org/1998/Math/MathML"> Signature \text{Signature} </math>Signature。本文只关心 <math xmlns="http://www.w3.org/1998/Math/MathML"> descriptor \text{descriptor} </math>descriptor 部分。
理解 <math xmlns="http://www.w3.org/1998/Math/MathML"> descriptor \text{descriptor} </math>descriptor
<math xmlns="http://www.w3.org/1998/Math/MathML"> descriptor \text{descriptor} </math>descriptor,字面意思是描述符。我觉得可以简单理解成字段的类型信息。在 The Java® Virtual Machine Specification 中的 4.3.2. Field Descriptors 小节 有关于字段的 <math xmlns="http://www.w3.org/1998/Math/MathML"> descriptor \text{descriptor} </math>descriptor 的描述

从中可以看到
<math xmlns="http://www.w3.org/1998/Math/MathML"> FieldDescriptor \text{FieldDescriptor} </math>FieldDescriptor 中的内容是 <math xmlns="http://www.w3.org/1998/Math/MathML"> FieldType \text{FieldType} </math>FieldType。
<math xmlns="http://www.w3.org/1998/Math/MathML"> FieldType \text{FieldType} </math>FieldType 中是以下三种内容之一
- <math xmlns="http://www.w3.org/1998/Math/MathML"> BaseType \text{BaseType} </math>BaseType
- <math xmlns="http://www.w3.org/1998/Math/MathML"> ClassType \text{ClassType} </math>ClassType
- <math xmlns="http://www.w3.org/1998/Math/MathML"> ArrayType \text{ArrayType} </math>ArrayType
1. <math xmlns="http://www.w3.org/1998/Math/MathML"> BaseType \text{BaseType} </math>BaseType 分类
<math xmlns="http://www.w3.org/1998/Math/MathML"> BaseType \text{BaseType} </math>BaseType 中一共有 <math xmlns="http://www.w3.org/1998/Math/MathML"> 8 \text{8} </math>8 种情况,对应 <math xmlns="http://www.w3.org/1998/Math/MathML"> 8 \text{8} </math>8 种基本类型。 4.3.2. Field Descriptors 小节 中提供了如下的表格 (表格的名称是 Table 4.3-A. Interpretation of field descriptors ),表格中的 <math xmlns="http://www.w3.org/1998/Math/MathML"> B, C, D, F, I, J, S, Z \text{B, C, D, F, I, J, S, Z} </math>B, C, D, F, I, J, S, Z 对应 <math xmlns="http://www.w3.org/1998/Math/MathML"> BaseType \text{BaseType} </math>BaseType 分类下的 <math xmlns="http://www.w3.org/1998/Math/MathML"> 8 \text{8} </math>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 |
我们看一下 <math xmlns="http://www.w3.org/1998/Math/MathML"> Main.java \text{Main.java} </math>Main.java 中属于 <math xmlns="http://www.w3.org/1998/Math/MathML"> BaseType \text{BaseType} </math>BaseType 分类的字段在 <math xmlns="http://www.w3.org/1998/Math/MathML"> Main.class \text{Main.class} </math>Main.class 文件中的 <math xmlns="http://www.w3.org/1998/Math/MathML"> descriptor \text{descriptor} </math>descriptor ⬇️

2. <math xmlns="http://www.w3.org/1998/Math/MathML"> ClassType \text{ClassType} </math>ClassType 分类
<math xmlns="http://www.w3.org/1998/Math/MathML"> ClassType \text{ClassType} </math>ClassType 分类下,对应的内容由以下三部分组成
L- <math xmlns="http://www.w3.org/1998/Math/MathML"> ClassName \text{ClassName} </math>ClassName (其值是对应类的全限定类名,但需要将 <math xmlns="http://www.w3.org/1998/Math/MathML"> . \text{.} </math>. 替换为 <math xmlns="http://www.w3.org/1998/Math/MathML"> / \text{/} </math>/,细节可以参考 4.2.1. Binary Class and Interface Names 小节)
;
我们来看几个例子 ⬇️
| 全限定类名 | ➡️ <math xmlns="http://www.w3.org/1998/Math/MathML"> ClassName \text{ClassName} </math>ClassName | ➡️ L <math xmlns="http://www.w3.org/1998/Math/MathML"> ClassName \text{ClassName} </math>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; |
我们看一下 <math xmlns="http://www.w3.org/1998/Math/MathML"> Main.java \text{Main.java} </math>Main.java 中属于 <math xmlns="http://www.w3.org/1998/Math/MathML"> ClassType \text{ClassType} </math>ClassType 分类的字段在 <math xmlns="http://www.w3.org/1998/Math/MathML"> Main.class \text{Main.class} </math>Main.class 文件中的 <math xmlns="http://www.w3.org/1998/Math/MathML"> descriptor \text{descriptor} </math>descriptor ⬇️ 
3. <math xmlns="http://www.w3.org/1998/Math/MathML"> ArrayType \text{ArrayType} </math>ArrayType 分类
<math xmlns="http://www.w3.org/1998/Math/MathML"> ArrayType \text{ArrayType} </math>ArrayType 分类下,对应的内容由以下三部分组成
[- <math xmlns="http://www.w3.org/1998/Math/MathML"> ComponentType \text{ComponentType} </math>ComponentType (它的内容是 $\text{FieldType},所以这里出现了递归的情况)
我们来看几个例子 ⬇️
| <math xmlns="http://www.w3.org/1998/Math/MathML"> java \text{java} </math>java 代码中的类型 | ➡️ <math xmlns="http://www.w3.org/1998/Math/MathML"> ComponentType \text{ComponentType} </math>ComponentType | ➡️ [ <math xmlns="http://www.w3.org/1998/Math/MathML"> ComponentType \text{ComponentType} </math>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; |
我们看一下 <math xmlns="http://www.w3.org/1998/Math/MathML"> Main.java \text{Main.java} </math>Main.java 中属于 <math xmlns="http://www.w3.org/1998/Math/MathML"> ArrayType \text{ArrayType} </math>ArrayType 分类的字段在 <math xmlns="http://www.w3.org/1998/Math/MathML"> Main.class \text{Main.class} </math>Main.class 文件中的 <math xmlns="http://www.w3.org/1998/Math/MathML"> descriptor \text{descriptor} </math>descriptor ⬇️


至此,相信您已经了解了 <math xmlns="http://www.w3.org/1998/Math/MathML"> class \text{class} </math>class 文件中字段的 <math xmlns="http://www.w3.org/1998/Math/MathML"> descriptor \text{descriptor} </math>descriptor 的格式。