javap
是Java内建的一个反编译(反汇编)工具,可以拆解一个或者多个.class
文件(disassembles one or more class files)。
相关参数
1 | -v -verbose 输出附加信息 |
示例与解释
测试类源码
1 | public class JavapMain { |
javap -v JavapMain.class 效果如下
假设 JavapMain.class
文件在 /test/src
目录下。
1 | $ javap -v JavapMain.class |
-v 参数的简单解释
3-6行
显示了 最后修改时间、文件大小、MD5校验码、文件名 信息。
7-10行
显示了 class 文件的版本号、类的访问表示信息。
这里显示主版本号是 51,根据一个简单的公式推断[51 - 44 = 7
(主版本号-44)],说明该类可以被 7 以上的 JDK 执行。
ACC_PUBLIC
说明该类是 pubilc 访问级别的。
ACC_SUPER 标志用于确定该 Class 文件里面的 invokespecial 指令使用的是哪一种执行语义。
目前 Java 虚拟机的编译器都应当设置这个标志。
ACC_SUPER 标记 是为了向后兼容旧编译器编译的 Class 文件而存在的,在 JDK1.0.2 版本以前的编译器产生的 Class 文件中, access_flag 里面没有 ACC_SUPER 标志。
同时,JDK1.0.2 前的 Java 虚拟机遇到 ACC_SUPER 标记会自动忽略它。
—— 《Java 虚拟机规范(1.7)中文版》
除此之外还有 ACC_FINAL
不允许有子类、ACC_INTERFACE
标识定义的是接口而不是类、 ACC_ABSTRACT
不能被实例化、 ACC_SYNTHETIC
标识并非 Java 源码生成的代码
11-54行
为常量池信息,每一个常量都有一个以 # 开头的表示,供后面引用。
56-59行
说明该类中有一个 名为 HELLO
的 public static final String
常量,常量值是 Hello
。
61-73行
显示了该类的 默认无参数构造该方法。
可以看出 默认构造方法,默认会调用父类 Object
的默认默认构造方法。
75-109行
main 方法内的信息
78-95行
为实际执行的 JVM 指令,指令后面的 #n
为引用常量池中的常量。
80-85 基本上就是把字符串从常量池推送至栈顶,然后将其存入局部变量。 需要注意的是84行,源码中是一个常量和一个字符串相加,编译后变成了一个完整的字符串。
86-94 也比较特殊,源码是 一个局部变量和一个字符串相加,实际上变成了 new 一个 StringBuilder,然后进行 append 操作,最后再 toString。
76-108行
为 行号表(LineNumberTable) 和 本地变量表 (LocalVariableTable) 的映射关系。
LocalVariableTable 中显示出有哪些局部变量。
本文涉及到的 Java 虚拟机指令
指令 | 解释 |
---|---|
aload_0 | 将第一个引用类型局部变量推送至栈顶 |
aload_1 | 将第二个引用类型局部变量推送至栈顶 |
astore | 将栈顶引用型数值存入指定局部变量 |
astore_1 | 将栈顶引用型数值存入第二个局部变量 |
dup | 复制栈顶数值并将复制值压入栈顶 |
invokespecial | 调用超类构造方法,实例初始化方法,私有方法 |
invokevirtual | 调用实例方法 |
ldc | 将 int, float 或 String 型常量值从常量池中推送至栈顶 |
new | 创建一个对象,并将其引用值压入栈顶 |
return | 从当前方法返回 void |
详情查看 拓展阅读中 《Java 虚拟机规范》 的 Java 虚拟机指令集相关章节。
PS
javap 的 -v 参数是输出内容最详细的参数了,基本上包含了其它参数输出的内容,这里就不再举例其它参数的使用方法了。
拓展阅读
Java 虚拟机规范 英文 (The Java® Virtual Machine Specification)
Java 反编译工具 jd-gui