JAVA面試題解惑系列(八)——聊聊基本類型(內建類型)

來源:互聯網
上載者:User

基本類型,或者叫做內建類型,是JAVA中不同於類的特殊類型。它們是我們編程中使用最頻繁的類型,因此面試題中也總少不了它們的身影,在這篇文章中我們將從面試中常考的幾個方面來回顧一下與基本類型相關的知識。

基本類型共有八種,它們分別都有相對應的封裝類。關於它們的詳細資料請看下錶:

基本類型可以分為三類,字元類型char,布爾類型boolean以及數實值型別byte、short、int、long、float、double。數實值型別又可以分為整數類型byte、short、int、long和浮點數類型float、double。JAVA中的數實值型別不存在無符號的,它們的取值範圍是固定的,不會隨著機器硬體環境或者作業系統的改變而改變。實際上,JAVA中還存在另外一種基本類型void,它也有對應的封裝類java.lang.Void,不過我們無法直接對它們進行操作。對於數實值型別的基本類型的取值範圍,我們無需強制去記憶,因為它們的值都已經以常量的形式定義在對應的封裝類中了。請看下面的例子:

Java代碼

public class PrimitiveTypeTest {       public static void main(String[] args) {           // byte           System.out.println("基本類型:byte 二進位位元:" + Byte.SIZE);           System.out.println("封裝類:java.lang.Byte");           System.out.println("最小值:Byte.MIN_VALUE=" + Byte.MIN_VALUE);           System.out.println("最大值:Byte.MAX_VALUE=" + Byte.MAX_VALUE);           System.out.println();             // short           System.out.println("基本類型:short 二進位位元:" + Short.SIZE);           System.out.println("封裝類:java.lang.Short");           System.out.println("最小值:Short.MIN_VALUE=" + Short.MIN_VALUE);           System.out.println("最大值:Short.MAX_VALUE=" + Short.MAX_VALUE);           System.out.println();             // int           System.out.println("基本類型:int 二進位位元:" + Integer.SIZE);           System.out.println("封裝類:java.lang.Integer");           System.out.println("最小值:Integer.MIN_VALUE=" + Integer.MIN_VALUE);           System.out.println("最大值:Integer.MAX_VALUE=" + Integer.MAX_VALUE);           System.out.println();             // long           System.out.println("基本類型:long 二進位位元:" + Long.SIZE);           System.out.println("封裝類:java.lang.Long");           System.out.println("最小值:Long.MIN_VALUE=" + Long.MIN_VALUE);           System.out.println("最大值:Long.MAX_VALUE=" + Long.MAX_VALUE);           System.out.println();             // float           System.out.println("基本類型:float 二進位位元:" + Float.SIZE);           System.out.println("封裝類:java.lang.Float");           System.out.println("最小值:Float.MIN_VALUE=" + Float.MIN_VALUE);           System.out.println("最大值:Float.MAX_VALUE=" + Float.MAX_VALUE);           System.out.println();             // double           System.out.println("基本類型:double 二進位位元:" + Double.SIZE);           System.out.println("封裝類:java.lang.Double");           System.out.println("最小值:Double.MIN_VALUE=" + Double.MIN_VALUE);           System.out.println("最大值:Double.MAX_VALUE=" + Double.MAX_VALUE);           System.out.println();             // char           System.out.println("基本類型:char 二進位位元:" + Character.SIZE);           System.out.println("封裝類:java.lang.Character");           // 以數值形式而不是字元形式將Character.MIN_VALUE輸出到控制台           System.out.println("最小值:Character.MIN_VALUE="                  + (int) Character.MIN_VALUE);           // 以數值形式而不是字元形式將Character.MAX_VALUE輸出到控制台           System.out.println("最大值:Character.MAX_VALUE="                  + (int) Character.MAX_VALUE);       }   }  

運行結果:

1、基本類型:byte 二進位位元:8
2、封裝類:java.lang.Byte
3、最小值:Byte.MIN_VALUE=-128
4、最大值:Byte.MAX_VALUE=127
5、
6、基本類型:short 二進位位元:16
7、封裝類:java.lang.Short
8、最小值:Short.MIN_VALUE=-32768
9、最大值:Short.MAX_VALUE=32767
10、
11、基本類型:int 二進位位元:32
12、封裝類:java.lang.Integer
13、最小值:Integer.MIN_VALUE=-2147483648
14、最大值:Integer.MAX_VALUE=2147483647
15、
16、基本類型:long 二進位位元:64
17、封裝類:java.lang.Long
18、最小值:Long.MIN_VALUE=-9223372036854775808
19、最大值:Long.MAX_VALUE=9223372036854775807
20、
21、基本類型:float 二進位位元:32
22、封裝類:java.lang.Float
23、最小值:Float.MIN_VALUE=1.4E-45
24、最大值:Float.MAX_VALUE=3.4028235E38
25、
26、基本類型:double 二進位位元:64
27、封裝類:java.lang.Double
28、最小值:Double.MIN_VALUE=4.9E-324
29、最大值:Double.MAX_VALUE=1.7976931348623157E308
30、
31、基本類型:char 二進位位元:16
32、封裝類:java.lang.Character
33、最小值:Character.MIN_VALUE=0
34、最大值:Character.MAX_VALUE=65535

Float和Double的最小值和最大值都是以科學記號標記法的形式輸出的,結尾的“E+數字”表示E之前的數字要乘以10的多少倍。比如3.14E3就是3.14×1000=3140,3.14E-3就是3.14/1000=0.00314。

大家將運行結果與上表資訊仔細比較就會發現float、double兩種類型的最小值與Float.MIN_VALUE、Double.MIN_VALUE的值並不相同,這是為什麼呢?實際上Float.MIN_VALUE和Double.MIN_VALUE分別指的是float和double類型所能表示的最小正數。也就是說存在這樣一種情況,0到±Float.MIN_VALUE之間的值float類型無法表示,0到±Double.MIN_VALUE之間的值double類型無法表示。這並沒有什麼好奇怪的,因為這些範圍內的數值超出了它們的精度範圍。

基本類型儲存在棧中,因此它們的存取速度要快於儲存在堆中的對應封裝類的執行個體對象。從Java5.0(1.5)開始,JAVA虛擬機器(Java Virtual Machine)可以完成基本類型和它們對應封裝類之間的自動轉換。因此我們在賦值、參數傳遞以及數學運算的時候像使用基本類型一樣使用它們的封裝類,但這並不意味著你可以通過基本類型調用它們的封裝類才具有的方法。另外,所有基本類型(包括void)的封裝類都使用了final修飾,因此我們無法繼承它們擴充新的類,也無法重寫它們的任何方法。

各種數實值型別之間的賦值與轉換遵循什麼規律呢?我們來看下面這個例子:
Java代碼

public class PrimitiveTypeTest {       public static void main(String[] args) {           // 給byte類型變數賦值時,數字後無需尾碼標識           byte byte_a = 1;           // 編譯器會做範圍檢查,如果賦予的值超出了範圍就會報錯           // byte byte_b = 1000;           // 把一個long型值賦值給byte型變數,編譯時間會報錯,即使這個值沒有超出byte類型的取值範圍           // byte byte_c = 1L;             // 給short類型變數賦值時,數字後無需尾碼標識           short short_a = 1;           // 編譯器會做範圍檢查,如果賦予的值超出了範圍就會報錯           // short short_b = 70000;           // 把一個long型值賦值給short型變數,編譯時間會報錯,即使這個值沒有超出short類型的取值範圍           // byte short_c = 1L;             // 給short類型變數賦值時,數字後無需尾碼標識           int int_a = 1;           // 編譯器會做範圍檢查,如果賦予的值超出了範圍就會報錯           // int int_b = 2200000000;           // 把一個long型值賦值給int型變數,編譯時間會報錯,即使這個值沒有超出int類型的取值範圍           // int int_c = 1L;             // 可以把一個int型值直接賦值給long型變數,數字後無需尾碼標識           long long_a = 1;           // 如果給long型變數賦予的值超出了int型值的範圍,數字後必須加L(不區分大小寫)標識           long long_b = 2200000000L;           // 編譯器會做範圍檢查,如果賦予的值超出了範圍就會報錯           // long long_c = 9300000000000000000L;             // 可以把一個int型值直接賦值給float型變數           float float_a = 1;           // 可以把一個long型值直接賦值給float型變數           float float_b = 1L;           // 沒有F(不區分大小寫)尾碼標識的浮點數預設為double型的,不能將它直接賦值給float型變數           // float float_c = 1.0;           // float型數值需要有一個F(不區分大小寫)尾碼標識           float float_d = 1.0F;           // 把一個double型值賦值給float型變數,編譯時間會報錯,即使這個值沒有超出float類型的取值範圍           // float float_e = 1.0D;           // 編譯器會做範圍檢查,如果賦予的值超出了範圍就會報錯           // float float_f = 3.5000000E38F;             // 可以把一個int型值直接賦值給double型變數           double double_a = 1;           // 可以把一個long型值直接賦值給double型變數           double double_b = 1L;           // 可以把一個float型值直接賦值給double型變數           double double_c = 1F;           // 不帶尾碼標識的浮點數預設為double類型的,可以直接賦值           double double_d = 1.0;           // 也可以給數字增加一個D(不區分大小寫)尾碼標識,明確標出它是double類型的           double double_e = 1.0D;           // 編譯器會做範圍檢查,如果賦予的值超出了範圍就會報錯           // double double_f = 1.8000000000000000E308D;             // 把一個double型值賦值給一個byte類型變數,編譯時間會報錯,即使這個值沒有超出byte類型的取值範圍           // byte byte_d = 1.0D;           // 把一個double型值賦值給一個short類型變數,編譯時間會報錯,即使這個值沒有超出short類型的取值範圍           // short short_d = 1.0D;           // 把一個double型值賦值給一個int類型變數,編譯時間會報錯,即使這個值沒有超出int類型的取值範圍           // int int_d = 1.0D;           // 把一個double型值賦值給一個long類型變數,編譯時間會報錯,即使這個值沒有超出long類型的取值範圍           // long long_d = 1.0D;             // 可以用字元初始化一個char型變數           char char_a = 'a';           // 也可以用一個int型數值初始化char型變數           char char_b = 1;           // 把一個long型值賦值給一個char類型變數,編譯時間會報錯,即使這個值沒有超出char類型的取值範圍           // char char_c = 1L;           // 把一個float型值賦值給一個char類型變數,編譯時間會報錯,即使這個值沒有超出char類型的取值範圍           // char char_d = 1.0F;           // 把一個double型值賦值給一個char類型變數,編譯時間會報錯,即使這個值沒有超出char類型的取值範圍           // char char_e = 1.0D;           // 編譯器會做範圍檢查,如果賦予的值超出了範圍就會報錯           // char char_f = 70000;       }   }  

從上面的例子中我們可以得出如下幾條結論:

1、未帶有字元尾碼標識的整數預設為int類型;未帶有字元尾碼標識的浮點數預設為double類型。
2、如果一個整數的值超出了int類型能夠表示的範圍,則必須增加尾碼“L”(不區分大小寫,建議用大寫,因為小寫L與阿拉伯數字1很容易混淆),表示為long型。
3、帶有“F”(不區分大小寫)尾碼的整數和浮點數都是float類型的;帶有“D”(不區分大小寫)尾碼的整數和浮點數都是double類型的。
4、編譯器會在編譯期對byte、short、int、long、float、double、char型變數的值進行檢查,如果超出了它們的取值範圍就會報錯。
5、int型值可以賦給所有數實值型別的變數;long型值可以賦給long、float、double類型的變數;float型值可以賦給float、double類型的變數;double型值只能賦給double類型變數。

顯示了幾種基本類型之間的預設邏輯轉換關係:

圖中的實線表示無精度損失的轉換,而虛線則表示這樣的轉換可能會損失一定的精度。如果我們想把一個能表示更大範圍或者更高精度的類型,轉換為一個範圍更小或者精度更低的類型時,就需要使用強制類型轉換(Cast)了。不過我們要盡量避免這種用法,因為它常常引發錯誤。請看下面的例子,如果不運行代碼,你能預測它的結果嗎?

Java代碼

public class PrimitiveTypeTest {       public static void main(String[] args) {           int a = 123456;           short b = (short) a;           // b的值會是什麼呢?           System.out.println(b);       }   }  

運行結果:

1、-7616

運算子對基本類型的影響

當使用+、-、*、/、%運算子對基本類型進行運算時,遵循如下規則:

1、只要兩個運算元中有一個是double類型的,另一個將會被轉換成double類型,並且結果也是double類型;
2、否則,只要兩個運算元中有一個是float類型的,另一個將會被轉換成float類型,並且結果也是float類型;
3、否則,只要兩個運算元中有一個是long類型的,另一個將會被轉換成long類型,並且結果也是long類型;
4、否則,兩個運算元(包括byte、short、int、char)都將會被轉換成int類型,並且結果也是int類型。

當使用+=、-=、*=、/=、%=、運算子對基本類型進行運算時,遵循如下規則:

1、運算子右邊的數值將首先被強制轉換成與運算子左邊數值相同的類型,然後再執行運算,且運算結果與運算子右邊數實值型別相同。

瞭解了這些,我們就能解答下面這個常考的面試題了。請看:

short s1=1;s1=s1+1;有什麼錯?short s1=1;s1+=1;有什麼錯?

乍一看,覺得它們都應該沒有錯誤,可以正常運行。我們來寫個例子試試:
Java代碼

public class PrimitiveTypeTest {       public static void main(String[] args) {           short s1 = 1;           // 這一行代碼會報編譯錯誤           // s1 = s1 + 1;           // 這一行代碼沒有報錯           s1 = 1 + 1;           // 這一行代碼也沒有報錯           s1 += 1;       }   }  

從例子中我們可以看出結果了。利用上面列舉的規律,也很容易解釋。在s1=s1+1;中,s1+1運算的結果是int型,把它賦值給一個short型變數s1,所以會報錯;而在s1+=1;中,由於是s1是short類型的,所以1首先被強制轉換為short型,然後再參與運算,並且結果也是short類型的,因此不會報錯。那麼,s1=1+1;為什麼不報錯呢?這是因為1+1是個編譯時間可以確定的常量,“+”運算在編譯時間就被執行了,而不是在程式執行的時候,這個語句的效果等同於s1=2,所以不會報錯。前面講過了,對基本類型執行強制類型轉換可能得出錯誤的結果,因此在使用+=、-=、*=、/=、%=等運算子時,要多加註意。

Math.round()方法

java.lang.Math類裡有兩個round()方法,它們的定義如下:
Java代碼

public static int round(float a) {       //other code   }     public static long round(double a) {       //other code   }  

它們的傳回值都是整數,且都採用四捨五入法。運算規則如下:

1、如果參數為正數,且小數點後第一位>=5,運算結果為參數的整數部分+1。
2、如果參數為負數,且小數點後第一位>5,運算結果為參數的整數部分-1。
3、如果參數為正數,且小數點後第一位<5;或者參數為負數,且小數點後第一位<=5,運算結果為參數的整數部分。

我們可以通過下面的例子來驗證:
Java代碼

public class MathTest {       public static void main(String[] args) {           System.out.println("小數點後第一位=5");           System.out.println("正數:Math.round(11.5)=" + Math.round(11.5));           System.out.println("負數:Math.round(-11.5)=" + Math.round(-11.5));           System.out.println();             System.out.println("小數點後第一位<5");           System.out.println("正數:Math.round(11.46)=" + Math.round(11.46));           System.out.println("負數:Math.round(-11.46)=" + Math.round(-11.46));           System.out.println();             System.out.println("小數點後第一位>5");           System.out.println("正數:Math.round(11.68)=" + Math.round(11.68));           System.out.println("負數:Math.round(-11.68)=" + Math.round(-11.68));       }   }  

運行結果:

1、小數點後第一位=5
2、正數:Math.round(11.5)=12
3、負數:Math.round(-11.5)=-11
4、
5、小數點後第一位<5
6、正數:Math.round(11.46)=11
7、負數:Math.round(-11.46)=-11
8、
9、小數點後第一位>5
10、正數:Math.round(11.68)=12
11、負數:Math.round(-11.68)=-12

根據上面例子的運行結果,我們還可以按照如下方式總結,或許更加容易記憶:

1、參數的小數點後第一位<5,運算結果為參數整數部分。
2、參數的小數點後第一位>5,運算結果為參數整數部分絕對值+1,符號(即正負)不變。
3、參數的小數點後第一位=5,正數運算結果為整數部分+1,負數運算結果為整數部分。

switch語句

哪些類型可以用於switch語句的判斷呢?我們做個測試就知道了:
Java代碼

public class MathTest {       // 枚舉類型,Java5.0以上版本可用       static enum enum_e {           A, B       }         public static void main(String[] args) {           // byte           byte byte_n = 0;           switch (byte_n) {           case 0:               System.out.println("byte可以用於switch語句");               break;           }             // Byte類           Byte byte_m = 0;           // 需要Java5.0(1.5)以上版本支援           switch (byte_m) {           case 0:               System.out.println("Byte類可以用於switch語句");               System.out.println();               break;           }             // char           char char_n = 0;           switch (char_n) {           case 0:               System.out.println("char可以用於switch語句");               break;           }             // Character類           Character char_m = 0;           // 需要Java5.0(1.5)以上版本支援           switch (char_m) {           case 0:               System.out.println("Character類可以用於switch語句");               System.out.println();               break;           }             // short           short short_n = 0;           switch (short_n) {           case 0:               System.out.println("short可以用於switch語句");               break;           }             // Short           Short short_m = 0;           // 需要Java5.0(1.5)以上版本支援           switch (short_m) {           case 0:               System.out.println("Short類可以用於switch語句");               System.out.println();               break;           }             // int           int int_n = 0;           switch (int_n) {           case 0:               System.out.println("int可以用於switch語句");               break;           }             // Integer類           Integer int_m = 0;           // 需要Java5.0(1.5)以上版本支援           switch (int_m) {           case 0:               System.out.println("Integer類可以用於switch語句");               System.out.println();               break;           }             // long           long long_n = 0;           // 編譯錯誤,long型不能用於switch語句           // switch (long_n) {           // case 0:           // System.out.println("long可以用於switch語句");           // break;           // }             // Long類           Long long_m = 0L;           // 編譯錯誤,Long類型不能用於switch語句           // switch (long_m) {           // case 0:           // System.out.println("Long類可以用於switch語句");           // System.out.println();           // break;           // }             // float           float float_n = 0.0F;           // 編譯錯誤,float型不能用於switch語句           // switch (float_n) {           // case 0.0F:           // System.out.println("float可以用於switch語句");           // break;           // }             // Float類           Float float_m = 0.0F;           // 編譯錯誤,Float類型不能用於switch語句           // switch (float_m) {           // case 0.0F:           // System.out.println("Float類可以用於switch語句");           // System.out.println();           // break;           // }             // double           double double_n = 0.0;           // 編譯錯誤,double型不能用於switch語句           // switch (double_n) {           // case 0.0:           // System.out.println("double可以用於switch語句");           // break;           // }             // Double類           Double double_m = 0.0;           // 編譯錯誤,Double類型不能用於switch語句           // switch (double_m) {           // case 0.0:           // System.out.println("Double類可以用於switch語句");           // System.out.println();           // break;           // }             // boolean           boolean bool_b = true;           // 編譯錯誤,boolean型不能用於switch語句           // switch (bool_b) {           // case true:           // System.out.println("boolean可以用於switch語句");           // break;           // }             // Boolean類           Boolean bool_l = true;           // 編譯錯誤,Boolean類型不能用於switch語句           // switch (bool_l) {           // case true:           // System.out.println("Boolean類可以用於switch語句");           // System.out.println();           // break;           // }             // String對象           String string_s = "Z";           // 編譯錯誤,long型不能用於switch語句           // switch (string_s) {           // case "Z":           // System.out.println("String可以用於switch語句");           // System.out.println();           // break;           // }             // enum(枚舉類型,Java5.0以上版本可用)           switch (MathTest.enum_e.A) {           case A:               System.out.println("enum可以用於switch語句-A");               break;           case B:               System.out.println("enum可以用於switch語句-B");               break;           }       }   }  

運行結果如下:

1、byte可以用於switch語句
2、Byte類可以用於switch語句
3、
4、char可以用於switch語句
5、Character類可以用於switch語句
6、
7、short可以用於switch語句
8、Short類可以用於switch語句
9、
10、int可以用於switch語句
11、Integer類可以用於switch語句
12、
13、enum可以用於switch語句-A

結果已經出來了,我們來總結一下:

1、byte、char、short、int四種基本類型以及它們的封裝類(需要Java5.0/1.5以上版本支援)都可以用於switch語句。
2、long、float、double、boolean四種基本類型以及它們的封裝類(在Java所有版本中)都不能用於switch語句。
3、enum類型,即枚舉類型可以用於switch語句,但是要在Java5.0(1.5)版本以上才支援。
4、所有類型的對象(包括String類,但在Java5.0/1.5以上版本中,該項要排除byte、char、short、int四種基本類型對應的封裝類)都不能用於switch語句。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.