1. 程式人生 > >小白的消費為何被迫升級?-java資料型別的轉換

小白的消費為何被迫升級?-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