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.” |
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
左边代表代码中的行号,右边代表字节码执行的序号。
参考:
- jvm字节码表
- 深入理解java虚拟机–周志明
赏
微信打赏

赞赏是不耍流氓的鼓励