小白的消費為何被迫升級?-java資料型別的轉換
背景
小白最近有點煩惱,原因也很簡單,不知道為何?小白的消費不知不覺被迫升級了,請看費用清單:
for (byte b = Byte.MIN_VALUE; b < Byte.MAX_VALUE; b++) { if (b == 0x90) System.out.print("life is Happy!"); }
本來小白預期輸出結果:
life is Happy!
但是什麼都沒有輸出,這是怎麼回事呢?是不是以後的幸福小日子就沒了?
於是小白向柯南請教:
破案
為了比較byte 數值(byte)0x90 和int 數值0x90,Java 通過拓寬原始型別轉換
將byte 提升為一個int[JLS 5.1.2],然後比較這兩個int 數值。0x90為144,但byte指的範圍為-128~127
故沒有打印出預期的值。
究根到底
原來java language Specification中有自動Promotion機制,讓我們瞭解一下數字提升的問題吧
數字提升總體的規則是向上提升,又分為一元數字提升和二元數字提升
一元數字提升
某些運算子將一元數值提升用在了單運算元運算中,其必定能得到一個數字型別的值,
規則如下:
if 運算元是編譯時包裝型別Byte、Short、Character或Integer,那麼它會先拆箱為對應的原始型別,然後拓寬為int型別。
else if 運算元為編譯時包裝型別Long、Float或Double,那麼就直接拆箱為對應的原始型別。
else if 運算元是編譯時拆箱型別byte、short、char或int,那麼就拓寬為int型別。
else 保持原樣。
一元數值提升還用在以下情境的表示式中:
陣列建立表示式的維度
陣列索引表示式的索引
正號運算子(+)的運算元
負號運算子(-)的運算元
按位補運算子(~)的運算元
移位運算子(>>, >>>, << )的每一個運算元。注意移位運算並不會使兩邊的運算元提升到相同型別,如 A << B 中若B為long型別,A並不會被提升到long。
是不是很難理解?
那就舉個例子吧
class Test { public static void main(String[] args) { byte b = 2; int a[] = new int[b]; // dimension expression promotion char c = '\\u0001'; a[c] = 1; // index expression promotion a[0] = -c; // unary - promotion System.out.println("a: " + a[0] + "," + a[1]); b = -1; int i = ~b; // bitwise complement promotion System.out.println("~0x" + Integer.toHexString(b) + "==0x" + Integer.toHexString(i)); i = b << 4L; // shift promotion (left operand) System.out.println("0x" + Integer.toHexString(b) + "<<4L==0x" + Integer.toHexString(i)); } }
輸出結果為:
a: -1,1
~0xffffffff==0x0
0xffffffff<<4L==0xfffffff0
二元數字提升
當二元運算子的運算元皆可轉化為數字型別時,那麼將採用如下二元數值提升規則:
如果任一運算元為引用型別,那麼對其進行自動拆箱。
拓寬型別轉換被應用於以下情況:
if 某一運算元為double型別,那麼另一個也轉為double
else if 某一運算元為float型別,那麼另一個也轉為float
else if 某一運算元為long型別,那麼另一個也轉為long
else 兩個運算元都轉為int
應用場景
- 乘除法運算子: * 、 / 、%
- 針對數字型別的加減運算子: + 、 -
- 數值比較運算子:< 、<= 、> 、>=
- 數值相等比較運算子: == 、 !=
- 整數按位運算子: & 、^ 、|
- 某些情況下的條件運算子 ? : 中
來個例子吧
class Test { public static void main(String[] args) { int i = 0; float f = 1.0f; double d = 2.0; // First int*float is promoted to float*float, then // float==double is promoted to double==double: if (i * f == d) System.out.println("oops"); // A char&byte is promoted to int&int: byte b = 0x1f; char c = 'G'; int control = c & b; System.out.println(Integer.toHexString(control)); // Here int:float is promoted to float:float: f = (b==0) ? i : 4.0f; System.out.println(1.0/f); } }
其實上面的都是冰山一角罷了
更多資訊可以檢視jls Chapter 5. Conversions and Contexts
參考資料
【1】https://docs.oracle.com/javase/specs/jls/se12/html/jls-5.html#jls-5