柚子快報激活碼778899分享:Java 數據結構之初始泛型
柚子快報激活碼778899分享:Java 數據結構之初始泛型
找往期文章包括但不限于本期文章中不懂的知識點:
個人主頁:我要學編程(?_?)-CSDN博客
所屬專欄:數據結構(Java版)
目錄
深入了解包裝類?
包裝類的由來
裝箱與拆箱?
面試題?
泛型? ? ? ? ? ??
泛型的語法與使用
泛型如何編譯的?
泛型的上界
泛型方法?
泛型占位符
深入了解包裝類?
我們在最開始學習Java的數據類型時,就知道了Java的八大基本數據類型有自己對應的包裝類,也就是引用類型。今天,我們就來徹底了解它們。
包裝類的由來
在Java中,由于基本類型不是繼承自Object類,為了在泛型代碼中可以支持基本類型,Java給每個基本類型都創(chuàng)造了對應的一個包裝類型。如下:
基本類型與其對應的包裝類 類型
基本數據類型包裝類 類型byteBytechar Character shortShortintIntegerlongLongfloatFloatdoubleDoublebooleanBoolean
裝箱與拆箱?
裝箱也叫作:裝包。就是把基本數據類型轉換成其對應的包裝類 類型。
例如:
public class Test {
public static void main(String[] args) {
Integer a = 10;
Integer c = new Integer(10);
Integer b = Integer.valueOf(10);
System.out.println(a);
System.out.println(b);
System.out.println(c);
}
}
上面三種寫法,都是裝箱的操作,即把基本數據類型轉換成其對應的包裝類 類型。但要注意的是第二種方法,雖然代碼可以正常執(zhí)行,但我們現在不再使用這種方法了。從Java 9開始,這個方法就已經被摒棄了。下面是Java 8 和 Java 17的不同情況:
Java 8:
Java 17:?
需要注意的是:這里爆紅,但還是可以運行通過的。?
拆箱也叫作:拆包。就是把包裝類 類型轉換成其對應的基本數據類型。
例如:
public class Test {
public static void main(String[] args) {
Integer integer = 10;
int a = integer;
int b = integer.intValue();
System.out.println(a);
System.out.println(b);
}
}
注意:
當我們顯式地去調用方法來進行裝箱或者拆箱操作時,這種方式叫做:顯式裝箱(拆箱)。
當我們直接把基本數據類型轉換為其對應的包裝類 類型或者進行這種拆箱操作時,這就叫做:自動裝箱(拆箱)。
public class Test {
public static void main(String[] args) {
Integer a = 10; // 自動裝箱
Integer b = Integer.valueOf(10); // 顯式裝箱
int c = a; // 自動拆箱
int d = b.intValue(); // 顯式拆箱
}
}
面試題?
public class Test {
public static void main(String[] args) {
Integer a = 100;
Integer b = 100;
System.out.println(a == b); // 結果是 true
Integer c = 200;
Integer d = 200;
System.out.println(c == d); // 結果是 false
}
}
這里同樣都是自動裝箱,為什么輸出的結果會不一樣呢?
分析:a b c d?在這里都是一個引用類型,那么在用 == 比較時,比較的是它們各自在堆區(qū)的地址。這也就說明 a 和 b在堆區(qū)是同一塊地址,但是c 和 d 在堆區(qū)不是同一塊地址。
我們就可以去看這個裝箱的源碼,看看到底做了什么??
可以看到當裝箱的 i 的值在 [low, high] 之間的時候,返回的是一個數組所對應的下標的值,而當 i 不在這個范圍內時,返回的是一個新的對象。因此,我們可以得出結論了:100在這個范圍內,200不在這個范圍內。我們還是可以看一個這個源碼對應的 low 和?high 的值的。
low 對應的值是 -128,high 對應的值是 127。?
根據條件得出這個數組大致是這樣的。因此 當 i = 100時,返回的是在數組中的同一份;而 i? = 200時,返回的是 new 的一個新對象。?
泛型? ? ? ? ? ??
包裝類的出現就是為泛型服務的。那么什么是泛型呢?顧名思義:就是一個廣泛的類型。泛型的出現是為了解決掉:一個類或者方法只能解決對應類型的問題。
例如:對整型數據排序,就只能用整型數組類解決。
class Myarray {
public static void bubble_sort (int[] array) {
// 趟數
for (int i = 0; i < array.length; i++) {
boolean flag = true;
// 每一趟要比較的內容
for (int j = 0; j < array.length-i-1; j++) {
if (array[j] > array[j+1]) {
int tmp = array[j];
array[j] = array[j+1];
array[j+1] = tmp;
flag = false;
}
}
if (flag) {
break;
}
}
}
}
public class Test {
public static void main(String[] args) {
int[] array = {10,9,8,7,6,5,4,3,2,1};
System.out.println("排序前:"+ Arrays.toString(array));
Myarray.bubble_sort(array);
System.out.println("排序后:"+ Arrays.toString(array));
}
}
從排序的結果來看:這個排序的功能是正確的。但是也只局限于排序整型數據,不能排序其他類型的數據,如果要排序的話,還得重新寫一個這樣的方法。于是就出現了泛型。?所以,泛型的主要目的:就是指定當前的容器,要持有什么類型的對象。讓編譯器去做檢查。
泛型的語法與使用
class 泛型類名稱<類型形參列表> {
// 這里可以使用類型參數
}
?這里的參數列表可以不只有一種。就像下面這樣:
class 泛型類名稱
}
這里的T代表的是占位符,表示當前類是一個泛型類。
類型形參一般使用一個大寫字母表示,常用的名稱有:
E 表示 Elements? ? ? ? ? ? ? ?K 表示 Key? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?V? 表示 Value?
N 表示 Number? ? ? ? ? ? ? ? ?T 表示 Type? ? ? ? ? ? ? ? S, U, V 等等 - 第二、第三、第四個類型
至于這些占位符的區(qū)別以及各自之間的含義,我們待會再學習。我們可以用一個數組來接收多種不同的元素了。
class MyArray
// 語法規(guī)定不能創(chuàng)建泛型數組,但Object可以擁有接收所有類的功能
public Object[] objects;
public MyArray(){
this.objects = new Object[10];
}
public T getValue(int pos) {
// 取出來的是一個Object類,得強轉成 T
return (T)objects[pos];
}
public void setValue(int pos, T value) {
// 這里不考慮pos的無效,注重這里的思想
objects[pos] = value;
}
}
public class Test {
public static void main(String[] args) {
MyArray
myarray.setValue(0, 10);
myarray.setValue(1, 20);
// 這里可寫可不寫
MyArray
myArray.setValue(0, "Hello");
myArray.setValue(1, "World");
}
}
這里就實現了同一個類,但是可以接收不同的類型,實現了類型的參數化。這就是泛型的意義。
注意:
1. 泛型只能接受類,所有的基本數據類型必須使用包裝類!?
2. 編譯器會根據我們在實例化一個對象的時候來判斷這個T到底是什么類型。
3. 我們也可能會遇到有的代碼在使用泛型類時,沒有標明具體的類型,但是還是可以運行通過。這是因為泛型這個概念是在 java 5之后提出來的。之前的 java 版本并沒有這種寫法,所以為了兼容老版本,泛型類在實例化時,會有未標明具體類型的情況,這種叫做裸類型。但是我們最好不要寫這種代碼出來。
泛型如何編譯的?
泛型是只存在于編譯時期的名詞,因為在編譯過后不存在T、E等泛型占位符了。泛型的占位符在編譯之后就被替換成了Object。這種機制被稱為“擦除機制”?。將占位符擦除成Object。
泛型的上界
?如果我們想要限制泛型的傳過來的種類也是可以的。
在定義泛型類時,有時需要對傳入的類型變量做一定的約束(就像上面那樣),可以通過類型邊界來約束。?
?語法:
class 泛型類名稱<類型形參 extends 類型邊界> {
...
}
注意:其實所有的泛型類都有一個上界:Object。?
泛型方法?
語法:
方法限定符 <類型形參列表> 返回值類型 方法名稱(形參列表) {
...
}
例如:
// 規(guī)定這個方法是叫把pos位置的值置為value
public static
array[pos] = value;
}
注意:只有靜態(tài)的泛型方法里面才能有泛型的出現,普通的靜態(tài)方法不能有泛型的出現。
這句話不是說只有靜態(tài)方法才能是泛型方法,普通方法也可以是泛型方法。只是說不是泛型的方法里面如果沒有實例化泛型對象,就不能出現任何與泛型有關的東西。
因此,上面的排序就可以用泛型方法來解決。但是會有一個新的問題:Comparable 與 這個數組會出現不兼容的情況,因為這個數組是基本數據類型,沒有實現接口這一說法。所以得把這個數組變成包裝類,但是在傳參的過程中,T會被擦除成Object類,因此即使我們傳過去的參數強轉成T[ ],也會發(fā)生類型轉換異常。
因此這個排序最終是失敗了。所以我就沒有把代碼傳上來。但是泛型還是有很大的好處的:可以讓一份代碼對不同的對象執(zhí)行相同的操作。?
類型推導?
class Myarray
// ......
}
public class Test {
public static void main(String[] args) {
// 通過這個 Integer 來推導出 Myarray 中的泛型
Myarray
}
}
上面是泛型類的推導。下面是泛型方法的推導?
?
class Myarray {
public
// ......
}
}
public class Test {
public static void main(String[] args) {
Myarray myarray = new Myarray();
// 通過這個 Integer 來推斷這個 T(也可以不寫,編譯器會根據其具體操作來判斷)
myarray.
}
}
泛型占位符
接下來就學習泛型占位符的知識:
在Java泛型中,像
T - 通常代表 Type,是最常見的泛型占位符,用于表示任何類型。在沒有特定上下文暗示的情況下,泛型類或方法常使用 T。 E - 通常代表 Element,特別在集合框架中使用較多,暗示它代表集合中的元素類型,如 List
使用這些占位符主要是為了提高代碼的可讀性和自文檔化能力。開發(fā)者可以根據上下文選擇最合適的占位符來表達意圖,但最終這些占位符都會被編譯器替換為具體的類型信息,不會影響到生成的字節(jié)碼或運行時行為。
此外,泛型中還有一個特殊的占位符 ?(問號),它作為通配符使用,表示未知的類型,可以有三種形式:無界通配符(?)、上界通配符(? extends SomeType)和下界通配符(? super SomeType),用來實現更靈活的泛型參數約束。(了解即可)
這里基本就是java泛型語法的全部內容啦!通過對泛型的學習,我們就可以讓代碼變得更加高大上一些。
好啦!本期?數據結構之初始泛型 的學習之旅就到此結束了!相信通過這篇文章的學習,你對Java中泛型的了解將會更進一步!我們下一期再一起學習吧!
柚子快報激活碼778899分享:Java 數據結構之初始泛型
精彩文章
本文內容根據網絡資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點和立場。
轉載請注明,如有侵權,聯系刪除。