往期目錄:java
走進 JDK 之 Longbash
走進 JDK 之 Byte數據結構
今天來講說 Boolean
。Boolean
類源碼也很簡單,在閱讀源碼的過程當中思考這麼一個問題,Boolean
類型在內存中是如何表示的?或者說,JVM
是如何看待 Boolean
的?函數
public final class Boolean implements java.io.Serializable,Comparable<Boolean> 複製代碼
Boolean
也是不可變類,事實上全部的基本類型包裝類、String
、BigDecimal
、BigInteger
也都是不可變類。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
類型只有兩個值,true
和 false
。ui
public Boolean(boolean value) {
this.value = value;
}
public Boolean(String s) {
this(parseBoolean(s));
}
複製代碼
仍是熟悉的味道,第一個構造函數直接傳入 boolean
。第二個構造函數調用 parseBoolean()
方法,將 String
轉換爲布爾值。this
public static boolean parseBoolean(String s) {
return ((s != null) && s.equalsIgnoreCase("true"));
}
複製代碼
直接和字符串 true
進行比較。spa
public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}
public static Boolean valueOf(String s) {
return parseBoolean(s) ? TRUE : FALSE;
}
複製代碼
public static String toString(boolean b) {
return b ? "true" : "false";
}
複製代碼
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
的眼裏,並無這麼多的數據類型,對於 boolean
、byte
、short
和 char
,在編譯期都會變成 int
類型,JVM
也僅僅只對 int
提供了最完整的操做碼,其餘類型數據的操做,都是使用相應的 int
類型的操做碼進行操做。那麼 JVM
爲何沒有給每種數據類型都配置完整的操做碼呢?這還得從操做碼的長度提及。
Java 虛擬機操做碼的長度爲一個字節,因此字節碼指令集的操做碼總數不可能超過 256
條。這麼作是爲了儘量得到短小精幹的字節碼,字節碼指令流都是單字節對齊的,數據量小,傳輸效率高。固然,這麼作的代價就是你不可能設計出一套面向全部數據類型都完整的操做碼。若是每一種數據結構都要獲得 Java 虛擬機的字節碼指令的支持的話,那麼指令的數量將遠遠超過 256
種。因此,這也給指令集的設計帶來了麻煩。最終權衡的結果就是,只對有限的類型提供完整的指令。大部分的指令都沒有支持 byte
、 char
和 short
,boolean
則更慘,沒有任何指令支持 boolean 類型。對於這些不支持的指令類型,一概使用 int
的相關指令代替。
Boolean
實際上是被當作 int
值處理的,true
表示 1
,false
表示 0
JVM
爲 int
提供了完善的操做碼,boolean
、byte
、char
、short
在編譯期或運行期都會被轉換爲 int
,使用 int
類型的字節碼指令進行處理
文章同步更新於微信公衆號:
秉心說
, 專一 Java 、 Android 原創知識分享,LeetCode 題解,歡迎關注!
![]