第 1-2 课:你不知道的基础数据类型和包装类 + 面试题
基本數據類型
Java 基礎數據按類型可以分為四大類:布爾型、整數型、浮點型、字符型,這四大類包含 8 種基礎數據類型。
- 布爾型:boolean
- 整數型:byte、short、int、long
- 浮點型:float、double
- 字符型:char
八種基礎類型取值如下:
| boolean | 布爾型 | false | 0(false) 到 1(true) | Boolean |
| byte | 字節型 | (byte)0 | -128 到 127 | Byte |
| char | 字符型 | '\u0000'(空) | '\u0000' 到 '\uFFFF' | Character |
| short | 短整數型 | (short)0 | -215 到 215-1 | Short |
| int | 整數型 | 0 | -231 到 231-1 | Integer |
| long | 長整數型 | 0L | -263 到 263-1 | Long |
| float | 單浮點型 | 0.0f | 1.4e-45 到 3.4e+38 | Float |
| double | 雙浮點型 | 0.0d | 4.9e-324 到 1.798e+308 | Double |
除 chat 的包裝類 Character 和 int 的包裝類 Integer 之外,其他基礎數據類型的包裝類只需要首字母大寫即可。包裝類的作用和特點,本文下半部分詳細講解。
我們可以在代碼中,查看某種類型的取值范圍,代碼如下:
public static void main(String[] args) {// Byte 取值:-128 ~ 127System.out.println(String.format("Byte 取值:%d ~ %d", Byte.MIN_VALUE, Byte.MAX_VALUE));// Int 取值:-2147483648 ~ 2147483647System.out.println(String.format("Int 取值:%d ~ %d", Integer.MIN_VALUE, Integer.MAX_VALUE)); }包裝類型
我們知道 8 種基本數據類型都有其對應的包裝類,因為 Java 的設計思想是萬物既對象,有很多時候我們需要以對象的形式操作某項功能,比如說獲取哈希值(hashCode)或獲取類(getClass)等。
那包裝類特性有哪些?
1. 功能豐富
包裝類本質上是一個對象,對象就包含有屬性和方法,比如 hashCode、getClass 、max、min 等。
2. 可定義泛型類型參數
包裝類可以定義泛型,而基礎類型不行。
比如使用 Integer 定義泛型,代碼:
List<Integer> list = new ArrayList<>();如果使用 int 定義就會報錯,代碼:
List list = new ArrayList<>(); // 編譯器代碼報錯3. 序列化
因為包裝類都實現了 Serializable 接口,所以包裝類天然支持序列化和反序列化。比如 Integer 的類圖如下:
4. 類型轉換
包裝類提供了類型轉換的方法,可以很方便的實現類型之間的轉換,比如 Integer 類型轉換代碼:
String age = "18"; int ageInt = Integer.parseInt(age) + 2; // 輸出結果:20 System.out.println(ageInt);5. 高頻區間的數據緩存
此特性為包裝類很重要的用途之一,用于高頻區間的數據緩存,以 Integer 為例來說,在數值區間為 -128~127 時,會直接復用已有對象,在這區間之外的數字才會在堆上產生。
我們使用 == 對 Integer 進行驗證,代碼如下:
public static void main(String[] args) {// Integer 高頻區緩存范圍 -128~127Integer num1 = 127;Integer num2 = 127;// Integer 取值 127 == 結果為 true(值127 num1==num2 => true)System.out.println("值127 num1==num2 => " + (num1 == num2));Integer num3 = 128;Integer num4 = 128;// Integer 取值 128 == 結果為 false(值128 num3==num4 => false)System.out.println("值128 num3==num4 => " + (num3 == num4));}從上面的代碼很明顯可以看出,Integer 為 127 時復用了已有對象,當值為 128 時,重新在堆上生成了新對象。
為什么會產生高頻區域數據緩存?我們查看源碼就能發現“線索”,源碼版本 JDK 8,源碼如下:
public static Integer valueOf(int i) {if (i >= IntegerCache.low && i <= IntegerCache.high)return IntegerCache.cache[i + (-IntegerCache.low)];return new Integer(i); }由此可見,高頻區域的數值會直接使用已有對象,非高頻區域的數值會重新 new 一個新的對象。
各包裝類高頻區域的取值范圍:
- Boolean:使用靜態 final 定義,就會返回靜態值;
- Byte:緩存區 -128~127,全部緩存;
- Short:緩存區 -128~127,部分緩存;
- Character:緩存區 0~127,部分緩存;
- Long:緩存區 -128~127,部分緩存;
- Integer:緩存區 -128~127,部分緩存。
包裝類的注意事項
int 的默認值是 0,而 Integer 的默認值是 null。
推薦所有包裝類對象之間的值比較使用 equlas() 方法,因為包裝類的非高頻區數據會在堆上產生,而高頻區又會復用已有對象,這樣會導致同樣的代碼,因為取值的不同,而產生兩種截然不同的結果。代碼示例:
Float 和 Double 不會有緩存,其他包裝類都有緩存。
Integer 是唯一一個可以修改緩存范圍的包裝類,在 VM optons 加入參數:
-XX:AutoBoxCacheMax=666 即修改緩存最大值為 666 。
示例代碼如下:
public static void main(String[] args) {Integer num1 = -128;Integer num2 = -128;System.out.println("值為-128 => " + (num1 == num2));Integer num3 = 666;Integer num4 = 666;System.out.println("值為666 => " + (num3 == num4));Integer num5 = 667;Integer num6 = 667;System.out.println("值為667 => " + (num5 == num6)); }執行結果如下:
值為-128 => true 值為666 => true 值為667 => false由此可見將 Integer 最大緩存修改為 666 之后,667 不會被緩存,而 -128~666 之間的數都被緩存了。
相關面試題
1. 以下 Integer 代碼輸出的結果是?
Integer age = 10; Integer age2 = 10; Integer age3 = 133; Integer age4 = 133; System.out.println((age == age2) + "," + (age3 == age4));答:true,false
2. 以下 Double 代碼輸出的結果是?
Double num = 10d; Double num2 = 10d; Double num3 = 133d; Double num4 = 133d; System.out.println((num == num2) + "," + (num3 == num4));答:false,false
3. 以下程序輸出結果是?
int i = 100; Integer j = new Integer(100); System.out.println(i == j); System.out.println(j.equals(i));A:true,true
B:true,false
C:false,true
D:false,false
答:A
題目分析:有人認為這和 Integer 高速緩存有關系,但你發現把值改為 10000 結果也是 true,true,這是因為 Integer 和 int 比較時,會自動拆箱為 int 相當于兩個 int 比較,值一定是 true,true。
4. 以下程序執行的結果是?
final int iMax = Integer.MAX_VALUE; System.out.println(iMax + 1);A:2147483648
B:-2147483648
C:程序報錯
D:以上都不是
答:B
題目解析:這是因為整數在內存中使用的是補碼的形式表示,最高位是符號位 0 表示正數,1 表示負數,當執行 +1 時,最高位就變成了 1,結果就成了 -2147483648。
5. 以下程序執行的結果是?
Set<Short> set = new HashSet<>(); for (short i = 0; i < 5; i++) {set.add(i);set.remove(i - 1); } System.out.println(set.size());A:1
B:0
C:5
D:以上都不是
答:5
題目解析:Short 類型 -1 之后轉換成了 Int 類型,remove() 的時候在集合中找不到 Int 類型的數據,所以就沒有刪除任何元素,執行的結果就是 5。
6. short s=2;s=s+1; 會報錯嗎?short s=2;s+=1; 會報錯嗎?
答:s=s+1 會報錯,s+=1 不會報錯,因為 s=s+1 會導致 short 類型升級為 int 類型,所以會報錯,而 s+=1 還是原來的 short 類型,所以不會報錯。
7. float f=3.4; 會報錯嗎?為什么?
答:會報錯,因為值 3.4 是 double 類型,float 類型級別小于 double 類型,所以會報錯。如下圖所示:
8. 為什么需要包裝類?
答:需要包裝類的原因有兩個。
① Java 的設計思想是萬物既對象,包裝類體現了面向對象的設計理念;
② 包裝類包含了很多屬性和方法,比基礎數據類型功能多,比如提供的獲取哈希值(hashCode)或獲取類(getClass)的方法等。
9. 基礎類 int 和包裝類 Integer,在 -128~127 之間都會復用已有的緩存對象,這種說法正確嗎?
答:不正確,只有包裝類高頻區域數據才有緩存。
10. 包裝類 Double 和 Integer 一樣都有高頻區域數據緩存,這種說法正確嗎?
答:不正確,基礎數據類型的包裝類只有 Double 和 Float 沒有高頻區域的緩存。
11. 包裝類的值比較要使用什么方法?
答:包裝類因為有高頻區域數據緩存,所以推薦使用 equals() 方法進行值比較。
12. 包裝類有哪些功能?
答:包裝類提供的功能有以下幾個。
- 功能豐富:包裝類包含了有 hashCode、getClass 、max、min 等方法;
- 可定義泛型類型參數:例如 List<Integer> list = new ArrayList<>(); ;
- 序列化:包裝類實現了 Serializable 接口,所以包裝類天然支持序列化和反序列化;
- 類型轉換:包裝類提供了方便的類型轉換方法,比如 Integer 的 parseInt() 方法;
- 高頻區域數據緩存:高頻區域可使用已有的緩存對象。
詳見正文“包裝類型”部分內容。
13. 泛型可以為基礎類型嗎?為什么?
答:泛型不能使用基礎數據類型。泛型在 JVM(Java虛擬機)編譯的時候會類型檫除,比如代碼 List<Integer> list 在 JVM 編譯的時候會轉換為 List list ,因為泛型是在 JDK 5 時提供的,而 JVM 的類型檫除是為了兼容以前代碼的一個折中方案,類型檫除之后就變成了 Object,而 Object 不能存儲基礎數據類型,但可以使用基礎數據類型對應的包裝類,所以像 List<int> list 這樣的代碼是不被允許的,編譯器階段會檢查報錯,而 List<Integer> list 是被允許的。
14. 選擇包裝類還是基礎類的原則有哪些?
答:我們知道正確的使用包裝類,可以提供程序的執行效率,可以使用已有的緩存,一般情況下選擇基本數據類型還是包裝類原則有以下幾個。
① 所有 POJO 類屬性必須使用包裝類;
② RPC 方法返回值和參數必須使用包裝類;
③ 所有局部變量推薦使用基礎數據類型。
15. 基礎數據類型在 JVM 中一定存儲在棧中嗎?為什么?
答:基礎數據類型不一定存儲在棧中,因為基礎類型的存儲位置取決于聲明的作用域,來看具體的解釋。
- 當基礎數據類型為局部變量的時候,比如在方法中聲明的變量,則存放在方法棧中的,當方法結束系統會釋放方法棧,在該方法中的變量也會隨著棧的銷毀而結束,這也是局部變量只能在方法中使用的原因;
- 當基礎數據類型為全局變量的時候,比如類中的聲明的變量,則存儲在堆上,因為全局變量不會隨著某個方法的執行結束而銷毀。
16. 以下程序執行的結果是?
Integer i1 = new Integer(10); Integer i2 = new Integer(10); Integer i3 = Integer.valueOf(10); Integer i4 = Integer.valueOf(10); System.out.println(i1 == i2); System.out.println(i2 == i3); System.out.println(i3 == i4);A:false,false,false
B:false,false,true
C:false,true,true
D:true,false,false
答:B
題目解析:new Integer(10) 每次都會創建一個新對象,Integer.valueOf(10) 則會使用緩存池中的對象。
17. 3*0.1==0.3 返回值是多少?
答:返回值為:false。
題目解析:因為有些浮點數不能完全精確的表示出來,如下代碼:
System.out.println(3 * 0.1);
返回的結果是:0.30000000000000004。
總結
以上是生活随笔為你收集整理的第 1-2 课:你不知道的基础数据类型和包装类 + 面试题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Redis 如何实现限流功能?
- 下一篇: ReactNative环境搭建扩展篇——