在開始學習JVM
字節碼以後,遇到了一個有意思的問題,下面這段代碼,會輸出什麼:java
public class Foo { public static void main(String[] args) { boolean flag = true; if (flag) { System.out.print("A"); } if (flag == true) { System.out.print("B"); } } }
這個問題的答案很顯然——會輸出AB
。工具
接下來重點來了,若是將 2 賦值給flag
變量,會輸出什麼呢?若是將flag
賦值爲 3 呢?學習
要知道這兩個問題的答案,咱們得知道在JVM
中boolean
類型的變量是如何表示的,以及在這兩個if
語句中到底進行了怎樣的判斷。spa
在命令行中輸入javap -c Foo
,獲得反編譯的字節碼以下:命令行
Compiled from "Foo.java" public class geektime.part1.Foo { public geektime.part1.Foo(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: iconst_1 1: istore_1 2: iload_1 3: ifeq 14 6: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 9: ldc #3 // String Hello, Java! 11: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 14: iload_1 15: iconst_1 16: if_icmpne 27 19: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 22: ldc #5 // String Hello, JVM! 24: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 27: return }
在字節碼文件的main
方法中,第0~2行完成了將一個int
類型的常量賦值給了第一個變量flag
的操做。因而咱們獲得告終論:code
JVM
中boolean
類型的變量是用數字 0 和 1 來表示的。false
用 0 表示,true
用 1 表示。而後繼續看字節碼文件,接下來的字節碼就是表明第一個判斷語句if (flag)
語句,從3~11行都屬於第一個if
代碼塊,首先是ifeq
。blog
ifeq
助記符的做用是:當棧頂 int 型數值等於0時跳轉。這裏跳轉的意思就是不執行語句塊中的代碼。
此時的棧頂 int 型數值就是剛剛被賦值給 flag 的1,因此在這裏ifeq 14
的意思就是當 flag 等於 0 的時候跳轉到14行,因爲第14已經不屬於 if 語句的範圍了,因此這裏的跳轉是不執行 if 語句的意思。也就是說,if(flag)
中是判斷 flag 的值,當 flag 值不等於0的時候才執行 if 中的語句。ci
接下來繼續看第二個判斷語句if (flag == true)
語句,是在字節碼的14~24行。前面兩行是將一個int
類型的數值1和flag
變量推送到棧頂,能夠理解爲把接下來將要進行比較的true
放入將要進行比較的一個「容器」,而後是if_icmpne
助記符。get
if_icmpne
助記符的做用是:比較棧頂兩int型數值大小,當結果不相等時跳轉。
如今棧頂兩int
型的數值是剛剛推送的true
也就是1,以及flag
變量,因此if_icmpne
助記符是比較這兩個數值,若是他們相等,執行if
語句的內容。也就是說,if(flag == true)
中是進行flag
和true
的判斷,當它們相等時執行if
中的語句。編譯器
關於上述的示例代碼,編譯器或許會在第二個if
語句提醒你能夠Simplify
,當你執行簡化之後你會發現它將if (flag == true)
變成了if (flag)
,以下圖所示。
在沒有Simplify
以前,這兩個 if 語句若是要執行,第一個的條件是 flag 不等0,第二個的條件是 flag 等於1。這是兩個徹底不一樣的比較嘛!可是爲何在編譯器中能夠畫上等號呢?
當咱們已經瞭解過boolean
類型的變量在虛擬機中的表現形式以後,答案顯而易見。首先這裏的flag
是一個boolean
類型的變量,它只有true
和false
兩種值,即在虛擬機中只有0和1兩種值。因此對於一個boolean
類型的變量,不等0就表明了它必定等1,因此編譯器纔會發出能夠簡化的提示。
咱們知道在正常狀況下編譯器不會接受將2這一個數字賦值給boolean
類型變量的這麼一個操做,可是咱們能夠經過一些其餘的工具(如asmtools
)來實現這個操做。
flag
的值賦爲2。在字節碼中就是將iconst_2
賦值給flag
時,輸出爲空。flag
的值賦爲3。在字節碼中就是將iconst_3
賦值給flag
時,輸出爲AB
。flag
的值賦爲4,輸出爲空;將flag
的值賦爲5,輸出爲AB
......若是咱們將這些整數都轉化爲二進制,即2=0010,3=0011,4=0100,5=0101。
當二進制末尾爲0時,無輸出,當二進制末尾爲1時,輸出AB
。
由此咱們能夠得出結論:若是將其餘整數類型的值賦值給一個boolean
類型的變量,虛擬機會取此整數值二進制的最後一位。
boolean
類型的變量在虛擬機中有兩種表現形式:true
用1表示,false
用0表示。if(boolean)
的條件判斷時,實際進行的判斷是該布爾值是否不等於0,當該布爾值不等於0時執行if
語句中的內容。if(boolean==true)
的條件判斷時,實際進行的判斷時該布爾值是否等於1,當該布爾值等於1時執行if
語句中的內容。if(boolean==true)
能夠簡化爲if(boolean)
的緣由是在虛擬機中布爾值只有兩種值,執行不等於0的判斷與執行等於1的判斷是等價的。int
值賦值給一個布爾類型的變量,虛擬機會取此整數值二進制的最後一位。