Java虛擬機基本原理(二):Java的基本類型

1、Java引進八個基本類型(byte、short、int、long、float、double、boolean、char)來支持數值計算,主要原因是工程上的考慮,使用基本類型能夠在執行效率以及內存使用兩方面提升軟件性能。

 

Java 的基本類型都有對應的值域和默認值。可以看到,byte、short、int、long、float 以及 double 的值域依次擴大,而且前面的值域被後面的值域所包含。因此,從前面的基本類型轉換至後面的基本類型,無需強制轉換。另外一點值得注意的是,儘管他們的默認值看起來不一樣,但在內存中都是 0。

在這些基本類型中,boolean 和 char 是唯二的無符號類型。在不考慮違反規範的情況下,boolean 類型的取值範圍是 0 或者 1。char 類型的取值範圍則是 [0, 65535]。通常我們可以認定 char 類型的值爲非負數。這種特性十分有用,比如說作爲數組索引等。

2、在 Java 語言規範中,boolean 類型的值只有兩種可能,它們分別用符號“true”和“false”來表示。顯然,這兩個符號是不能被虛擬機直接使用的。

在 Java 虛擬機規範中,boolean 類型則被映射成 int 類型。具體來說,“true”被映射爲整數 1,而“false”被映射爲整數 0。這個編碼規則約束了 Java 字節碼的具體實現。

舉個例子,對於存儲 boolean 數組的字節碼,Java 虛擬機需保證實際存入的值是整數 1 或者 0。

Java 虛擬機規範同時也要求 Java 編譯器遵守這個編碼規則,並且用整數相關的字節碼來實現邏輯運算,以及基於 boolean 類型的條件跳轉。這樣一來,在編譯而成的 class 文件中,除了字段和傳入參數外,基本看不出 boolean 類型的痕跡了。

 

3、Java 的浮點類型採用 IEEE 754 浮點數格式。以 float 爲例,浮點類型通常有兩個 0,+0.0F 以及 -0.0F。

前者在 Java 裏是 0,後者是符號位爲 1、其他位均爲 0 的浮點數,在內存中等同於十六進制整數 0x8000000(即 -0.0F 可通過 Float.intBitsToFloat(0x8000000) 求得)。儘管它們的內存數值不同,但是在 Java 中 +0.0F == -0.0F 會返回真。

正無窮就是任意正浮點數(不包括 +0.0F)除以 +0.0F 得到的值,而負無窮是任意正浮點數除以 -0.0F 得到的值。在 Java 中,正無窮和負無窮是有確切的值,在內存中分別等同於十六進制整數 0x7F800000 和 0xFF800000。

0x7F800001 這個數字對應的浮點數是 NaN(Not-a-Number)。

[0x7F800001, 0x7FFFFFFF] 和 [0xFF800001, 0xFFFFFFFF] 對應的都是 NaN。當然,一般我們計算得出的 NaN,比如說通過 +0.0F/+0.0F,在內存中應爲 0x7FC00000。這個數值,我們稱之爲標準的 NaN,而其他的我們稱之爲不標準的 NaN。

NaN 有一個有趣的特性:除了“!=”始終返回 true 之外,所有其他比較結果都會返回 false。

 

4、 解釋器使用的解釋棧幀

棧幀有兩個主要的組成部分,分別是局部變量區,以及字節碼的操作數棧。這裏的局部變量是廣義的,除了普遍意義下的局部變量之外,它還包含實例方法的“this 指針”以及方法所接收的參數。

在 Java 虛擬機規範中,局部變量區等價於一個數組,並且可以用正整數來索引。除了 long、double 值需要用兩個數組單元來存儲之外,其他基本類型以及引用類型的值均佔用一個數組單元。

也就是說,boolean、byte、char、short 這四種類型,在棧上佔用的空間和 int 是一樣的,和引用類型也是一樣的。因此,在 32 位的 HotSpot 中,這些類型在棧上將佔用 4 個字節;而在 64 位的 HotSpot 中,他們將佔 8 個字節。

當然,這種情況僅存在於局部變量,而並不會出現在存儲於堆中的字段或者數組元素上。對於 byte、char 以及 short 這三種類型的字段或者數組單元,它們在堆上佔用的空間分別爲一字節、兩字節,以及兩字節,也就是說,跟這些類型的值域相吻合。

因此,當我們將一個 int 類型的值,存儲到這些類型的字段或數組時,相當於做了一次隱式的掩碼操作。舉例來說,當我們把 0xFFFFFFFF(-1)存儲到一個聲明爲 char 類型的字段裏時,由於該字段僅佔兩字節,所以高兩位的字節便會被截取掉,最終存入“\uFFFF”。

boolean 字段和 boolean 數組則比較特殊。在 HotSpot 中,boolean 字段佔用一字節,而 boolean 數組則直接用 byte 數組來實現。爲了保證堆中的 boolean 值是合法的,HotSpot 在存儲時顯式地進行掩碼操作,也就是說,只取最後一位的值存入 boolean 字段或數組中。

 

5、Java 虛擬機的算數運算幾乎全部依賴於操作數棧。也就是說,我們需要將堆中的 boolean、byte、char 以及 short 加載到操作數棧上,而後將棧上的值當成 int 類型來運算。

對於 boolean、char 這兩個無符號類型來說,加載伴隨着零擴展。舉個例子,char 的大小爲兩個字節。在加載時 char 的值會被複制到 int 類型的低二字節,而高二字節則會用 0 來填充。

對於 byte、short 這兩個類型來說,加載伴隨着符號擴展。舉個例子,short 的大小爲兩個字節。在加載時 short 的值同樣會被複制到 int 類型的低二字節。如果該 short 值爲非負數,即最高位爲 0,那麼該 int 類型的值的高二字節會用 0 來填充,否則用 1 來填充。

 

6、課後題目: 將下面代碼中 boolValue = true 裏的 true 換爲 2 或者 3,會輸出什麼?

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!");

  }

}

答: 替換爲2無輸出;替換爲3打印HelloJava及HelloJVM;

因爲boolean 保存在靜態域中,指定了其類型爲'Z',當修改爲2時取低位最後一位爲0,當修改爲3時取低位最後一位爲1

則說明boolean的掩碼處理是取低位的最後一位。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章