型態與變數看似簡單,但每種程式語言可能都有其不同的細節,接下來要介紹的型態轉換觀念,看似只是認證題目中很常考,然而實務上,確實也有些情況,因為忽略了型態轉換而誤踏地雷的例子,因此你還是要有所瞭解。
首先,如果你寫了這個程式片段:
double PI = 3.14;
這個片段編譯時沒有問題,但如果你寫了個程式片段:
float PI = 3.14;
就會得到了possible loss of precision的編譯錯誤?這是因為在程式中寫下一個浮點數時,編譯器預設會使用double
型態。就上圖而言,你想要將double
長度的資料指定給float
型態變數,編譯器就會很貼心地告訴你double
型態放到float
變數,會因為8個位元組要放到4個位元組而遺失4個位元組的資料。
你有兩種方式可以避免這個錯誤,第一個方式是在3.14後加上F
,這會告訴編譯器,請用float
來儲存3.14這個值。例如:
float PI = 3.14F;
另一個方式是,明確告訴編譯器,你就是要將double
型態的3.14丟(Cast)到float
變數中,請編譯器住嘴:
float PI = (float) 3.14;
編譯器看到double
型態的3.14要指定給float變數,本來囉嗦地告訴你會遺失精度,但你使用(float)
語法告訴編譯器,你就是要將double
型態的3.14指定給float
變數,別再囉嗦了,於是編譯器就住嘴不講話了,於是編譯通過,既然你不要編譯器囉嗦了,那執行時期出錯,那後果請自負,也就是如果真的因為遺失精度而發生程式錯誤了,那絕不是編譯器的問題。
再來看整數的部份。如果你寫下:
int number = 10;
這沒有問題。如果你寫下:
int number = 2147483648;
編譯時會得到integer number too large的錯誤?也許你以為原因是int
變數number
裝不下2147483648,因為int
型態最大值是2147483647,認為這樣可以解決問題:
long number = 2147483648;
編譯時還是會得到integer number too large的錯誤,事實上,並非是number
裝不下2147483648(如果是的話,編譯錯誤訊息應該是possible loss of precision),而是程式中寫下一個整數時,預設是使用不超過int
型態長度。2147483648超出了int型態的長度,你要直接告訴編譯器,用long
來配置整數的長度,也就是在數字後加上個L:
long number = 2147483648L;
如上就可以通過編譯了,方才談到,程式中寫下一個整數時,預設是使用不超過int
型態的長度,所以下面的程式可以通過編譯:
byte number = 10;
因為10是在byte
可儲存的範圍中,不過這樣不行:
byte number = 128;
128超過byte
可儲存的範圍,於是會使用int
儲存128,你要將int
型態儲存至byte變數,就會出現possible loss of precision的編譯錯誤。
再來看運算,如果運算式中包括不同型態數值,則運算時以長度最長的型態為主,其它數值自動提昇(Promote)型態。例如:
int a = 10;
double b = a * 3.14;
在這個程式片段中,a
是int
型態,而寫下的3.14預設是double
,所以a
的值被提至double
空間進行運算。
如果運算元都是不大於int
的整數,則自動全部提昇為int
型態進行運算。下面這個片段通不過編譯:
short a = 1;
short b = 2;
short c = a + b; // possible loss of precision
雖然a
與b
都是short
型態,但Java在運算整數時,如果全部的運算元都是不大於int
,那麼一律在int
的空間中運算,int
的運算結果要放到short
,編譯器就又會囉嗦遺失精度的問題,所以你要告訴編譯器,就是要將int的運算結果丟到short
,請它住嘴:
short a = 1;
short b = 2;
short c = (short) (a + b);
類似地,以下的程式片段通不過編譯:
short a = 1;
long b = 2;
int c = a + b; // possible loss of precision
記得之前說過嗎?如果運算式中包括不同型態,則運算時會以最長的型態為主,以上面的程式而言,b
是long
型態,於是a
也被提至long
空間中作運算,long
的運算結果要放到int
變數c
,自然就會被編譯器囉嗦精度遺失了。如果這真的是你想要的,那就叫編譯器住嘴吧!
short a = 1;
long b = 2;
int c = (int) (a + b);
那麼以下你覺得會顯示多少?
System.out.println(10 / 3);
答案是3,而不是3.333333....,因為10與3會在int長度的空間中作運算,因此不會作浮點數表示,如果想得到3.333333...的結果,那麼必須有一個運算元是浮點數。例如:
System.out.println(10.0 / 3);
很無聊對吧!好像只是在玩弄語法似地!那麼,稍微看看底下的程式片段有沒有問題?
int count = 0;
while(someCondition) {
if(count + 1 > Integer.MAX_VALUE) {
count = 0;
}
else {
count++;
}
...
}
這個程式片段想作的是,在某些情況下,不斷遞增count
的值,如果count
超過上限就歸零,在這邊以int
型態的最大值為上限。程式邏輯看似沒錯,但count + 1 > Integer.MAX_VALUE
永遠不會是true
,如果count
已經到了2147483647,也就是int
的最大值,此時記憶體中的位元組會是:
01111111 11111111 11111111 11111111
count + 1
則會變為:
10000000 00000000 00000000 00000000
位元組第一個位元是1,在Java中表示一個負數,上例也就是表示-2147483648,簡單來講,最後count + 1
會因為超出了int
可儲存範圍而溢值,count + 1 > Integer.MAX_VALUE
永遠不會成立。
Promotion 與 Cast 中還有兩個例子,你可以想想看問題是什麼?