logo头像
Snippet 博客主题

JDK命令————javap 查看class文件

今天学习一个jdk工具:javap,可以查看编译后的class字节码文件。

用法

用法: javap <options> <classes>
其中, 可能的选项包括:
  -help  --help  -?        输出此用法消息
  -version                 版本信息
  -v  -verbose             输出附加信息
  -l                       输出行号和本地变量表
  -public                  仅显示公共类和成员
  -protected               显示受保护的/公共类和成员
  -package                 显示程序包/受保护的/公共类
                           和成员 (默认)
  -p  -private             显示所有类和成员
  -c                       对代码进行反汇编
  -s                       输出内部类型签名
  -sysinfo                 显示正在处理的类的
                           系统信息 (路径, 大小, 日期, MD5 散列)
  -constants               显示最终常量
  -classpath <path>        指定查找用户类文件的位置
  -cp <path>               指定查找用户类文件的位置
  -bootclasspath <path>    覆盖引导类文件的位置

测试

public class Test{
    public static void main(String[] args) {
        int i = 1;
        String s1 = "a";
        String s2 = "b";
        String s3 = "a"+"b";
        String s4 = "ab";
        String s5 = s1 + s2;
    }
}

编译后执行命令

javap -v Test.class

输出:

Classfile /E:/Test.class
  Last modified 2018-8-27; size 480 bytes
  MD5 checksum 5fe4ad86b37f7755fe4e1b52c67b953b
  Compiled from "Test.java"
public class Test
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #10.#19        // java/lang/Object."<init>":()V
   #2 = String             #20            // a
   #3 = String             #21            // b
   #4 = String             #22            // ab
   #5 = Class              #23            // java/lang/StringBuilder
   #6 = Methodref          #5.#19         // java/lang/StringBuilder."<init>":()V
   #7 = Methodref          #5.#24         // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   #8 = Methodref          #5.#25         // java/lang/StringBuilder.toString:()Ljava/lang/String;
   #9 = Class              #26            // Test
  #10 = Class              #27            // java/lang/Object
  #11 = Utf8               <init>
  #12 = Utf8               ()V
  #13 = Utf8               Code
  #14 = Utf8               LineNumberTable
  #15 = Utf8               main
  #16 = Utf8               ([Ljava/lang/String;)V
  #17 = Utf8               SourceFile
  #18 = Utf8               Test.java
  #19 = NameAndType        #11:#12        // "<init>":()V
  #20 = Utf8               a
  #21 = Utf8               b
  #22 = Utf8               ab
  #23 = Utf8               java/lang/StringBuilder
  #24 = NameAndType        #28:#29        // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #25 = NameAndType        #30:#31        // toString:()Ljava/lang/String;
  #26 = Utf8               Test
  #27 = Utf8               java/lang/Object
  #28 = Utf8               append
  #29 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
  #30 = Utf8               toString
  #31 = Utf8               ()Ljava/lang/String;
{
  public Test();
    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 1: 0

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=7, args_size=1
         0: iconst_1
         1: istore_1
         2: ldc           #2                  // String a
         4: astore_2
         5: ldc           #3                  // String b
         7: astore_3
         8: ldc           #4                  // String ab
        10: astore        4
        12: ldc           #4                  // String ab
        14: astore        5
        16: new           #5                  // class java/lang/StringBuilder
        19: dup
        20: invokespecial #6                  // Method java/lang/StringBuilder."<init>":()V
        23: aload_2
        24: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        27: aload_3
        28: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        31: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        34: astore        6
        36: return
      LineNumberTable:
        line 3: 0
        line 4: 2
        line 5: 5
        line 6: 8
        line 7: 12
        line 8: 16
        line 9: 36
}
SourceFile: "Test.java"

字节码文件结构

  • class文件路径,最后修改时间,文件大小
  • md5校验码。
  • 类全路径
  • 类描述信息
  • 常量池
  • 方法描述信息

下面看下字节执行步骤:

序号 助记符 含义 对应代码
0 iconst_1 把int类型1推至栈顶
1 istore_1 将栈顶int类型数值存入第二个本地变量
2 ldc #2 将String类型常量值从常量池中推送至栈顶 String a
4 astore_2 将栈顶引用类型数值存入第三个本地变量
5 ldc #3 将String类型常量值从常量池中推送至栈顶 String b
7 astore_3 将栈顶引用类型数值存入第四个本地变量
8 ldc #4 将String类型常量值从常量池中推送至栈顶 String ab
10 astore 4 将引用类型数值存入第五个本地变量
12 ldc #4 将引用类型数值存入第五个本地变量 String ab
14 astore 5 将引用类型数值存入第五个本地变量
16 new #5 创建一个对象,并将其引用值压入栈顶 class java/lang/StringBuilder
19 dup 复制栈顶数值并将数值值压入栈顶
20 invokespecial #6 调用超类构造方法,示例初始化方法,私有方法 Method java/lang/StringBuilder.”“:()V
23 aload_2 将第三个引用类型本地变量推送至栈顶
24 invokevirtual #7 调用实例方法 Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
27 aload_3 将第四个引用类型本地变量推送至栈顶
28 invokevirtual #7 调用实例方法 Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
31 invokevirtual #8 调用实例方法 Method java/lang/StringBuilder.toString:()Ljava/lang/String;
34 astore 6 将引用类型数值存入第七个本地变量
36 return 从当前方法返回void

从上面字节码和执行步骤可以看出:
“1”、“a”、“b”、“ab”在类加载的时候已经被放入常量池。
s1、s2、s3、s4都是用的常量池中的引用。
s5是使用了StringBuilder,append了“a”和“b”,然后调用了toString()方法,最后将引用赋给s5。

可见s3==s4。s4!=s5

再看LineNumberTable

LineNumberTable:
        line 3: 0
        line 4: 2
        line 5: 5
        line 6: 8
        line 7: 12
        line 8: 16
        line 9: 36

左边代表代码中的行号,右边代表字节码执行的序号。

参考:

微信打赏

赞赏是不耍流氓的鼓励