字節代碼指令由一個標識該指令的操作碼和固定數目的參數組成:
- 操作碼是一個無符號字節值——即字節代碼名,由助記符號標識。例如,操作碼 0 用助 記符號 NOP 表示,對應於不做任何操作的指令。
- 參數是靜態值,確定了精確的指令行爲。它們緊跟在操作碼之後給出。比如GOTO標記 指令(其操作碼的值爲 167)以一個指明下一條待執行指令的標記作爲參數標記。不要 將指令參數與指令操作數相混淆:參數值是靜態已知的,存儲在編譯後的代碼中,而 操作數值來自操作數棧,只有到運行時才能知道。
字節代碼指令可以分爲兩類:一小組指令,設計用來在局部變量和操作數棧之間傳送值;其 他一些指令僅用於操作數棧:它們從棧中彈出一些值,根據這些值計算一個結果,並將它壓回棧 中。
ILOAD, LLOAD, FLOAD, DLOAD 和 ALOAD 指令讀取一個局部變量,並將它的值壓到操 作數棧中。它們的參數是必須讀取的局部變量的索引 i。ILOAD 用於加載一個 boolean、byte、 char、short 或 int 局部變量。LLOAD、FLOAD 和 DLOAD 分別用於加載 long、float 或 double 值。(LLOAD 和 DLOAD 實際加載兩個槽 i 和 i+1)。最後,ALOAD 用於加載任意非基元值,即對 象和數組引用。與之對應,ISTORE、LSTORE、FSTORE、DSTORE 和 ASTORE 指令從操作數棧 中彈出一個值,並將它存儲在由其索引 i 指定的局部變量中。
可以看到,xLOAD 和 xSTORE 指令被賦入了類型(事實上,下面將要看出,幾乎所有指令 都被賦予了類型)。它用於確保不會執行非法轉換。實際上,將一個值存儲在局部變量中,然後 再以不同類型加載它,是非法的。例如,ISTORE 1 ALOAD 1 序列是非法的——它允許將一個 任意內存位置存儲在局部變量 1 中,並將這個地址轉換爲對象引用!但是,如果向一個局部變 量中存儲一個值,而這個值的類型不同於該局部變量中存儲的當前值,卻是完全合法的。這意味 着一個局部變量的類型,即這個局部變量中所存值的類型可以在方法執行期間發生變化。
上面已經說過,所有其他字節代碼指令都僅對操作數棧有效。它們可以劃分爲以下類別(見 附件 A.1):
棧 這些指令用於處理棧上的值:POP彈出棧頂部的值,DUP壓入頂部棧值的一個副本, SWAP 彈出兩個值,並按逆序壓入它們,等等。
常量 這些指令在操作數棧壓入一個常量值:ACONST_NULL壓入null,ICONST_0壓入 int 值 0,FCONST_0 壓入 0f,DCONST_0 壓入 0d,BIPUSH b 壓入字節值 b,SIPUSH s 壓入 short 值 s,LDC cst 壓入任意 int、float、long、double、String 或 class1 常量 cst,等等。
算術與邏輯 這些指令從操作數棧彈出數值,合併它們,並將結果壓入棧中。它們沒有任何 參數。xADD、xSUB、xMUL、xDIV 和 xREM 對應於+、-、*、/和%運算,其中 x 爲 I、 L、F 或 D 之一。類似地,還有其他對應於<<、>>、>>>、|、&和^運算的指令,用於 處理int和long值。
類型變換 這些指令從棧中彈出一個值,將其轉換爲另一類型,並將結果壓入棧中。它們對 應於 Java 中的類型轉換表達式。I2F, F2D, L2D 等將數值由一種數值類型轉換爲另一種 類型。CHECKCAST t 將一個引用值轉換爲類型 t。
對象 這些指令用於創建對象、鎖定它們、檢測它們的類型,等等。例如,NEWtype指令將 一個 type 類型的新對象壓入棧中(其中 type 是一個內部名)。
字段 這些指令讀或寫一個字段的值。GETFIELD owner name desc 彈出一個對象引用,並 壓和其 name 字段中的值。PUTFIELD owner name desc 彈出一個值和一個對象引用,並 將這個值存儲在它的 name 字段中。在這兩種情況下,該對象都必須是 owner 類型,它 的字段必須爲 desc 類型。GETSTATIC 和 PUTSTATIC 是類似指令,但用於靜態字段。
方法 這些指令調用一個方法或一個構造器。它們彈出值的個數等於其方法參數個數加 1 (用於目標對象),並壓回方法調用的結果。INVOKEVIRTUAL owner name desc 調用在 類 owner 中定義的 name 方法,其方法述符爲 desc。INVOKESTATIC 用於靜態方法, INVOKESPECIAL 用於私有方法和構造器,INVOKEINTERFACE 用於接口中定義的方 法。最後,對於 Java 7 中的類,INVOKEDYNAMIC 用於新動態方法調用機制。
數組 這些指令用於讀寫數組中的值。xALOAD指令彈出一個索引和一個數組,並壓入此索 引處數組元素的值。xASTORE 指令彈出一個值、一個索引和一個數組,並將這個值存 儲在該數組的這一索引處。這裏的 x 可以是 I、L、F、D 或 A,還可以是 B、C 或 S。
跳轉 這些指令無條件地或者在某一條件爲真時跳轉到一條任意指令。它們用於編譯if、 for、do、while、break 和 continue 指令。例如,IFEQ label 從棧中彈出一個 int 值,如果這個值爲 0,則跳轉到由這個 label 指定的指令處(否則,正常執行下一 條指令)。還有許多其他跳轉指令,比如 IFNE 或 IFGE。最後,TABLESWITCH 和
LOOKUPSWITCH 對應於 switch Java 指令。
返回 最後,xRETURN和RETURN指令用於終止一個方法的執行,並將其結果返回給調用 者。RETURN 用於返回 void 的方法,xRETURN 用於其他方法。