java基礎(一) 深入解析基本型別

程式設計師歐陽思海發表於2018-04-09

一、基本型別的簡介

基本型別的兩條準則:

  • 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。

出處:http://www.cnblogs.com/jinggod/p/8424583.html

文章有不當之處,歡迎指正,你也可以關注我的微信公眾號:好好學java,獲取優質資源。

相關文章