深入理解JVM字节码
上QQ阅读APP看书,第一时间看更新

1.3 使用javap查看类文件

前面零星介绍了使用javap来查看类文件,本节将详细介绍javap命令。由前面的小节也可以看出class文件是二进制块,想直接与它打交道比较艰难,好在JDK提供了专门用来分析类文件的工具javap以窥探class文件内部的细节。它的使用方式如下:

        javap [options]  <classes>

不加任何参数运行javap的输出如下所示。

        javap HelloWorld

       Compiled from "HelloWorld.java"
        public class HelloWorld {
          public HelloWorld();
          public static void main(java.lang.String[]);
        }

默认情况下,javap会显示访问权限为public、protected和默认级别的方法。如果想要显示private方法和字段,就需要加上 -p选项。

javap还有一个好用的选项 -s,可以输出类型描述符签名信息,HelloWorld.java所有的方法签名如下所示。

        javap -s HelloWorld

       Compiled from "HelloWorld.java"
        public class HelloWorld {
          public HelloWorld();
            descriptor: ()V

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

加上 -c选项可以对类文件进行反编译,可以显示出方法内的字节码,加上 -c选项以后的输出如下所示。

        javap -c HelloWorld

       public static void main(java.lang.String[]);
        Code:
            0: getstatic      #2        // Field java/lang/System.out:Ljava/io/PrintStream;
            3: ldc             #3        // String hello, world
            5: invokevirtual #4        // Method java/io/PrintStream.println:(Ljava/lang/
    String; )V
            8: return

加上 -v选项可以显示更加详细的内容,比如版本号、类访问权限、常量池相关的信息,是一个非常有用的参数,如下所示。

        javap -v HelloWorld

       public class HelloWorld
          minor version: 0
          major version: 52
          flags: ACC_PUBLIC, ACC_SUPER
        Constant pool:
            #1 = Methodref       #6.#15      // java/lang/Object."<init>":()V
            #2 = Fieldref        #16.#17     // java/lang/System.out:Ljava/io/PrintStream;
            ...

还有一个比较少用的 -l选项,可以用来显示行号表和局部变量表,实测并没有输出局部变量表,只显示了行号表,如下所示。

        javap -l HelloWorld

       public static void main(java.lang.String[]);
        LineNumberTable:
          line 3: 0
          line 4: 8

原因是要想显示局部变量表,需要在javac编译的时候加 -g选项,生成所有的调试信息选项,加上 -g选项编译javac -g HelloWorld.java以后重新执行javap -l命令就可以看到局部变量表(LocalVariableTable)了,如下所示。

        javap -l HelloWorld
        public static void main(java.lang.String[]);
        LineNumberTable:
          line 3: 0
          line 4: 8
        LocalVariableTable:
          Start  Length  Slot  Name    Signature
              0        9      0  args    [Ljava/lang/String;