走進 JDK 之 Boolean

往期目錄:java

走進 JDK 之 Integer數組

走進 JDK 之 Longbash

走進 JDK 之 Float微信

走進 JDK 之 Byte數據結構

今天來講說 BooleanBoolean 類源碼也很簡單,在閱讀源碼的過程當中思考這麼一個問題,Boolean 類型在內存中是如何表示的?或者說,JVM 是如何看待 Boolean 的?函數

類聲明

public final class Boolean implements java.io.Serializable,Comparable<Boolean> 複製代碼

Boolean 也是不可變類,事實上全部的基本類型包裝類、StringBigDecimalBigInteger 也都是不可變類。post

字段

private final boolean value; 
public static final Boolean TRUE = new Boolean(true); // true
public static final Boolean FALSE = new Boolean(false); // false
public static final Class<Boolean> TYPE = (Class<Boolean>) Class.getPrimitiveClass("boolean");
private static final long serialVersionUID = -3665804199014368530L;
複製代碼

Boolean 類型只有兩個值,truefalseui

構造函數

public Boolean(boolean value) {
    this.value = value;
}

public Boolean(String s) {
    this(parseBoolean(s));
}
複製代碼

仍是熟悉的味道,第一個構造函數直接傳入 boolean。第二個構造函數調用 parseBoolean() 方法,將 String 轉換爲布爾值。this

方法

parseBoolean()

public static boolean parseBoolean(String s) {
    return ((s != null) && s.equalsIgnoreCase("true"));
}
複製代碼

直接和字符串 true 進行比較。spa

valueOf()

public static Boolean valueOf(boolean b) {
    return (b ? TRUE : FALSE);
}

public static Boolean valueOf(String s) {
    return parseBoolean(s) ? TRUE : FALSE;
}
複製代碼

toString()

public static String toString(boolean b) {
    return b ? "true" : "false";
}
複製代碼

hashcode()

public static int hashCode(boolean value) {
    return value ? 1231 : 1237;
}
複製代碼

源代碼都很簡單,沒有什麼好說的。回過頭看看文章開頭的問題:

JVM 是怎麼處理 Boolean 的 ?

源碼中貌似也看不出什麼端倪,咱們得從 Java 虛擬機的角度出發了。先看下面這個例子:

public class BooleanTest {
    public void test() {
        boolean flag = true;
        if (flag)
            System.out.println("This is true.");
    }
}
複製代碼

test 方法顯然會打印字符串 This is true。站在人腦的思惟很容易理解,下面咱們站在 JVM 的思惟來看一下該如何理解。

首先虛擬機確定是不認識這些源代碼的,它認識的只有字節碼,也就是 class 文件。關於 Class 文件的具體格式,能夠看看我以前的一篇文章,Class 文件格式詳解。我在這就直接使用 javap 命令來查看字節碼了。

javac BooleanTest.java
javap -v BooleanTest.class
複製代碼

略去常量池等部份內容,我把 test() 方法的字節碼內容拿過來:

public void test();
    descriptor: ()V     // 方法描述符
    flags: ACC_PUBLIC   // 訪問標誌
    Code:
      stack=2, locals=2, args_size=1
         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 This is true.
        11: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        14: return
複製代碼

下面逐行分析 Code 部分:

stack=2, locals=2, args_size=1
複製代碼

stack 表示操做數棧深度的最大值,這裏是 2 。locals 表示局部變量表所需的存儲空間,這裏須要兩個 slot。還記得什麼是 slot 嗎,slot 是虛擬機爲局部變量分配內存所使用的最小單位。args_size 是參數個數,這裏 test() 方法並無參數,可是每一個方法都有一個參數是指向當前引用自身的。

0: iconst_1     // 將一個 int 常量 1 加載到操做數棧
1: istore_1     // 將數值 1 從操做數棧存儲到局部變量表
2: iload_1      // 將局部變量 1 加載到操做數棧
複製代碼

這三行字節碼其實就是 boolean flag = true;JVM 並無爲 boolean 專門作處理,而是直接當作 int 處理。true 就是 1, false 就是 0

3: ifeq          14
...
14: return
複製代碼

ifeq 是控制轉移指令,這裏的含義是若是操做數棧上的值是 0, 就跳轉到 14 處,14 處指令爲 return,則結束方法執行。這裏已經將 1 加載到操做數棧,因此會繼續往下執行,省略號中的字節碼內容就是打印語言 System.out.println("This is true.");,不做過多分析。

根據 Java 虛擬機規範,JVM 並無任何供 boolean 值專用的字節碼指令,Java 源代碼中使用到的布爾值,在編譯以後都使用 int 值來代替。JVM 也支持 boolean 類型數組,其通常通過編譯會被看成 byte 數組進行處理。因此,在字節碼中,你是看不到 boolean 的。

還記得上篇文章 走進 JDK 之 Byte 中提出的一個問題,做爲方法內部局部變量的 byte 在內存中佔幾個字節 ? 結論是:

基本類型做爲方法局部變量是存儲在棧幀上的,除了 long 和 double 佔兩個 Slot,其餘都佔用一個 Slot

JVM 的眼裏,並無這麼多的數據類型,對於 booleanbyteshortchar,在編譯期都會變成 int 類型,JVM 也僅僅只對 int 提供了最完整的操做碼,其餘類型數據的操做,都是使用相應的 int 類型的操做碼進行操做。那麼 JVM 爲何沒有給每種數據類型都配置完整的操做碼呢?這還得從操做碼的長度提及。

Java 虛擬機操做碼的長度爲一個字節,因此字節碼指令集的操做碼總數不可能超過 256 條。這麼作是爲了儘量得到短小精幹的字節碼,字節碼指令流都是單字節對齊的,數據量小,傳輸效率高。固然,這麼作的代價就是你不可能設計出一套面向全部數據類型都完整的操做碼。若是每一種數據結構都要獲得 Java 虛擬機的字節碼指令的支持的話,那麼指令的數量將遠遠超過 256 種。因此,這也給指令集的設計帶來了麻煩。最終權衡的結果就是,只對有限的類型提供完整的指令。大部分的指令都沒有支持 bytecharshortboolean 則更慘,沒有任何指令支持 boolean 類型。對於這些不支持的指令類型,一概使用 int 的相關指令代替。

總結

Boolean 實際上是被當作 int 值處理的,true 表示 1false 表示 0

JVMint 提供了完善的操做碼,booleanbytecharshort 在編譯期或運行期都會被轉換爲 int,使用 int 類型的字節碼指令進行處理

文章同步更新於微信公衆號: 秉心說 , 專一 Java 、 Android 原創知識分享,LeetCode 題解,歡迎關注!

![]

相關文章
相關標籤/搜索