1. 程式人生 > >【類型轉換】 隱式轉換 自動提升 強制轉換

【類型轉換】 隱式轉換 自動提升 強制轉換

最大 直接 歸納 隱式轉換 微軟雅黑 mil 邊界 操作 行數

基本數據類型的類型轉換

Java中基本數據類型共有8種,分別是:布爾型boolean,字符型char和數值型byte/short/int/long/float/double。由於字符型char所表示的單個字符與Ascii碼中相應整形對應,因此,有時也將其劃分到數值型中
基本數據類型中,布爾類型boolean占有一個字節,由於其本身所代表的特殊含義,boolean類型與其他基本類型不能進行類型的轉換(既不能進行自動類型的提升,也不能強制類型轉換), 否則,將編譯出錯。

數值類型在內存中直接存儲其本身的值,對於不同的數值類型,內存中會分配相應的大小去存儲。相應的,不同的數值類型會有與其存儲空間相匹配的取值範圍。具體如下所示:
byte->【char
->short->int->long->float->double
技術分享圖中依次表示了各數值類型的字節數和相應的取值範圍。在Java中,整數類型(byte/short/int/long)中,對於未聲明數據類型的整形,其默認類型為int型;浮點類型(float/double)中,對於未聲明數據類型的浮點型,默認為double型。

強制類型轉換

當我們需要將【數值範圍較大】的數值類型賦給【數值範圍較小】的數值類型變量時,由於此時【可能】會丟失精度(下面說的隱式轉換除外),因此,需要人為進行轉換。我們稱之為強制類型轉換
float b = 1.5;  // 編譯出錯 Type mismatch: cannot convert from double to float,1.5默認是一個double型,不能自動轉換為float類型
float b2 = (float) 1.5;  //  編譯正確。進行強制類型轉換
float b3 = 1.5f;  //  編譯正確。直接用一個float類型的數值給b3初始化

double d = 0;
float f = (float) d;
int i2 = (int) f;
long l = (long) f;//註意,雖然long有64位,而float只有32位,但float->>long時也需要強制類型轉換
強制類型轉換時,如果超出範圍較小的類型數值範圍,很可能出現一些意外情況,如下:
System.out.println((byte) 127 + "   " + (byte) 128 + "   " + (byte) 255 + "   " + (byte) 256); //127   -128   -1   0
所以,在進行強制類型轉換時,我們需要自己保證代碼的可靠性。

隱式類型轉換

jvm在編譯過程中,對於默認為int類型的數值,當賦給一個比int型數值範圍小的數值類型變量時(在此統一稱為數值類型k,k可以是byte/char/short類型),會進行判斷,如果此int型數值超過數值類型k,那麽會直接編譯出錯,因為你將一個超過了範圍的數值賦給類型為k的變量,k裝不下嘛,你有沒有進行強制類型轉換,當然報錯了;但是如果此int型數值尚在數值類型k範圍內,jvm會自動進行一次隱式類型轉換
,將此int型數值轉換成類型k
。如上圖中的虛線箭頭。這一點有點特別,需要特別註意。
接下來我們看看如下一個較為經典例子:
byte b1 = 3;      //編譯正確。如果此int型數值在數值類型byte的範圍內,則jvm會自動進行一次隱式類型轉換,將此int型數值轉換成byte類型
byte b2 = 1000;  //編譯出錯 Type mismatch: cannot convert from int to byte,註意,錯誤的原因是"在隱式類型轉換後發現超出了byte邊界",所以jvm就不進行"隱式類型轉換"了
byte b3 = (byte) 1000;  //  編譯正確。進行強制類型轉換
我們再看另一個經典例子:
byte p = 3; // 編譯正確:int到byte編譯過程中發生了【隱式類型轉換】
int a = 3;
byte b = a; // 編譯出錯:cannot convert from int to byte。
byte c = (byte) a; // 編譯正確。強制類型轉換
byte p =3;編譯正確在上面已經進行了解釋,接下來將一個值為3的int型變量a賦值給byte型變量b,發生編譯錯誤,這又是為什麽呢?區別在於前者3是直接量,編譯期間可以直接進行判定,後者a為一變量,需要到運行期間才能確定,也就是說,編譯期間為以防萬一,當然不可能編譯通過了。此時,需要進行強制類型轉換。

自動類型提升

當將一個數值範圍小的類型賦給一個數值範圍大的數值型變量,jvm在編譯過程中均將此數值的類型進行了自動提升。如上圖所示,從byte到double一路都可以自動類型提升。
byte->【char】->short->int->long->float->double
float f = 5;//編譯正確。整數默認是int類型,此時直接發生了【自動類型提升】
double d = 1.5f;//編譯正確。float類型的浮點數賦值給double類型,此時直接發生了【自動類型提升】
long l = ‘a‘;//編譯正確。字符‘a‘被當做int類型的97後賦值給long類型,此時直接發生了【自動類型提升】
在分析自動類型提升時,不要被一些虛假的表象欺騙了,如下經典案例:
long l_maxInt = 21474_83647;//編譯正確。
long l_bigerThanMaxInt = 21474_83648;//編譯出錯:The literal ** of type int is out of range。由於數值**【超過了int類型的範圍】,故而其自身已經編譯出錯
long l_endWithL = 21474_83648L;//編譯正確。如果一個整數以字母L或l結尾,則其類型為long類型;否則為默認的int類型

int i_outOfRange = Integer.MAX_VALUE + 1;//編譯正確。雖然結果溢出,但是程序沒有任何問題,所以需要開發中自己留意溢出問題
如上:定義long類型的 l_bigerThanMaxInt 變量時,將編譯出錯,原因在於 21474_83648 默認是int類型,同時int類型的數值範圍是-2^31 ~ 2^31-1(-21474_83648 - 21474_83647),因此,21474_83648 已經超過此範圍內的最大值,故而其自身已經編譯出錯,更談不上賦值給long型變量 l_bigerThanMaxInt 了。

不能 byte->char,char->short

由於char型其本身是unsigned型,同時具有兩個字節,其數值範圍是0 ~ 2^16-1,因此,這直接導致byte型不能自動類型提升到char,char到short直接也不會發生自動類型提升(因為負數的問題)。不過,byte當然可以直接提升到short等類型。
byte b = 3;
char c1=b;//編譯錯誤:Type mismatch: cannot convert from byte to char
char c2 = ‘a‘;
short s1 = c2;//編譯錯誤:Type mismatch: cannot convert from char to short
short s2 = b;//編譯正確。自動類型提升
實際上,你可以認為"不能 byte->char,char->short"是特例,也可以認為"能char->int,char->long,char->float,char->double"是特例,就看你怎麽解釋了。

自動類型提升規則

註意以下討論的是二元操作符
Java定義了若幹使用於表達式的類型提升規則:
  • 所有的byte型、short型和char型將被提升到int型(例外:final修飾的short、char變量相加後不會被自動提升)
  • 如果一個操作數是long型,計算結果就是long型
  • 如果一個操作數是float型,計算結果就是float型
  • 如果一個操作數是double型,計算結果就是double型
另一種歸納方式,《Java核心技術卷I》P43:  
  • 如果兩個操作數其中有一個是double類型,另一個操作就會轉換為double類型。
  • 否則,如果其中一個操作數是float類型,另一個將會轉換為float類型。
  • 否則,如果其中一個操作數是long類型,另一個會轉換為long類型。
  • 否則,兩個操作數都轉換為int類型
如下示例
byte b = 50;
char c = ‘a‘;
short s = 1024;
int i = 50000;
float f = 5.67f;
double d = 0.1234;
double result = (f * b) + (i / c) - (d * s);
System.out.println("(f * b)=" + (f * b) + "  (i / c)=" + (i / c) + "  (d * s)=" + (d * s) + "  result=" + result);
//(f * b)=283.5  (i / c)=515  (d * s)=126.3616  result=672.1384
//第一個表達式 f * b 中,b被提升為float類型,該子表達式的結果也提升為float類型。
//第二個表達式 i / c 中,變量c被提升為int類型,該子表達式的結果提升為int類型。
//第三個表達式 d * s 中,變量s被提升為double類型,該子表達式的結果提升為double型。
//最後,這三個結果類型分別是float,int和double類型,想減後該表達式的最後的結果就是double類型。

自動類型提升後的精度問題

在數值類型的自動類型提升過程中,數值精度問題要分情況來看
  • 整型-->整型,自動類型提升後精度保持不變
  • float-->double,自動類型提升後精度將變高
  • 整型-->float/double,暫且定義為"精度將變低"
    這個我表示很糾結啊,按上面的節奏來說的話,應該是精度將變高(比如int轉為float),但是實際上數值都可能被"消尾",也即數據都已經由"精確值"變成了"近似",那還哪來的"精度變高",很明顯是"精度變低"啊?但是,怎麽也找不到相應的說法。

float f = Integer.MAX_VALUE;
System.out.println(Integer.MAX_VALUE + "  " + f);//21474_83647  2.1474_8365E9(吶,被"消尾"了,已經由"精確值"變成了"近似值")
等以後找到更多更權威的資料後再補充

再來一個經典案例

進行數學運算時的數據類型自動提升與可能需要的強制類型轉換
byte b = 0, b1 = 1, b2 = 2;//【隱式類型轉換】
b = b1 + b2; //編譯出錯,Type mismatch: cannot convert from int to byte。進行 b1 + b2 運算時會先將b1和b2【自動類型提升】成為int,運算結果也是int,賦給byte類型的b1時需要強制轉換(向下轉型)
b = b + 0;//編譯出錯,同上面的錯誤一樣,運算時會先將b【自動類型提升】成為int,運算結果也是int
b = (byte) (b1 + b2);//編譯正確。【強制類型轉換】
b += b2; //編譯正確。【自加】沒有自增類型提升問題。編譯器在編譯的時候自動進行了強轉,相當於 b = (byte) (b + b2)

//一個奇葩的情況
final byte b3 = 3, b4 = 4;
b = b3 + b4;//編譯正確。有【final】修飾的變量相加後【不會被自動提升】為int類型

//一個更奇葩的情況
final byte b6 = Byte.MAX_VALUE, b7 = 0, b8 = 1;
b = b6 + b7;//編譯正確。
b = b6 + b8;//編譯出錯,Type mismatch: cannot convert from int to byte。註意,錯誤的原因和【long l = 21474_83648】一樣,是因為由於數值**【超過了byte類型的範圍】,故而其自身已經編譯出錯,而不是因為需要強制轉換(向下轉型)
b = 128;//編譯出錯,但是這裏出錯的原因我感覺和上面的原因是不一樣的
b = (byte) (b6 + b8);//編譯正確。強制轉換後肯定沒問題啦!
2017-8-27

來自為知筆記(Wiz)

【類型轉換】 隱式轉換 自動提升 強制轉換