Java中,經常可以遇到類型轉換的場景,從變量的定義到復制、數值變量的計算到方法的參數傳遞、基類與派生類間的造型等,隨處可見類型轉換的身影。Java中的類型轉換在Java編碼中具有重要的作用。
首先,來了解下數據類型的基本理解:數據是用來描述數據的種類,包括其值和基於其值基礎上的可進行的操作集合。
Java中數據類型主要分為兩大類:基本數據類型和引用數據類型。
基本數據類型共有8種,分別是:布爾型boolean, 字符型char和數值型byte/short/int/long/float/double。由於字符型char所表示的單個字符與Ascii碼中相應整形對應,因此,有時也將其划分到數值型中。引用類型具體可分為:數組、類和接口。因此,本文中Java類型轉換的總結也將分為基本數據類型和引用數據類型兩個方面展開。
一、基本數據類型的類型轉換
基本數據類型中,布爾類型boolean占有一個字節,由於其本身所代碼的特殊含義,boolean類型與其他基本類型不能進行類型的轉換(既不能進行自動類型的提升,也不能強制類型轉換), 否則,將編譯出錯。
1.基本數據類型中數值類型的自動類型提升
數值類型在內存中直接存儲其本身的值,對於不同的數值類型,內存中會分配相應的大小去存儲。如:byte類型的變量占用8位,int類型變量占用32位等。相應的,不同的數值類型會有與其存儲空間相匹配的取值范圍。具體如下所示:
圖中依次表示了各數值類型的字節數和相應的取值范圍。在Java中,整數類型(byte/short/int/long)中,對於未聲明數據類型的整形,其默認類型為int型。在浮點類型(float/double)中,對於未聲明數據類型的浮點型,默認為double型。
接下來我們看看如下一個較為經典例子:
1 package com.corn.testcast; 2
3
4 public class TestCast { 5
6 public static void main(String[] args) { 7 byte a = 1000; // 編譯出錯 Type mismatch: cannot convert from int to byte
8 float b = 1.5; // 編譯出錯 Type mismatch: cannot convert from double to float
9 byte c = 3; // 編譯正確
10 } 11
12 }
是不是有點奇怪?按照上面的思路去理解,將一個int型的1000賦給一個byte型的變量a,編譯出錯,提示"cannot convert from int to byte"是對的,1.5默認是一個double型,將一個double類型的值賦給一個float類型,編譯出錯,這也是對的。但是最后一句:將一個int型的3賦給一個byte型的變量c,居然編譯正確,這是為什么呢?
原因在於:jvm在編譯過程中,對於默認為int類型的數值時,當賦給一個比int型數值范圍小的數值類型變量(在此統一稱為數值類型k,k可以是byte/char/short類型),會進行判斷,如果此int型數值超過數值類型k,那么會直接編譯出錯。因為你將一個超過了范圍的數值賦給類型為k的變量,k裝不下嘛,你有沒有進行強制類型轉換,當然報錯了。但是如果此int型數值尚在數值類型k范圍內,jvm會自定進行一次隱式類型轉換,將此int型數值轉換成類型k。如圖中的虛線箭頭。這一點有點特別,需要稍微注意下。
在其他情況下,當將一個數值范圍小的類型賦給一個數值范圍大的數值型變量,jvm在編譯過程中俊將此數值的類型進行了自動提升。在數值類型的自動類型提升過程中,數值精度至少不應該降低(整型保持不變,float->double精度將變高)。
1 package com.corn.testcast; 2
3 public class TestCast { 4
5 public static void main(String[] args) { 6 long a = 10000000000; //編譯出錯: The literal 10000000000 of type int is out of range
7 long b = 10000000000L; //編譯正確
8 int c = 1000; 9 long d = c; 10 float e = 1.5F; 11 double f = e; 12 } 13
14 }
如上:定義long類型的a變量時,將編譯出錯,原因在於10000000000默認是int類型,同時int類型的數值范圍是-2^31 ~ 2^31-1,因此,10000000000已經超過此范圍內的最大值,故而其自身已經編譯出錯,更談不上賦值給long型變量a了。
此時,若想正確賦值,改變10000000000自身默認的類型即可,直接改成10000000000L即可將其自身類型定義為long型。此時再賦值編譯正確。
將值為1000的int型變量c賦值給long型變量d,按照上文所述,此時直接發生了自動類型提升, 編譯正確。同理,將e賦給f編譯正確。
接下來,還有一個地方需要注意的是:char型其本身是unsigned型,同時具有兩個字節,其數值范圍是0 ~ 2^16-1,因為,這直接導致byte型不能自動類型提升到char,char和short直接也不會發生自動類型提升(因為負數的問題),同時,byte當然可以直接提升到short型。
2.基本數據類型中的數值類型強制轉換
當我們需要將數值范圍較大的數值類型賦給數值范圍較小的數值類型變量時,由於此時可能會丟失精度(1講到的從int到k型的隱式轉換除外),因此,需要人為進行轉換。我們稱之為強制類型轉換。
首先我們看一下如下的例子:
1 package com.corn.testcast; 2 3 public class TestCast { 4 5 public static void main(String[] args) { 6 byte p = 3; // 編譯正確:int到byte編譯過程中發生隱式類型轉換 7 int a = 3; 8 byte b = a; // 編譯出錯:cannot convert from int to byte 9 byte c = (byte) a; // 編譯正確 10 float d = (float) 4.0; 11 } 12 13 }
byte p =3;編譯正確在1中已經進行了解釋。接下來將一個值為3的int型變量a賦值給byte型變量b,發生編譯錯誤。這兩種寫法之間有什么區別呢?
區別在於前者3是直接量,編譯期間可以直接進行判定,后者a為一變量,需要到運行期間才能確定,也就是說,編譯期間為以防萬一,當然不可能編譯通過了。此時,需要進行強制類型轉換。
強制類型轉換所帶來的結果是可能會丟失精度,如果此數值尚在范圍較小的類型數值范圍內,對於整型變量精度不變,但如果超出范圍較小的類型數值范圍內,很可能出現一些意外情況。
如下經典例子:
1 package com.corn.testcast; 2 3 public class TestCast { 4 5 public static void main(String[] args) { 6 int a = 233; 7 byte b = (byte) a; 8 System.out.println("b:" + b); // 輸出:-23 9 } 10 11 }
為什么結果是-23?需要從最根本的二進制存儲考慮。
233的二進制表示為:24位0 + 11101001,byte型只有8位,於是從高位開始舍棄,截斷后剩下:11101001,由於二進制最高位1表示負數,0表示正數,其相應的負數為-23。
3.進行數學運算時的數據類型自動提升與可能需要的強制類型轉換
如下代碼:
1 package com.corn.testcast; 2 3 public class TestCast { 4 5 public static void main(String[] args) { 6 byte a = 3 + 5; // 編譯正常 編譯成 3+5直接變為8 7 int b = 3, c = 5; 8 byte d = b + c; // 編譯錯誤:cannot convert from int to byte 9 10 byte e = 10, f = 11; 11 byte g = e + f; // 編譯錯誤 +直接將10和11類型提升為了int 12 byte h = (byte) (e + f); //編譯正確 13 } 14 15 }
當進行數學運算時,數據類型會自動發生提升到運算符左右之較大者,以此類推。當將最后的運算結果賦值給指定的數值類型時,可能需要進行強制類型轉換。
二、引用類型的類型轉換/造型
本部分內容將在后續時間在本文中補上。