02 Java 的基本型別
Java 的基本型別
Java 包括了八種基本型別,明細如下:
Java 的基本型別都有對應的值域和預設值。byte,short,int,long,float以及double的值域依次擴大,前面的值域都被後面的值域包括在內。所以,從前面的基本型別轉換成後面的基本型別,無需強制轉換。補充:儘管它們的預設值表示不一樣,但是在記憶體中都是 0.
boolean 和 char 是唯二的無符號型別。boolean 的取值範圍是 0 或者 1,char 型別的取值範圍是 [0,65535]。通常可以認定 char 型別的值為非負數,這種特性十分有用,比如說作為陣列的索引等。
區域性變數也可以儲存超出它們取值範圍的數值,但是這些超出取值範圍的數字會帶來一些麻煩。例如: char 型別的區域性變數實際上有可能是負數。但是在正常使用 Java 編譯器的情況下,生成的位元組碼會遵守 Java 虛擬機器規範對編譯器的約束,因此無需過分簡訊區域性變數會超出他們的取值範圍。
浮點型別中的 float,通常有兩個 0,+0.0F 和 -0.0F。這兩個 0 的表示,在記憶體中的數值是不同的,但是在 Java 中 +0.0F == -0.0F 會返回真。有了這兩者,我們就可以定義浮點數中的正無窮和負無窮。
任意正浮點數 (不包括 +0.0F) 除以 +0.0F 得到的值就是正無窮,記憶體中的值為 0x7F800000
任意負浮點數 (不包括 -0.0F) 除以 -0.0F 得到的值就是負無窮,記憶體中的值為 0xFF800000
[0x7F800001,0x7FFFFFFF] 和 [0xFF800001,0xFFFFFFFF] 對應的數值都是 NaN。一般我們計算得出的 NaN,比如說通過 +0.0F/-0.0F 得出的記憶體值為 0x7FC00000,這個數值我們稱之為標準的 NaN,其他的統稱為不標準的 NaN。
NaN 有一個特性:除了 "!=" 始終返回 true 以為,所有其他的比較結果都會返回 false。例如: "NaN < 1.0F" ,"NaN > 1.0F" 返回 false。對於任意浮點數 f ,無論它是 0 還是 NaN,"f != NaN" 始終返回 true。
Java 基本型別的大小
Java 虛擬機器每呼叫一個 Java 方法的時候,都會建立一個棧幀。為了方便理解,只討論供直譯器使用的解釋棧幀。
這種棧幀包含兩個部分:區域性變數區和位元組碼的運算元棧。這裡的區域性變數指的是廣義的,包含普通意義下的區域性變數,還包含例項方法中的 "this指標" 以及方法接受到的引數。
- 下面總結基本型別的儲存。
在 Java 虛擬機器規範中,區域性變數區等價於一個數組,並且可以用正整數來索引。除了 long 和 double 的值需要兩個陣列單元來儲存之外,其他的基本型別以及引用型別的值均佔用一個數組單元。也就是說,boolean,byte,chart,short 這四種類型在棧上佔用的空間和 int 是一樣的,和引用型別也是一樣的。因此,在 32 位的 HotSpot 中,這些型別在棧上將佔用 4 個位元組,而在 64 位的 HotSpot 中,他們將佔 8 個位元組。
上述情況進存在於區域性變數,並不會出現在儲存於堆中的欄位或者陣列元素上。對於 byte,chart,short 這三種類型的欄位或者陣列單元,它們在堆上佔用的空間分別是 1 位元組,2 位元組,2 位元組,跟它們的值域是相吻合的。
將一個 int 型別的值儲存到這些型別的欄位或者陣列時,相當於做了一次隱式的掩碼操作。舉例: int 型別的值存入宣告為 char 型別的欄位裡時,由於該欄位僅佔兩位元組,所以高位位元組便會被擷取。
boolean 欄位和 boolean 陣列比較特殊。在 HotSpot 中,boolean 欄位佔用一位元組,而 boolean 陣列則直接用 byte 陣列來實現。為了保證堆中 boolean
值的合法性, HotSpot 在儲存時顯示地進行掩碼操作,也就說說只取低位 (最後一位) 的值存入 boolean 欄位或陣列中。
- 下面總結基本型別的載入。
Java 虛擬機器的算數運算幾乎全部依賴於運算元棧。也就是說需要將堆中的 boolean,byte,char 以及 short 載入到運算元棧上,而後將棧上的值當做 int 型別來運算。
對於 boolean 和 char 這兩個無符號型別,載入伴隨著零擴充套件。舉例:char 大小為兩個位元組,載入時 char 的值會複製到 int 型別的低二位元組,而高二位元組則會用 0 來填充。
對於 byte 和 short 這兩個型別來說,載入伴隨著符號的擴充套件。舉例:short 的大小為兩個位元組,載入時同樣會將值複製到 int 型別的低二位元組。如果 short 的值為非負數,即最高位為 0,那麼 int 型別的值的高二位元組會用 0 來填充,否則用 1 來填充。
擴充套件思考
public class Foo {
static boolean boolValue;
public static void main(String[] args) {
boolValue = true; // 將這個 true 替換為 2 或者 3,再看看列印結果
if (boolValue)
System.out.println("Hello, Java!");
if (boolValue == true)
System.out.println("Hello, JVM!");
}
}
分析上述程式碼第一列印結果,以及將 true 替換成 2 或者 3 的時候,列印結果又是什麼。
第一次列印:(條件:boolValue = true)
Hello, Java!
Hello, JVM!
第二次列印:(條件:boolValue = 2)
第三次列印:(條件:boolValue = 3)
Hello, Java!
Hello, JVM!
原因是:boolean 儲存在靜態域中,制定了它的型別。為了保證堆中 boolean
值的合法性, HotSpot 在儲存時顯示地進行掩碼操作,也就說說只取低位 (最後一位) 的值存入 boolean 欄位或陣列中。即:當 boolValue = 2 的時候,取最後一位值為 0 代表 false,當 boolValue = 3 的時候,取最後一位值為 1 代表 true。
問答
Q:為什麼 boolean,byte,char 以及 short 在棧中儲存的時候佔用四個位元組
變長陣列不好控制,所以犧牲空間換取效率。
Q:基本型別在記憶體中預設值都是0,那麼是如何區別是哪種型別資料的呢?
記憶體中不做區分,Java 程式想把它解讀成什麼型別,它就是什麼型別。
Q:double 和 long 佔用兩個陣列單位,64位的機器上,陣列單位是8個位元組,也就是說在解釋棧上面它們佔用了16個位元組?
是的。佔用但並沒有用高八位元組的空間。這個屬於HotSpot的實現細節,偏向了快速訪問而犧牲了耗費空間。
Q:使用基本型別能夠在執行效率以及記憶體使用兩方面提升軟體效能?具體是什麼原理呢?
佔的空間更小,不需要型別轉換。
總結
本文創作靈感來源於 極客時間 鄭雨迪老師的《深入拆解 Java 虛擬機器》課程,通過課後反思以及借鑑各位學友的發言總結,現整理出自己的知識架構,以便日後溫故知新,查漏補缺。