[Java] 如何理解 class 文件中方法的 descriptor?

背景

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

下表中列举了 java.lang.Object 中的一些方法和对应的 descriptor\text{descriptor} descriptor。

java.lang.Object 中的哪个方法 java\text{java} java 代码中的类型 对应的 descriptor\text{descriptor} descriptor
hashCode() 方法 没有入参 返回值是 int 类型 ()I
equals(Object)方法 入参是 java.lang.Object 类型 返回值是 boolean 类型 (Ljava/lang/Object;)Z
wait(long, int) 方法 入参分别是 longint 类型 返回值是 void 类型 (JI)V

下表中列举了 java.util.stream.Stream<T> 中的一些方法和对应的 descriptor\text{descriptor} descriptor。

java.util.stream.Stream<T> 中的哪个方法 java\text{java} java 代码中的类型 对应的 descriptor\text{descriptor} descriptor
of(T) 方法 入参是 T 类型 返回值是 java.util.stream.Stream<T> 类型 (Ljava/lang/Object;)Ljava/util/stream/Stream;
of(T...) 方法 入参是 T... 类型(可以视为 T 的数组) 返回值是 java.util.stream.Stream<T> 类型 ([Ljava/lang/Object;)Ljava/util/stream/Stream;

读完本文后,您会了解对应的转化遵循怎样的逻辑。

要点

  • 方法的 descriptor\text{descriptor} descriptor 体现了方法的类型信息,但是泛型中的类型信息不会记录在其中(假设返回值是 List<Integer>,那么其中的 Integer 不会记录在 descriptor\text{descriptor} descriptor 里)
  • 方法的 descriptor\text{descriptor} descriptor 建立在字段 descriptor\text{descriptor} descriptor 的基础上
    • 字段的 descriptor\text{descriptor} descriptor 共有三种情况 ⬇️
      • 8\text{8} 8 个基本类型
      • (除了数组外的)引用类型
      • 数组

示例值

方法的入参的类型 方法的返回值的类型 对应的 descriptor\text{descriptor} descriptor
(无) void ()V
int void (I)V
int, int int (II)I
java.lang.Object java.lang.String (Ljava/lang/Object;)Ljava/lang/String;
int[] void ([I)V
int[] int[] ([I)[I
java.lang.String[] java.lang.String[] ([Ljava/lang/String;)[Ljava/lang/String;

正文

理论知识

Java 如何理解 class 文件中字段的 descriptor? 一文中介绍了字段的 descriptor\text{descriptor} descriptor。在此基础上,我们来探索方法的 descriptor\text{descriptor} descriptor。

The Java® Virtual Machine Specification 中的 4.6. Methods 小节详细介绍了 class\text{class} class 文件中 method_info\text{method\_info} method_info 的结构,它的开头是这样的 ⬇️

descriptor\text{descriptor} descriptor,字面意思是描述符,方法的类型信息保存在其中。在 The Java® Virtual Machine Specification 中的 4.3.3. Method Descriptors 小节有关于方法的 descriptor\text{descriptor} descriptor 的描述 ⬇️

可见, MethodDescriptor\text{MethodDescriptor} MethodDescriptor 是由以下内容组成的

FieldType 中的内容共有 33 3 种情况 ⬇️

Java 如何理解 class 文件中字段的 descriptor? 一文中有更多相关内容,这里就不赘述了。

VoidDescriptor 其实就是一个 V\text{V} V 字符。

现在已经有了足够的理论知识,让我们写点代码来实战吧。

用代码来实战

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

java 复制代码
public abstract class Main {
    abstract void method1();

    abstract void method2(int a);

    abstract int method3(int a, int b);

    abstract String method4(Object o);

    abstract void method5(int[] array);

    abstract int[] method6(int[] array);

    abstract String[] method7(String[] strings);
}

编译

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

bash 复制代码
javac Main.java

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

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

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

bash 复制代码
javap -v -p Main

结果如下(我略去了开头几行)

text 复制代码
public abstract class Main
  minor version: 0
  major version: 65
  flags: (0x0421) ACC_PUBLIC, ACC_SUPER, ACC_ABSTRACT
  this_class: #7                          // Main
  super_class: #2                         // java/lang/Object
  interfaces: 0, fields: 0, methods: 8, attributes: 1
Constant pool:
   #1 = Methodref          #2.#3          // java/lang/Object."<init>":()V
   #2 = Class              #4             // java/lang/Object
   #3 = NameAndType        #5:#6          // "<init>":()V
   #4 = Utf8               java/lang/Object
   #5 = Utf8               <init>
   #6 = Utf8               ()V
   #7 = Class              #8             // Main
   #8 = Utf8               Main
   #9 = Utf8               Code
  #10 = Utf8               LineNumberTable
  #11 = Utf8               method1
  #12 = Utf8               method2
  #13 = Utf8               (I)V
  #14 = Utf8               method3
  #15 = Utf8               (II)I
  #16 = Utf8               method4
  #17 = Utf8               (Ljava/lang/Object;)Ljava/lang/String;
  #18 = Utf8               method5
  #19 = Utf8               ([I)V
  #20 = Utf8               method6
  #21 = Utf8               ([I)[I
  #22 = Utf8               method7
  #23 = Utf8               ([Ljava/lang/String;)[Ljava/lang/String;
  #24 = Utf8               SourceFile
  #25 = Utf8               Main.java
{
  public Main();
    descriptor: ()V
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 1: 0

  abstract void method1();
    descriptor: ()V
    flags: (0x0400) ACC_ABSTRACT

  abstract void method2(int);
    descriptor: (I)V
    flags: (0x0400) ACC_ABSTRACT

  abstract int method3(int, int);
    descriptor: (II)I
    flags: (0x0400) ACC_ABSTRACT

  abstract java.lang.String method4(java.lang.Object);
    descriptor: (Ljava/lang/Object;)Ljava/lang/String;
    flags: (0x0400) ACC_ABSTRACT

  abstract void method5(int[]);
    descriptor: ([I)V
    flags: (0x0400) ACC_ABSTRACT

  abstract int[] method6(int[]);
    descriptor: ([I)[I
    flags: (0x0400) ACC_ABSTRACT

  abstract java.lang.String[] method7(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)[Ljava/lang/String;
    flags: (0x0400) ACC_ABSTRACT
}
SourceFile: "Main.java"

从中可以看到 method1\text{method1} method1 ~ method7\text{method7} method7 的 descriptor\text{descriptor} descriptor,结果整理如下 ⬇️

方法的名称 方法的入参的类型 方法的返回值的类型 对应的 descriptor\text{descriptor} descriptor
method1\text{method1} method1 (无) void ()V
method2\text{method2} method2 int void (I)V
method3\text{method3} method3 int, int int (II)I
method4\text{method4} method4 java.lang.Object java.lang.String (Ljava/lang/Object;)Ljava/lang/String;
method5\text{method5} method5 int[] void ([I)V
method6\text{method6} method6 int[] int[] ([I)[I
method7\text{method7} method7 java.lang.String[] java.lang.String[] ([Ljava/lang/String;)[Ljava/lang/String;

参考资料

相关推荐
村口张大爷1 小时前
05 — 分层架构与依赖倒置
后端·架构·系统架构
云烟成雨TD1 小时前
Spring AI Alibaba 1.x 系列【63】AI Agent 长期记忆
java·人工智能·spring
憧憬成为java架构高手的小白1 小时前
苍穹外卖--day09
java·spring boot·百度
学代码的真由酱1 小时前
Java多用户一对一网页聊天室-测试报告
java·开发语言·功能测试·测试
Jasonakeke2 小时前
SpringBoot自动配置原理揭秘
java·spring boot·后端
2301_803538953 小时前
Java读取Word图片的两种实用方法
java·开发语言·word
C+-C资深大佬3 小时前
SSM 框架(Spring + SpringMVC + MyBatis)
java·spring·mybatis
帅次3 小时前
Android 17 开发者实战:核心更新与应用场景落地指南
android·java·ios·android studio·iphone·android jetpack·webview
IT_陈寒3 小时前
Vite热更新失灵?你可能漏了这个配置
前端·人工智能·后端