深入理解 Java 基本數據類型
📓 本文已歸檔到:「blog」
數據類型分類
Java 中的數據類型有兩類:
- 值類型(又叫內置數據類型,基本數據類型)
- 引用類型(除值類型以外,都是引用類型,包括
String
、數組)
值類型
Java 語言提供了 8 種基本類型,大致分為 4 類
- 整數型
byte
- 8 位。short
- 16 位。int
- 32 位。long
- 64 位,賦值時一般在數字后加上l
或L
。
- 浮點型
float
- 32 位,直接賦值時必須在數字后加上f
或F
。double
- 64 位,賦值時一般在數字后加d
或D
。
- 字符型
char
- 16 位,存儲 Unicode 碼,用單引號賦值。
- 布爾型
boolean
- 只有 true 和 false 兩個取值。
值類型和引用類型的區別
- 從概念方面來說
- 基本類型:變量名指向具體的數值。
- 引用類型:變量名指向存數據對象的內存地址。
- 從內存方面來說
- 基本類型:變量在聲明之后,Java 就會立刻分配給他內存空間。
- 引用類型:它以特殊的方式(類似 C 指針)向對象實體(具體的值),這類變量聲明時不會分配內存,只是存儲了一個內存地址。
- 從使用方面來說
- 基本類型:使用時需要賦具體值,判斷時使用
==
號。 - 引用類型:使用時可以賦 null,判斷時使用
equals
方法。
- 基本類型:使用時需要賦具體值,判斷時使用
👉 擴展閱讀:Java 基本數據類型和引用類型
這篇文章對於基本數據類型和引用類型的內存存儲講述比較生動。
數據轉換
Java 中,數據類型轉換有兩種方式:
- 自動換行
- 強制轉換
自動轉換
一般情況下,定義了某數據類型的變量,就不能再隨意轉換。但是 JAVA 允許用戶對基本類型做有限度的類型轉換。
如果符合以下條件,則 JAVA 將會自動做類型轉換:
-
由小數據轉換為大數據
顯而易見的是,“小”數據類型的數值表示范圍小於“大”數據類型的數值表示范圍,即精度小於“大”數據類型。
所以,如果“大”數據向“小”數據轉換,會丟失數據精度。比如:long 轉為 int,則超出 int 表示范圍的數據將會丟失,導致結果的不確定性。
反之,“小”數據向“大”數據轉換,則不會存在數據丟失情況。由於這個原因,這種類型轉換也稱為擴大轉換。
這些類型由“小”到“大”分別為:(byte,short,char) < int < long < float < double。
這里我們所說的“大”與“小”,並不是指占用字節的多少,而是指表示值的范圍的大小。
-
轉換前后的數據類型要兼容
由於 boolean 類型只能存放 true 或 false,這與整數或字符是不兼容的,因此不可以做類型轉換。
-
整型類型和浮點型進行計算后,結果會轉為浮點類型
示例:
long x = 30;
float y = 14.3f;
System.out.println("x/y = " + x/y);
輸出:
x/y = 1.9607843
可見 long 雖然精度大於 float 類型,但是結果為浮點數類型。
強制轉換
在不符合自動轉換條件時或者根據用戶的需要,可以對數據類型做強制的轉換。
強制轉換使用括號 ()
。
引用類型也可以使用強制轉換。
示例:
float f = 25.5f;
int x = (int)f;
System.out.println("x = " + x);
裝箱和拆箱
包裝類、裝箱、拆箱
Java 中為每一種基本數據類型提供了相應的包裝類,如下:
Byte <-> byte
Short <-> short
Integer <-> int
Long <-> long
Float <-> float
Double <-> double
Character <-> char
Boolean <-> boolean
引入包裝類的目的就是:提供一種機制,使得基本數據類型可以與引用類型互相轉換。
基本數據類型與包裝類的轉換被稱為裝箱
和拆箱
。
裝箱
(boxing)是將值類型轉換為引用類型。例如:int
轉Integer
- 裝箱過程是通過調用包裝類的
valueOf
方法實現的。
- 裝箱過程是通過調用包裝類的
拆箱
(unboxing)是將引用類型轉換為值類型。例如:Integer
轉int
- 拆箱過程是通過調用包裝類的
xxxValue
方法實現的。(xxx 代表對應的基本數據類型)。
- 拆箱過程是通過調用包裝類的
自動裝箱、自動拆箱
基本數據(Primitive)型的自動裝箱(boxing)拆箱(unboxing)自 JDK 5 開始提供的功能。
JDK 5 之前的形式:
Integer i1 = new Integer(10); // 非自動裝箱
JDK 5 之后:
Integer i2 = 10; // 自動裝箱
Java 對於自動裝箱和拆箱的設計,依賴於一種叫做享元模式的設計模式(有興趣的朋友可以去了解一下源碼,這里不對設計模式展開詳述)。
👉 擴展閱讀:深入剖析 Java 中的裝箱和拆箱
結合示例,一步步闡述裝箱和拆箱原理。
裝箱、拆箱的應用和注意點
裝箱、拆箱應用場景
- 一種最普通的場景是:調用一個含類型為
Object
參數的方法,該Object
可支持任意類型(因為Object
是所有類的父類),以便通用。當你需要將一個值類型(如 int)傳入時,需要使用Integer
裝箱。 - 另一種用法是:一個非泛型的容器,同樣是為了保證通用,而將元素類型定義為
Object
。於是,要將值類型數據加入容器時,需要裝箱。 - 當
==
運算符的兩個操作,一個操作數是包裝類,另一個操作數是表達式(即包含算術運算)則比較的是數值(即會觸發自動拆箱的過程)。
示例:
Integer i1 = 10; // 自動裝箱
Integer i2 = new Integer(10); // 非自動裝箱
Integer i3 = Integer.valueOf(10); // 非自動裝箱
int i4 = new Integer(10); // 自動拆箱
int i5 = i2.intValue(); // 非自動拆箱
System.out.println("i1 = [" + i1 + "]");
System.out.println("i2 = [" + i2 + "]");
System.out.println("i3 = [" + i3 + "]");
System.out.println("i4 = [" + i4 + "]");
System.out.println("i5 = [" + i5 + "]");
System.out.println("i1 == i2 is [" + (i1 == i2) + "]");
System.out.println("i1 == i4 is [" + (i1 == i4) + "]"); // 自動拆箱
// Output:
// i1 = [10]
// i2 = [10]
// i3 = [10]
// i4 = [10]
// i5 = [10]
// i1 == i2 is [false]
// i1 == i4 is [true]
示例說明:
上面的例子,雖然簡單,但卻隱藏了自動裝箱、拆箱和非自動裝箱、拆箱的應用。從例子中可以看到,明明所有變量都初始化為數值 10 了,但為何會出現
i1 == i2 is [false
而i1 == i4 is [true]
?原因在於:
- i1、i2 都是包裝類,使用
==
時,Java 將它們當做兩個對象,而非兩個 int 值來比較,所以兩個對象自然是不相等的。正確的比較操作應該使用equals
方法。- i1 是包裝類,i4 是基礎數據類型,使用
==
時,Java 會將兩個 i1 這個包裝類對象自動拆箱為一個int
值,再代入到==
運算表達式中計算;最終,相當於兩個int
進行比較,由於值相同,所以結果相等。
裝箱、拆箱應用注意點
- 裝箱操作會創建對象,頻繁的裝箱操作會造成不必要的內存消耗,影響性能。所以應該盡量避免裝箱。
- 基礎數據類型的比較操作使用
==
,包裝類的比較操作使用equals
方法。
小結
(1)Java 中的數據類型有兩類:
- 值類型(
byte
、short
、int
、long
、float
、double
、char
、boolean
) - 引用類型(除值類型以外,都是引用類型,包括
String
、數組)
(2)Java 中,數據類型轉換有兩種方式:
- 自動換行
- 強制轉換
強制轉換使用括號 ()
。
基礎數據類型可以自動轉換,轉換原則如下:
- 由小數據轉換為大數據
- 轉換前后的數據類型要兼容
- 整型類型和浮點型進行計算后,結果會轉為浮點類型
(3)包裝類有如下種類:
Byte <-> byte
Short <-> short
Integer <-> int
Long <-> long
Float <-> float
Double <-> double
Character <-> char
Boolean <-> boolean
(4)什么是裝箱、拆箱
裝箱
(boxing)是將值類型轉換為引用類型。例如:int
轉Integer
- 裝箱過程是通過調用包裝類的
valueOf
方法實現的。
- 裝箱過程是通過調用包裝類的
拆箱
(unboxing)是將引用類型轉換為值類型。例如:Integer
轉int
- 拆箱過程是通過調用包裝類的
xxxValue
方法實現的。(xxx 代表對應的基本數據類型)。
- 拆箱過程是通過調用包裝類的
(5)裝箱、拆箱的應用場景
- 含類型為
Object
參數的方法 - 非泛型的容器
- 當
==
運算符的兩個操作,一個操作數是包裝類,另一個操作數是表達式(即包含算術運算)則比較的是數值(即會觸發自動拆箱的過程)。
(6)裝箱、拆箱的應用注意點
- 裝箱操作會創建對象,頻繁的裝箱操作會造成不必要的內存消耗,影響性能。所以應該盡量避免裝箱。
- 基礎數據類型的比較操作使用
==
,包裝類的比較操作使用equals
方法。