javap 命令的常用方式

javap 是 Java 开发工具包(JDK)中的一个命令行工具,用于反编译 Java 类文件并显示其详细信息,包括类的成员(字段、方法)、字节码、常量池以及方法的签名信息。方法的签名(Method Signature)包含了方法的名称、参数类型和返回类型,这在反射编程和理解类的结构时非常有用。

以下是使用 javap 获取 Java 类中方法签名信息的详细指南:

1. 基本用法

编译 Java 文件

首先,确保你已经编译了 Java 文件,生成了对应的 .class 文件。例如,假设有一个 Example.java 文件:

// Example.java
public class Example {
    private int number;
    public String message;

    public Example() {}

    public Example(int num, String msg) {
        this.number = num;
        this.message = msg;
    }

    public int getNumber() {
        return number;
    }

    public void setNumber(int num) {
        this.number = num;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String msg) {
        this.message = msg;
    }

    public static void main(String[] args) {
        System.out.println("Hello, World!");
    }
}

编译该文件:

javac Example.java

这将生成 Example.class 文件。

使用 javap 查看类的基本信息

要查看类的基本信息,包括方法签名,可以使用以下命令:

javap Example

输出示例

Compiled from "Example.java"
public class Example {
  private int number;
  public java.lang.String message;

  public Example();
  public Example(int, java.lang.String);
  public int getNumber();
  public void setNumber(int);
  public java.lang.String getMessage();
  public void setMessage(java.lang.String);
  public static void main(java.lang.String[]);
}

在这个输出中,每个方法后面的括号内列出了参数类型,返回类型则在方法名之前。例如:

  • public int getNumber() 的签名是 (I)I(在字节码中表示),但在 javap 的默认输出中以更易读的形式展示。
  • public void setNumber(int) 的签名是 (I)V

不过,默认的 javap 输出并不直接显示方法的完整签名(包括参数类型和返回类型的内部表示)。要获取更详细的签名信息,需要使用额外的选项。

2. 使用 -s 选项显示方法签名

javap-s 选项可以显示方法的签名,包括参数类型和返回类型的内部名称(使用 JVM 内部类型表示法)。

命令

javap -s Example

输出示例

Compiled from "Example.java"
public class Example {
  private int number;
    descriptor: I

  public java.lang.String message;
    descriptor: Ljava/lang/String;

  public Example();
    descriptor: ()V

  public Example(int, java.lang.String);
    descriptor: (ILjava/lang/String;)V

  public int getNumber();
    descriptor: ()I

  public void setNumber(int);
    descriptor: (I)V

  public java.lang.String getMessage();
    descriptor: ()Ljava/lang/String;

  public void setMessage(java.lang.String);
    descriptor: (Ljava/lang/String;)V

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
}

解释

  • descriptor 字段展示了方法的签名,使用 JVM 的内部类型表示法:
    • I 表示 int
    • Ljava/lang/String; 表示 java.lang.String
    • V 表示 void
    • [Ljava/lang/String; 表示 String[]
    • 参数类型按顺序排列在括号内,返回类型在括号后。

例如:

  • public int getNumber(); 的描述符是 ()I,表示无参数,返回 int
  • public void setNumber(int); 的描述符是 (I)V,表示接受一个 int 参数,返回 void
  • public Example(int, java.lang.String); 的描述符是 (ILjava/lang/String;)V,表示接受一个 int 和一个 String 参数,返回 void

3. 使用 -p 选项显示所有类和成员(包括私有)

默认情况下,javap 不会显示类的私有成员。如果你想查看所有成员(包括私有的),可以使用 -p 选项。

命令

javap -p Example

输出示例

Compiled from "Example.java"
public class Example {
  private int number;
  public java.lang.String message;

  public Example();
  public Example(int, java.lang.String);
  public int getNumber();
  public void setNumber(int);
  public java.lang.String getMessage();
  public void setMessage(java.lang.String);
  public static void main(java.lang.String[]);
}

这与不加 -p 的输出类似,但如果类中有私有成员且未使用 -p,则不会显示这些私有成员。

4. 使用 -v 选项显示详细信息(包括字节码)

-v 选项会显示更详细的信息,包括常量池、方法字节码等。这对于深入理解类的内部结构非常有用。

命令

javap -v Example

输出示例

输出内容较多,这里仅展示部分相关部分:

Classfile /path/to/Example.class
  Last modified ...; size 500 bytes
  MD5 checksum ...
  Compiled from "Example.java"
public class Example
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #6.#17         // java/lang/Object."<init>":()V
   #2 = Fieldref           #5.#18         // Example.number:I
   #3 = Fieldref           #5.#19         // Example.message:Ljava/lang/String;
   #4 = String             #20            // Hello, World!
   #5 = Class              #21            // Example
   #6 = Class              #22            // java/lang/Object
   #7 = Utf8               number
   #8 = Utf8               I
   #9 = Utf8               message
  #10 = Utf8               Ljava/lang/String;
  #11 = Utf8               <init>
  #12 = Utf8               ()V
  #13 = Utf8               Code
  #14 = Utf8               LineNumberTable
  #15 = Utf8               LocalVariableTable
  #16 = Utf8               this
  #17 = NameAndType        #11:#12        // "<init>":()V
  #18 = NameAndType        #7:#8          // number:I
  #19 = NameAndType        #9:#10         // message:Ljava/lang/String;
  #20 = Utf8               Hello, World!
  #21 = Utf8               Example
  #22 = Utf8               java/lang/Object

{
  private int number;
    descriptor: I
    flags: ACC_PRIVATE

  public java.lang.String message;
    descriptor: Ljava/lang/String;
    flags: ACC_PUBLIC

  public Example();
    descriptor: ()V
    flags: 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 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   LExample;

  public Example(int, java.lang.String);
    descriptor: (ILjava/lang/String;)V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=3, args_size=3
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: aload_0
         5: iload_1
         6: putfield      #2                  // Field number:I
         9: aload_0
        10: aload_2
        11: putfield      #3                  // Field message:Ljava/lang/String;
        14: return
      LineNumberTable:
        line 7: 0
        line 8: 4
        line 9: 14
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      15     0  this   LExample;
            0      15     1  num    I
            0      15     2  msg    Ljava/lang/String;

  ... (其他方法类似)
}

解释

  • descriptor 字段在常量池和方法定义中展示了方法的签名。
  • Code 部分,可以看到字节码指令,这对于理解方法的具体实现非常有用。

5. 使用 -s-p 结合查看所有方法的签名

如果你想查看类中所有方法的签名,包括私有方法,可以结合使用 -s-p 选项:

命令

javap -sp Example

注意javap 不支持直接组合 -s-p 选项。要同时显示签名和所有成员,可以分两步进行,或者使用其他工具(如反射 API)。

不过,你可以分别使用 -s-p 来获取所需信息:

  • 查看签名

    javap -s Example
    
  • 查看所有成员(包括私有)

    javap -p Example
    

如果你确实需要在一个命令中同时获取签名和所有成员的信息,可以考虑编写一个脚本或使用其他工具来解析 javap 的输出。

6. 示例总结

假设你有以下类 Calculator.java

// Calculator.java
public class Calculator {
    private int value;

    public Calculator(int initialValue) {
        this.value = initialValue;
    }

    public int getValue() {
        return value;
    }

    public void setValue(int newValue) {
        this.value = newValue;
    }

    public int add(int a, int b) {
        return a + b;
    }

    public static double multiply(double x, double y) {
        return x * y;
    }
}

编译

javac Calculator.java

查看方法签名

javap -s Calculator

输出示例

Compiled from "Calculator.java"
public class Calculator {
  private int value;
    descriptor: I

  public Calculator(int);
    descriptor: (I)V

  public int getValue();
    descriptor: ()I

  public void setValue(int);
    descriptor: (I)V

  public int add(int, int);
    descriptor: (II)I

  public static double multiply(double, double);
    descriptor: (DD)D
}

解释各方法签名

  1. public Calculator(int);

    • 描述符:(I)V
    • 表示接受一个 int 参数,返回 void
  2. public int getValue();

    • 描述符:()I
    • 表示无参数,返回 int
  3. public void setValue(int);

    • 描述符:(I)V
    • 表示接受一个 int 参数,返回 void
  4. public int add(int, int);

    • 描述符:(II)I
    • 表示接受两个 int 参数,返回 int
  5. public static double multiply(double, double);

    • 描述符:(DD)D
    • 表示接受两个 double 参数,返回 double

7. 进一步了解 JVM 类型描述符

JVM 使用特定的符号来表示类型,这在方法签名中非常常见。以下是一些常见的类型描述符:

Java 类型 JVM 描述符
byte B
char C
double D
float F
int I
long J
short S
boolean Z
void V
类类型(如 String L + 完全限定名 + ;
数组类型 [ + 基本类型或类类型

示例

  • String 的描述符:Ljava/lang/String;
  • int[] 的描述符:[I
  • String[][] 的描述符:[[Ljava/lang/String;

8. 使用 javap 查看其他信息

除了方法签名,javap 还可以显示类的其他详细信息,例如:

查看常量池

javap -v Example | grep -A 1 "Constant pool"

查看字段和方法的访问标志

javap -v Example | grep -E "flags|Field|Method"

查看字节码

javap -c Example

这将显示每个方法的字节码指令。

9. 总结

javap 是一个强大的工具,可以帮助开发者深入了解 Java 类的内部结构,包括方法签名。通过结合不同的选项(如 -s-p-v 等),你可以获取所需的详细信息。以下是常用的命令总结:

  • 查看方法签名

    javap -s 类名
    
  • 查看所有成员(包括私有)

    javap -p 类名
    
  • 查看详细信息(包括常量池和字节码)

    javap -v 类名
    
  • 查看反编译的代码(部分版本支持):

    javap -c 类名
    

通过熟练使用 javap,你可以更好地理解 Java 类的结构,进行调试、学习和开发工作。

如果你有更多关于 javap 或 Java 开发的问题,欢迎继续提问! 😊

posted @ 2025-04-29 21:26  Greg_LYU  阅读(193)  评论(0)    收藏  举报