一、包裝類
1、什么是包裝類
? ? ? ? 將基礎類型包裝成的類就是包裝類。由于基礎類型不是繼承 Object 類的類,所以在泛型不能直接支持基礎類型,為了解決這個問題,就需要把基礎類型轉換為對應的包裝類。
基礎類型 | 包裝類 |
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
2、裝箱和拆箱
裝箱:基礎類型轉換為包裝類。
拆箱:包裝類轉換為基礎類型。
以下為過時的寫法:
3、自動裝箱和拆箱
????????我們現在用的都是自動裝箱、拆箱,第一種方式是最好的。
????????用 javap -v 命令反匯編字節碼文件,可以看到編譯器會自動添加 valueOf (裝箱)和 intValue(拆箱) 方法。
4、包裝類的常量緩沖池
? ? ? ? 同是比較有相同內容的對象的地址,結果卻不同:
? ? ? ? 這是因為 Integer 有一個常量池機制,當加載 Integer 類時,就會自動把 -128 ~ 127 范圍的對象(常用的數值)放到常量池中:
????????因此,i8、i9 引用都是存放的常量池中 127 對象的地址,而 i10、i11 引用分別存放不同的堆中新創建的對象的地址:
注:128 并沒有超過 Integer 的取值范圍,它的范圍大概是 -21億 ~ 21 億。
包裝類 | 緩沖值范圍 |
Byte | -128 ~ 127 |
Short | -128 ~ 127 |
Integer | -128 ~ 127 |
Long | -128 ~ 127 |
Float | 無 |
Double | 無 |
Character | 0 ~ 127 |
Boolean | true 和 false |
二、泛型
1、什么是泛型
? ? ? ? 一般的類和方法只能固定接收、返回一種類型的數據,但有時我們希望接收、返回很多種類型的數據。比如寫一個加法器,里面的數據可以是整型、浮點型,如果使用方法重載,方法里的步驟相同,只是數據類型不同,造成代碼的冗余。使用泛型可以解決這個問題,相當于將數據類型參數化,可以給同一個類或方法靈活地指定任意類型。
2、使用 Object 類
? ? ? ? 如果我們需要實現一個數組類,這個類中的數組成員可以存放任意類型的元素,并且有 set、get 指定位置的元素的方法。可以使用 Object 類,因為它是任意類的父親,通過任意類型向上轉型為 Object 類實現:
?
? ? ? ? 但是這種方法有個缺點,一是獲取值時需要手動強制類型轉換;二是無法確定一種類型,即讓類或方法在使用時持有一種類型,而不是同時擁有任意類型。而泛型,不需要手動強制類型轉換,并且在創建對象時就將某一種類型作為參數傳入,指定其類型,編譯器對其進行類型檢查。既能讓類指定不同的類型,又能給對象指定一種類型。
3、泛型的使用
? ? ? ? 將上面的類改寫成泛型:
- ?<T> 表示該類為泛型類,類型形參常用的名稱有:
- MyArray 中仍用 Object[] 類型創建對象,因為 T 只是泛型類的標志,它并沒有實際的構造函數。
- 在定義引用時,已確定為 String 類,?因此 new 對象時可根據上下文推導類型,省略為 <> 。
- 優點:編譯器自動進行類型轉換、類型檢查;一份代碼支持多種類型。
4、裸類型
? ? ? ? 泛型參數不指定,默認 T 為 Object,它只是為了兼容老版本,不建議使用:
? ? ? ? 我們學習泛型,重點不在定義泛型類,而在泛型類的實例化,后續使用 java 中的集合類時,會經常用到泛型的實例化。
5、如何編譯泛型——擦除機制
? ? ? ? 編譯器先將泛型類型擦除,再替換為 Object 或邊界類型。set 中把指定類型轉為 Object (向上轉型),get 中強制轉換 Object 為指定類型(針對泛型類)。
? ? ? ? 最后針對子類重寫泛型父類的方法,避免沒有真正覆蓋父類方法,編譯器會自動生成橋接方法。
擦除前:
public class Node<T> {T data;public void setData(T data) {this.data = data;}
}
public class StringNode extends Node<String> {@Overridepublic void setData(String data) {super.setData(data);}
}
擦除后:
public class Node {Object data;public void setData(Object data) {this.data = data;}
}public class StringNode extends Node {// 形參中,子類的 String 與父類替換后的 Object 不一致// 未成功覆蓋@Overridepublic void setData(String data) { super.setData(data);}
}
自動生成橋接方法:
public class StringNode extends Node {// 子類與父類的方法簽名一致@Overridepublic void setData(Object data) {setData((String) data);}
}
6、泛型上界
? ? ? ? 對泛型的類型范圍的上界進行約束:
? ? ? ? 在實現計算器時,使用泛型上界,可以限制類型為數字。若沒有 extends,默認為 extends Object。
7、泛型方法
? ? ? ? 將泛型寫到方法名后,返回值類型前,讓返回值類型、參數列表都能使用該泛型:
? ? ? ? 泛型方法的使用,類型推導見上文 “泛型的使用” :
8、通配符
? ? ? ? 使泛型類引用能接收所有類型的泛型類對象:
三、總結
? ? ? ? 泛型的重點在泛型類對象怎么實例化,為后續集合類的使用做鋪墊。而怎么定義泛型類、怎么定義泛型方法、擦除機制、泛型上界、通配符等相比較下就不那么重要了。