06、操作數棧管理指令
常見的操作數棧管理指令有 pop、dup 和 swap。
将一個或兩個元素從棧頂彈出,并且直接廢棄,比如 pop,pop2;
複制棧頂的一個或兩個數值并将其重新壓入棧頂,比如 dup,dup2,dup_×1,dup2_×1,dup_×2,dup2_×2;
将棧最頂端的兩個槽中的數值交換位置,比如 swap。
這些指令不需要指明資料類型,因為是按照位置壓入和彈出的。
舉例來說。
public class Dup {
int age;
public int incAndGet() {
return ++age;
}
}
通過 jclasslib 看一下 incAndGet() 方法的位元組碼指令。

aload_0:将 this 入棧。
dup:複制棧頂的 this。
getfield #2:将常量池中下标為 2 的常量加載到棧上,同時将一個 this 出棧。
iconst_1:将常量 1 入棧。
iadd:将棧頂的兩個值相加後出棧,并将結果放回棧上。
dup_x1:複制棧頂的元素,并将其插入 this 下面。
putfield #2: 将棧頂的兩個元素出棧,并将其指派給字段 age。
ireturn:将棧頂的元素出棧傳回。
07、控制轉移指令
控制轉移指令包括:
比較指令,比較棧頂的兩個元素的大小,并将比較結果入棧。
條件跳轉指令,通常和比較指令一塊使用,在條件跳轉指令執行前,一般先用比較指令進行棧頂元素的比較,然後進行條件跳轉。
比較條件轉指令,類似于比較指令和條件跳轉指令的結合體,它将比較和跳轉兩個步驟合二為一。
多條件分支跳轉指令,專為 switch-case 語句設計的。
無條件跳轉指令,目前主要是 goto 指令。
1)比較指令
比較指令有:dcmpg,dcmpl、fcmpg、fcmpl、lcmp,指令的第一個字母代表的含義分别是 double、float、long。注意,沒有 int 類型。
對于 double 和 float 來說,由于 NaN 的存在,有兩個版本的比較指令。拿 float 來說,有 fcmpg 和 fcmpl,差別在于,如果遇到 NaN,fcmpg 會将 1 壓入棧,fcmpl 會将 -1 壓入棧。
public void lcmp(long a, long b) {
if(a > b){}
}
通過 jclasslib 看一下 lcmp() 方法的位元組碼指令。
lcmp 用于兩個 long 型的資料進行比較。
2)條件跳轉指令
這些指令都會接收兩個位元組的操作數,它們的統一含義是,彈出棧頂元素,測試它是否滿足某一條件,滿足的話,跳轉到對應位置。
對于 long、float 和 double 類型的條件分支比較,會先執行比較指令傳回一個整形值到操作數棧中後再執行 int 類型的條件跳轉指令。
對于 boolean、byte、char、short,以及 int,則直接使用條件跳轉指令來完成。
public void fi() {
int a = 0;
if (a == 0) {
a = 10;
} else {
a = 20;
}
}
通過 jclasslib 看一下 fi() 方法的位元組碼指令。
3 ifne 12 (+9) 的意思是,如果棧頂的元素不等于 0,跳轉到第 12(3+9)行 12 bipush 20。
3)比較條件轉指令
字首“if_”後,以字元“i”開頭的指令針對 int 型整數進行操作,以字元“a”開頭的指令表示對象的比較。
public void compare() {
int i = 10;
int j = 20;
System.out.println(i > j);
}
通過 jclasslib 看一下 compare() 方法的位元組碼指令。
11 if_icmple 18 (+7) 的意思是,如果棧頂的兩個 int 類型的數值比較的話,如果前者小于後者時跳轉到第 18 行(11+7)。
4)多條件分支跳轉指令
主要有 tableswitch 和 lookupswitch,前者要求多個條件分支值是連續的,它内部隻存放起始值和終止值,以及若幹個跳轉偏移量,通過給定的操作數 index,可以立即定位到跳轉偏移量位置,是以效率比較高;後者内部存放着各個離散的 case-offset 對,每次執行都要搜尋全部的 case-offset 對,找到比對的 case 值,并根據對應的 offset 計算跳轉位址,是以效率較低。
public void switchTest(int select) {
int num;
switch (select) {
case 1:
num = 10;
break;
case 2:
case 3:
num = 30;
break;
default:
num = 40;
}
}
通過 jclasslib 看一下 switchTest() 方法的位元組碼指令。
case 2 的時候沒有 break,是以 case 2 和 case 3 是連續的,用的是 tableswitch。如果等于 1,跳轉到 28 行;如果等于 2 和 3,跳轉到 34 行,如果是 default,跳轉到 40 行。
5)無條件跳轉指令
goto 指令接收兩個位元組的操作數,共同組成一個帶符号的整數,用于指定指令的偏移量,指令執行的目的就是跳轉到偏移量給定的位置處。
前面的例子裡都出現了 goto 的身影,也很好了解。如果指令的偏移量特别大,超出了兩個位元組的範圍,可以使用指令 goto_w,接收 4 個位元組的操作數。
巨人的肩膀:
https://segmentfault.com/a/1190000037628881除了以上這些指令,還有異常處理指令和同步控制指令,我打算吊一吊大家的胃口,大家可以期待一波~~
(騷操作)
路漫漫其修遠兮,吾将上下而求索
想要走得更遠,Java 位元組碼這塊就必須得硬碰硬地吃透,希望二哥的這些分享可以幫助到大家~
叨逼叨
二哥在 CSDN 上寫了很多 Java 方面的系列文章,有 Java 核心文法、Java 集合架構、Java IO、Java 并發程式設計、Java 虛拟機等,也算是體系完整了。