一、基本類型的簡介 基本類型的兩條準則: Java中,如果對整數不指定類型,預設時int類型,對小數不指定類型,預設是double類型。 基本類型由小到大,可以自動轉換,但是由大到小,則需要強制類型轉換。 所占的位元組數: byte: 1個位元組; char: 2個位元組; short: 2個位元組; in ...
一、基本類型的簡介
基本類型的兩條準則:
- Java中,如果對整數不指定類型,預設時int類型,對小數不指定類型,預設是double類型。
- 基本類型由小到大,可以自動轉換,但是由大到小,則需要強制類型轉換。
所占的位元組數:
byte: 1個位元組;
char: 2個位元組;
short: 2個位元組;
int: 4個位元組;
long: 8個位元組;
float: 4個位元組;(6位小數,指數是:10^-38~10^38; 範圍:)
double: 8個位元組;
char:Java中用 "\u四位十六進位的數字 (即使在註釋中出現\u,後面如果
跟的不是4個16進位的數字,也會報錯)"表示將字元轉換成對應的unicode編 碼;也可以用字元來賦值如: char c="\u0000" ,char的預設初始化值,unicode的null字元
基本類型的尾碼:
long : l 或 L
float: f 或 F;
double: d 或 D
二、類型轉換
正如前面所說的,類型由大到小,是必需強制轉換。但這並不意味著需要用戶手動強制轉換 —— 也就是 隱式轉換。隱式轉換 說的透徹點就是由編譯器來進行強制轉換,不需要用戶再去寫強制轉換的代碼。下麵的前兩個小點所說的便是特殊的隱式類型轉換。
本小節所討論的類型轉換是不包括 類型由小到大的轉換,討論的是其他比較容易讓人迷惑的類型轉換
1. int類型的字面常量轉換成比int類型低的變數類型
所謂的字面常量就是值的本身,如 5、7、“aa”等等。我們先看個例子:
public static void main(String[] args) {
int a = 8; //8是字面常量
byte b = 9; //9是字面常量
char c = 9+5;//常量表達式
short s = (short) (c+10); //變數表達式,需要顯式強制轉換
}
上面的代碼是經過編譯的,是正確的。b是byte類型,但b=9
不需要顯式地手動強制轉換,這是因為9是字面常量,是由JVM自動完成。
我們再來看一下c=9+5
,c是char類型,9+5
得到結果是int類型,但也不需要顯式地手動強制轉換。這是因為 9+5
是常量表達式,所以在編譯期間已經由編譯器計算出結果了,即經過編譯後,相當於 c=14
,也是字面常量,所以可以隱式轉換。同理,short s = (short) (c+10);
子所以不能隱式轉換,就是因為表達式不是常量表達式,包含了變數,只能在運行期間完成,所以就要手動強制轉換。
整形字面常量隱式轉換的限制:
- 整形字面常量的大小超出目標類型所能表示的範圍時,要手動強制類型轉換。
byte b = 128;//編譯錯誤,128超出byte類型所能表示的範圍
byte c = (byte)128;//編譯通過
- 對於傳參數時,必須要顯式地進行強制類型轉換,明確轉換的類型
編譯器子所以這樣要求,其實為了避免 方法重載出現的隱式轉換 與 小類型自動轉大類型 發生衝突。
public static void main(String[] args) {
shortMethod(8);//編譯錯誤
shortMethod((short)8); //編譯通過
longMethod(8);//編譯通過,因為這是小類型變成大類型,是不需要強制類型轉換的
}
public static void shortMethod(short c){
System.out.println(c);
}
public static void longMethod(short l){
System.out.println(l);
}
- char類型的特殊情況 :下麵再細講
2. 複合運算符的隱式轉換
複合運算符(+=、-=、*=、/=、%=)是可以將右邊表達式的類型自動強制轉換成左邊的類型
public static void main(String[] args) {
int a = 8;
short s = 5;
s += a;
s += a+5;
}
s+=a
、s+=a+5;
的表達式計算結果都是int類型,但都不需要手動強制轉換。其實,如果是反編譯這段代碼的class文件,你會發現s+=a;
,其實是被編譯器處理成了
s=(short)(s+a)
也就是說對於所有的複合運算的隱式類型轉換,其實是編譯器自動添加類型轉換的代碼。
所以,相對於整形字面常量的隱式轉換,複合運算符的隱式轉換則沒有任何限制因為前者只能在編譯器期間發生,後者則是編譯器實實在在的補全了類型轉換的代碼。
3. 特殊的char類型
char類型在基本類中是一個比較特殊的存在。這種特殊性在於char類型是一個無符號類型,所以char類型與其他基本類型不是子集與父集間的關係(其他類型都是有符號的類型)。也就是說,char類型與byte、short之間的轉換都需要顯式的強制類型轉換(小類型自動轉換成大類型失敗)。
同時,由於char類型是一個無符號類型,所以對於整形字面常量的隱式轉換的限制,不僅包括字面常量數值的大小不能超出2個位元組,還包括字面常量數值不能為負數
byte b = 2;
char c = 2;//編譯通過
c = 100000000000;//編譯不通過,超出char類型的範圍
char d = -2//字面常量為負數,編譯不通過
d = (char)-100;//編譯通過
char f = (char)b; //編譯通過,必須顯式的強制類型轉換
f = b;//編譯不通過,不能隱式轉換
int i = c;//編譯通過,可以不需要強制類型轉換
short s = (short) c;//編譯通過,必須顯式地強制類型轉換
char類型是無符號的類型,這種無符號也體現在在其轉換成int類型時,也就是說,char類型在擴展時,也是按無符號的方式擴展,擴展位填0。我們來看一個例子:
public static void main(String[] args) {
short s = -5;
char c = (char)s;
System.out.println(c==s); //false
System.out.println("(int)c = "+(int)c); //轉換成int類型,值為65531
System.out.println("(short)c = "+(short)c); //-5
System.out.println("(int)s = "+(int)s);//-5
}
運行結果:
false
(int)c = 65531
(short)c = -5
(int)s = -5
從上面的結果發現,char類型的c 與 short類s其實存儲位元組碼內容是一樣的,但由於前者是無符號,所以擴展成int類型的結果是 65531,而不是 -5。運算符==
比較的就是他們擴展成int類型的值,所以為fasle。
對char類型的類型轉換,可以總結成以下幾點:
- char類型與byte、short的相互轉換,都需要顯式地強類型制轉換。
- 對於數值是負數的,都需要進行顯式地強制類型轉換,特別是在整形字面常量的隱式轉換中。
- char類型轉換成int、long類型是符合 小類型轉大類型的規則,即無需要強制類型轉換。
4. 運算結果的類型
在Java中,一個運算結果的類型是與表達式中類型最高的相等,如:
char cc = 5;
float dd = 0.6f+cc;//最高類型是float,運算結果是float
float ee = (float) (0.6d+cc);//最高類型是double,運算結果也是double
int aa = 5+cc;//最高類型是int,運算結果也為int
但是,對於最高類型是byte、short、char的運算來說,則運行結果卻不是最高類型,而是int類型。看下麵的例子,c、d運算的最高類型都是char,但運算結果卻是int,所以需要強制類型轉換。
byte b = 2;
char a = 5;
char c = (char) (a+b);//byte+char,運算結果的類型為int,需要強制類型轉換
int e = a+b;//編譯通過,不需要強制類型轉換,可以證明是int
char d = (char) (a+c);//char+char,
short s1 = 5;
short s2 = 6;
short s3 =(short)s1+s2;
綜上所述,java的運算結果的類型有兩個性質:
- 運算結果的類型必須是int類型或int類型以上。
- 最高類型低於int類型的,運算結果都為int類型。否則,運算結果與表達式中最高類型一致。
三、浮點數類型
1. 浮點類型的介紹
我們都知道,long類型轉換成float類型是不需要強制類型轉換的,也就是說相對於flaot類型,long類型是小類型,存儲的範圍要更小。然而flaot只占了4個位元組,而long卻占了8個位元組,long類型的存儲空間要比float類型大。這究竟是怎麼一回事,我們接下來將細細分析。
浮點數使用 IEEE(電氣和電子工程師協會)格式。 浮點數類型使用 符號位、指數、有效位數(尾數)來表示。要註意一下,尾數的最高
在java中,float 和 double 的結構如下:
類 型 | 符 號 位 | 指 數 域 | 有效位域 |
---|---|---|---|
float | 1位 | 8位 | 23位 |
double | 1位 | 11位 | 52位 |
符號位: 0為正,1為負;
指數域: 無符號的,float的偏移量為127(即float的指數範圍是-126~127,),double
有效位域 無符號的;
2. 浮點類型的兩個需要註意的地方
1)存儲的小數的數值可能是模糊值
public static void main(String[] args) {
double d1 = 0.1;
double d2 = 0.2;
System.out.println(d1+d2 == 0.3);
System.out.println(d1+d2);
}
運行結果:
false
0.30000000000000004
上述的運算結果並不是錯誤。這是因為無法用二進位來準確地存儲的0.3,這是一個無限迴圈的值,與10進位的1/3很相似。不只是0.3,很多小數都是無法準確地用浮點型表示,其實這是由 小數的十進位轉成二進位的演算法所決定的,十進位的小數要不斷乘2,知道最後的結果為整數才是最後的二進位值,但這有可能怎麼也得不到整數,所以最後得到的結果可能是一個 無限值 ,浮點型就無法表示了
但是對於 整數 來說,在浮點數的有效範圍內,則都是精確的。同樣,也是由於轉換演算法:十進位的整數轉成二進位的演算法是不斷對2求餘數,所以 不會存在無限值的情況;
2)浮點數的有效位及精度
浮點型所能表示的有效位是有限的,所以哪怕是整數,只要超出有效位數,也只能存儲相似值,也就是該數值的最低有效位將會丟失,從而造精度丟失。
float類型的二進位有效位是24位,對應十進位的7 ~ 8位數字;double類型的二進位53位,對應十進位的10 ~ 11位數字。
double、float類型 所能表示的範圍比int、long類型表示的範圍要廣,也浮點類型屬於大類型。但是,並不能完美地表整形,浮點類型的精度丟失會造成一些問題。
public static void main(String[] args) {
int a = 3000000;
int b = 30000000;
float f1 = a;
float f2 = b;
System.out.println("3000000==3000001 "+(f1==f1+1));
System.out.println("30000000==30000001 "+(f2==f2+1));
System.out.println("3000000的有效二進位位數:"+ Integer.toBinaryString(a).length());
System.out.println("30000000的有效二進位位數:"+ Integer.toBinaryString(b).length());
}
運行結果:
3000000 == 3000001 false
30000000 == 30000001 true
3000000的有效二進位位數: 22
30000000的有效二進位位數: 25
上面的例子很好體現了精度丟失所帶來的後果:30000000==30000001
的比較居然為true了。而造成這種結果的原因就是 30000000的有效二進位位數是25位,超出了float所能表示的有效位24位,最後一位就被捨去,所以就造成在剛加的1也被捨去,因此30000000的加一操作前後的浮點型表示是一樣的。
當然,並不是超出浮點型的有效位就不能精確表示,其實,主要看的是最高有效位與最低非0有效位之間的 “間隙”,如果間隙的在浮點型的有效位數內,自然可以精確表示,因為捨去的低有效位都是0,自然就無所謂了。如果上面的例子的浮點型用的是double就不會丟失精度了,因為double的精度是52位。
3)解決浮點型精度丟失的問題
浮點型帶來精度丟失的問題是很讓人頭痛的,所以一般情況下,在程式中是不會使用float、double來存儲比較大的數據。而商業計算往往要求結果精確。《Effactive Java》書中有一句話:
float和double類型的主要設計目標是為了科學計算和工程計算
JDK為此提供了兩個高精度的大數操作類給我們:BigInteger、BigDecimal。