前言
众所周知,Java字节码技术造就Java “一次部署,处处运行”的伟大理想,Java使用了一个自定义的中间语言,不基于操作系统的底层接口,而是基于自己的VM虚拟机,把对接系统底层操作交给VM虚拟机来处理,Java的开发者无需关心这些,即使想关心,也可使用Java提供的对应的工具类或者参数配置。那么字节码是什么呢?
概念
维基百科中是这样定义的:Java 字节码(英语:Java bytecode)是Java虚拟机执行的一种指令格式。大多数操作码都是一个字节长,而有些操作需要参数,导致了有一些多字节的操作码。而且并不是所有可能的256个操作码都被使用;其中有51个操作码被保留做将来使用。除此之外,原始Java平台开发商,太阳微系统,额外保留了3个代码永久不使用。
实战
首先,定义一个简单的基础类。如下:
public class SimpleAddDemo {
public static void main(String[] args) {
int a = 28;
int b = 2;
for (int i=0; i<7; ++i) {
if (i % 2 == 0) {
a++;
b++;
}
}
long c = (a + b) * 5;
}
}
以上为一个简单的数字逻辑操作,接下来,使用javac将其进行编译。再使用javap -c进行反编译class文件,得到以下的字节码信息
public class com.jackpan.jvm.basic.SimpleAddDemo {
public com.jackpan.jvm.basic.SimpleAddDemo();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: bipush 28
2: istore_1
3: iconst_2
4: istore_2
5: iconst_0
6: istore_3
7: iload_3
8: bipush 7
10: if_icmpge 31
13: iload_3
14: iconst_2
15: irem
16: ifne 25
19: iinc 1, 1
22: iinc 2, 1
25: iinc 3, 1
28: goto 7
31: iload_1
32: iload_2
33: iadd
34: iconst_5
35: imul
36: i2l
37: lstore_3
38: return
}
只需关注main方法的Code部分,以下对这几个指令进行解释:
bipush 28 当int的值在-128~127中,JVM 采用 bipush 指令将常量压入栈中
istore_1 将栈顶 int 型数值存入第二个本地变量
iconst_2 将int值2压入栈中,当 int 取值 -1~5 时,JVM 采用 iconst 指令将常量压入栈中。
iload_3 将第四个 int 型本地变量推送至栈顶
if_icmpge 30 比较栈顶两 int 型数值的大小,当结果大于或等于 0 时跳转至30行
irem 将栈顶两 int 型数值作取模运算并将结果压入栈顶
ifne 25 当栈顶 int 型数值不等于 0 时跳转25行
iinc 将栈顶 int 型变量增加指定值
goto 7 无条件跳转7行
iadd 将栈顶两 int 型数值相加并将结果压入栈顶
imul 将栈顶两 int 型数值相乘并将结果压入栈顶
i2l 将栈顶 int 型数值强制转成 long 型数值并将结果压入栈顶
return 从当前方法返回 void
总结
以上的13个命令包含了大多数的数值操作,很多命令都是一两个字母的变化而来的,可以根据一个命令扩展对应的多个命令。