有时候需要通过阅读java指令来学习java的某些语法的处理过程,因此就需要反编译.class文件。
java文件通过编译后生成.class,如果需要阅读java文件对应的java虚拟机指令,则需要通过javap来反编译.class文件。
比如,有一段代码如下:
public class Main {
public static void main(String[] args) {
if ((Boolean)getBoolean()) {
System.out.println("no exception");
}
}
private static Object getBoolean() {
return null;
}
}
经过编译后,生成Main.class文件,然后通过以下命令:
javap -c -p Main.class
可以得到以下结果:
public class com.kevin.TestJava.Main {
public com.kevin.TestJava.Main();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: invokestatic #16 // Method getBoolean:()Ljava/lang/Object;
3: checkcast #20 // class java/lang/Boolean
6: invokevirtual #22 // Method java/lang/Boolean.booleanValue:()Z
9: ifeq 20
12: getstatic #26 // Field java/lang/System.out:Ljava/io/PrintStream;
15: ldc #32 // String no exception
17: invokevirtual #34 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
20: return
private static java.lang.Object getBoolean();
Code:
0: aconst_null
1: areturn
}
下面拿main方法的汇编码来举例说明如何阅读java汇编码
{
0: invokestatic #16 // Method getBoolean:()Ljava/lang/Object;
}
通过查询java虚拟机指令集,得到关于invokestatic的解释:invokestatic的作用是调用类的静态方法。
文档中对应每个指令都有详细解释,以下解释下各个字段的含义:
Format
invokestatic
indexbyte1
indexbyte2
format表示指令的格式,比如:invokestatic后面两个字节,两个字节的含义可在Description中找到解释:
The unsigned indexbyte1 and indexbyte2 are used to construct an index into the run-time constant pool of the current class, where the value of the index is (indexbyte1 << 8) | indexbyte2.
所以,invokestatic指令后面的两个字节是用来构造常量池的索引(反汇编中的#16
就是计算出来的索引,后面的// Method getBoolean:()Ljava/lang/Object;
就是这个索引所对应的内容,此处是指向的一个Method
,名字是getBoolean:()Ljava/lang/Object
。
Operand Stack
..., [arg1, [arg2 ...]] →
...
Operand Stack表示这个指令如何依赖栈及影响栈
- 第一行,表示这个指令在栈中可能会0个或者多个参数,并且执行完这个指令后,该栈上的值就会被弹出。箭头代表栈的生长方向。
- 第二行,表示这个指令会在栈上新增的内容。
Linking Exceptions
...
Run-time Exceptions
...
这种Exceptions表示该指令在运行时,可能会抛出的异常类型。