你不知道的Java——19.byte

你不知道的Java——19.byte

下面的程式迴圈遍歷 byte 數值,以查詢某個特定值。這個程式會打印出什麼呢?

public class BigDelight {

public static void main(String[] args) {

for (byte b = Byte.MIN_VALUE; b < Byte.MAX_VALUE; b++) {

if (b == 0x90)

System.out.print("Joy!");

}

}

}

這個迴圈在除了 Byte。MAX_VALUE 之外所有的 byte 數值中進行迭代,以查詢0x90。這個數值適合用 byte 表示,並且不等於 Byte。MAX_VALUE,因此你可能會想這個迴圈在該迭代會找到它一次,並將打印出 Joy!。

但是,所見為虛。

如果 你執行該程式,就會發現它沒有列印任何東西。怎麼回事?

簡單地說,0x90 是一個 int 常量,它超出了 byte 數值的範圍。這與直覺是相悖 的,因為 0x90 是一個兩位的十六進位制字面常量,每一個十六進位制位都佔據 4 個位元的位置,所以整個數值也只佔據 8 個位元,即 1 個 byte。問題在於 byte 是有符號型別。

常量 0x90 是一個正的最高位被置位的 8 位 int 數值。合法的 byte數值是從-128 到+127,但是 int 常量 0x90 等於+144。

拿一個 byte 與一個 int 進行的比較是一個混合型別比較(mixed-type comparison)。如果你把 byte 數值想象為蘋果,把 int 數值想象成為桔子,那麼該程式就是在拿蘋果與桔子比較。請考慮表示式((byte)0x90 == 0x90),儘管外表看起來是成立的,但是它卻等於 false。

為了比較 byte 數值(byte)0x90 和 int 數值 0x90,Java 透過拓寬原始型別轉換將 byte 提升為一個 int[JLS 5。1。2],然後比較這兩個 int 數值。因為 byte 是一個有符號型別,所以這個轉換執行的是符號擴充套件,將負的 byte 數值提升為了在數字上相等的 int 數值。

在本例中,該轉換將(byte)0x90 提升為 int 數值-112,它不等於 int 數值 0x90,即+144。

由於系統總是強制地將一個運算元提升到與另一個運算元相匹配的型別,所以混 合型別比較總是容易把人搞糊塗。這種轉換是不可視的,而且可能不會產生你所期望的結果。有若干種方法可以避免混合型別比較。

我們繼續有關水果的比喻,你可以選擇拿蘋果與蘋果比較,或者是拿桔子與桔子比較。你可以將 int 轉型為byte,之後你就可以拿一個 byte 與另一個 byte 進行比較了:

if (b == (byte)0x90)

System.out.println("Joy!");

或者,你可以用一個遮蔽碼來消除符號擴充套件的影響,從而將 byte 轉型為 int,之後你就可以拿一個 int 與另一個 int 進行比較了:

if ((b & 0xff) == 0x90)

System.out.print("Joy!");

上面的兩個解決方案都可以正常執行,但是避免這類問題的最佳方法還是將常量值移出到迴圈的外面,並將其在一個常量宣告中定義它。下面是我們對此作出的第一個嘗試:

public class BigDelight {

private static final byte TARGET = 0x90;

public static void main(String[] args) {

for (byte b = Byte.MIN_VALUE; b <

Byte.MAX_VALUE; b++) {

if (b == TARGET)

System.out.print("Joy!");

}

}

}

遺憾的是,它根本就通不過編譯。常量宣告有問題,編譯器會告訴你問題所在:0x90 對於 byte 型別來說不是一個有效的數值。

如果你想下面這樣訂正該宣告,那麼程式將執行得非常好:

private static final byte TARGET = (byte)0x90;

總之,要避免混合型別比較,因為它們內在地容易引起混亂(問題 5)。為了幫助實現這個目標,請使用宣告的常量替代“魔幻數字”。你已經瞭解了這確實是一個好主意:它說明了常量的含義,集中了常量的定義,並且根除了重複的定義。

現在你知道它還可以強制你去為每一個常量賦予適合其用途的型別,從而消除了產生混合型別比較的一種根源。

對語言設計的教訓是 byte 數值的符號擴充套件是產生 bug 和混亂的一種常見根源。

而用來抵銷符號擴充套件效果所需的遮蔽機制會使得程式顯得混亂無序,從而降低了程式的可讀性。因此,byte 型別應該是無符號的。還可以考慮為所有的原始型別提供定義字面常量的機制,這可以減少對易於產生錯誤的型別轉換的需求。