「JVM」原始類型 boolean 在 JVM 中的討論

導言

在開始學習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 呢?學習

要知道這兩個問題的答案,咱們得知道在JVMboolean類型的變量是如何表示的,以及在這兩個if語句中到底進行了怎樣的判斷。spa

boolean 類型在 JVM 中是如何表示的

在命令行中輸入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

  • JVMboolean類型的變量是用數字 0 和 1 來表示的。false用 0 表示,true用 1 表示

if (flag) 分析

而後繼續看字節碼文件,接下來的字節碼就是表明第一個判斷語句if (flag) 語句,從3~11行都屬於第一個if代碼塊,首先是ifeqblog

7A29389B-DD73-4675-9DF2-B36FB06BD56E.png

ifeq助記符的做用是:當棧頂 int 型數值等於0時跳轉。這裏跳轉的意思就是不執行語句塊中的代碼。

此時的棧頂 int 型數值就是剛剛被賦值給 flag 的1,因此在這裏ifeq 14的意思就是當 flag 等於 0 的時候跳轉到14行,因爲第14已經不屬於 if 語句的範圍了,因此這裏的跳轉是不執行 if 語句的意思。也就是說,if(flag)中是判斷 flag 的值,當 flag 值不等於0的時候才執行 if 中的語句ci

if (flag == true) 分析

接下來繼續看第二個判斷語句if (flag == true)語句,是在字節碼的14~24行。前面兩行是將一個int類型的數值1和flag變量推送到棧頂,能夠理解爲把接下來將要進行比較的true放入將要進行比較的一個「容器」,而後是if_icmpne助記符。get

7A29389B-DD73-4675-9DF2-B36FB06BD56E.png

if_icmpne助記符的做用是:比較棧頂兩int型數值大小,當結果不相等時跳轉。

如今棧頂兩int型的數值是剛剛推送的true也就是1,以及flag變量,因此if_icmpne助記符是比較這兩個數值,若是他們相等,執行if語句的內容。也就是說,if(flag == true)中是進行flagtrue的判斷,當它們相等時執行if中的語句編譯器

爲何編譯器提示能夠簡化

關於上述的示例代碼,編譯器或許會在第二個if語句提醒你能夠Simplify,當你執行簡化之後你會發現它將if (flag == true)變成了if (flag),以下圖所示。

559A0D7D-7954-4A7E-8D64-EC6A352F7AD1.png

在沒有Simplify以前,這兩個 if 語句若是要執行,第一個的條件是 flag 不等0,第二個的條件是 flag 等於1。這是兩個徹底不一樣的比較嘛!可是爲何在編譯器中能夠畫上等號呢?

timg.jpeg

當咱們已經瞭解過boolean類型的變量在虛擬機中的表現形式以後,答案顯而易見。首先這裏的flag是一個boolean類型的變量,它只有truefalse兩種值,即在虛擬機中只有0和1兩種值。因此對於一個boolean類型的變量,不等0就表明了它必定等1,因此編譯器纔會發出能夠簡化的提示。

若是將 flag 的值賦爲其餘整數型值

咱們知道在正常狀況下編譯器不會接受將2這一個數字賦值給boolean類型變量的這麼一個操做,可是咱們能夠經過一些其餘的工具(如asmtools)來實現這個操做。

  1. flag的值賦爲2。在字節碼中就是將iconst_2賦值給flag時,輸出爲空。
  2. flag的值賦爲3。在字節碼中就是將iconst_3賦值給flag時,輸出爲AB
  3. 若是再多作幾個實驗,將flag的值賦爲4,輸出爲空;將flag的值賦爲5,輸出爲AB......

若是咱們將這些整數都轉化爲二進制,即2=0010,3=0011,4=0100,5=0101。
當二進制末尾爲0時,無輸出,當二進制末尾爲1時,輸出AB

由此咱們能夠得出結論:若是將其餘整數類型的值賦值給一個boolean類型的變量,虛擬機會取此整數值二進制的最後一位。

總結

  1. boolean類型的變量在虛擬機中有兩種表現形式:true用1表示,false用0表示。
  2. 當虛擬機執行if(boolean)的條件判斷時,實際進行的判斷是該布爾值是否不等於0,當該布爾值不等於0時執行if語句中的內容。
  3. 當虛擬機執行if(boolean==true)的條件判斷時,實際進行的判斷時該布爾值是否等於1,當該布爾值等於1時執行if語句中的內容。
  4. 編譯器提示if(boolean==true)能夠簡化爲if(boolean)的緣由是在虛擬機中布爾值只有兩種值,執行不等於0的判斷與執行等於1的判斷是等價的。
  5. 若在虛擬機中將其餘int值賦值給一個布爾類型的變量,虛擬機會取此整數值二進制的最後一位。
相關文章
相關標籤/搜索