java的“一处编译、多处执行”,得益于它的字节码和JVM。字节码就是java编译后的能被JVM读懂的类汇编语言。
1.举个例子:
package jvm;
public class A {
}
上面是一个最简单的类,编译该类:
javac A.java
生成对应的A.class文件,反编译该class:
javap A.class
Compiled from "A.java"
public class jvm.A {
public jvm.A();
}
反编译后,可以看到默认的构造函数jvm.A()。
如果要看到字节码,可以使用如下的命令:
javap -c A.class
Compiled from "A.java"
public class jvm.A {
public jvm.A();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
}
简单解释一下:
aload_0 表示将第一个变量(引用类型)压入堆栈,对于非静态方法,第一个引用变量是this,即将this指针压栈。aload_0的16进制代码为0x2a
invokespecial 表示调用超类构造方法,其十六进制码为0xb7
return 表示当前方法返回void,其十六进制码为0xb1
2.更复杂些的例子
package jvm;
public class A {
public int add(int a, int b) {
return a + b;
}
}
更复杂些的类A,编译再反编译后,可以看到如下的字节码:
javap -c A.class
Compiled from "A.java"
public class jvm.A {
public jvm.A();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public int add(int, int);
Code:
0: iload_1
1: iload_2
2: iadd
3: ireturn
}
默认的构造函数之外,增加了新的方法int add(int, int),它的字节码解释如下:
iload_1 将第二个变量(int类型)压栈
iload_2 将第三个变量(int类型)压栈
iadd 将栈顶两int型数值相加并将结果压入栈顶
ireturn 返回int类型
注意,之所以是iload_1而不是iload_0是因为目前是非静态方法,第一个变量默认为this。如果上述add方法换为静态方法,则反编译后,字节码如下:
javap -c A.class
Compiled from "A.java"
public class jvm.A {
public jvm.A();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static int add(int, int);
Code:
0: iload_0
1: iload_1
2: iadd
3: ireturn
}
后续文章还会接着本文的例子聊下.class文件的结构。