目錄
1. 包裝類
1.1 基本數據類型和對應的包裝類
1.2 (自動)裝箱和(自動)拆箱
1.2.1 裝箱與拆箱
1.2.2 自動(顯式)裝箱與自動(顯式)拆箱
1.3 valueOf()方法
2. 泛型類
2.1 泛型類與Object類
2.2 泛型類語法
2.3 泛型類示例
2.4 裸類型(Raw Type)
2.5 泛型類的編譯
2.5.1 擦除機制
2.5.2 泛型類型數組的實例化
2.6 泛型的上界
2.6.1? N為接口
?2.6.2?? Number為類
3. 泛型方法
3.1 語法
3.2 泛型方法示例
3.2.1 普通類的普通泛型方法
3.2.2 普通類的靜態泛型方法
1. 包裝類
在Java中由于基本類型不是繼承自Object,為了在泛型代碼中可以支持基本類型,Java給每個基本類型都對應了一個包裝類型;
1.1 基本數據類型和對應的包裝類
基本數據類型 | 包裝類 |
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
除了Integer和Character,其余基本類型的包裝類都是首字母大寫;
1.2 (自動)裝箱和(自動)拆箱
1.2.1 裝箱與拆箱
裝箱(裝包):把基本數據類型變為對應的包裝類型;
拆箱(拆包):把引用類型變為基本數據類型;
1.2.2 自動(顯式)裝箱與自動(顯式)拆箱
代碼示例1:自動裝箱與顯式裝箱:
int a = 10;// 自動裝箱Integer val = a;System.out.println(val);// 顯式裝箱Integer val2 = Integer.valueOf(a);Integer val3 = new Integer(a);System.out.println(val2);System.out.println(val3);
?代碼示例2:自動拆箱:
Integer val1 = 10;// 自動拆箱int a = val1;System.out.println(a);// 顯式拆箱// 拆為int類型int a2 = val1.intValue();System.out.println(a2);// 拆為double類型double a3 = val1.doubleValue();System.out.println(a3);
?注:(1)自動裝箱的底層邏輯:Integer包裝類的valueOf()方法:?
(2)自動拆箱的底層邏輯:Integer包裝類的intValue()方法:
(3)顯式拆箱時,Integer包裝類不止提供了intValue()方法,還提供了doubleValue()等方法:
可以根據所需的基本類型對應調用其方法,在上文示例中,輸出結果為:
1.3 valueOf()方法
試運行以下代碼:
Integer a = 127;Integer b = 127;System.out.println(a==b);Integer c = 128;Integer d = 128;System.out.println(c==d);
輸出結果為:
原理注解:
(1)Integer包裝類的valueOf方法源代碼與方法內部相關量的值:
(2)方法含義:
如果i在[low, high]即[-128, 127]之間,則返回cache緩存數組中下標為i+128位置的元素:
如i=0時存儲在cache[128],i=-128時則存儲在cache[0],i=127時則存儲在cache[255];
即在緩存數組中存儲了256個數字;
在該范圍內的i調用valueOf方法調用到的是同一個元素,故而a=b;
在該范圍外的i調用valueOf方法會new Integer對象,作為引用類型,==判斷的是引用類型是否是對一個對象,故而a!=b;
2. 泛型類
一般的類和方法只能使用具體的類型:基本類型或自定義類,泛型就是適用于多種類型,即對參數實現了參數化;
2.1 泛型類與Object類
現要求實現一個不限制元素種類的數組,聯想到Object類,示例代碼如下:
class MyArray{public Object[] obj = new Object[3];public Object getPos(int pos){return obj[pos];}public void setPos(int pos,Object val){obj[pos]=val;}
}
public class Demo1 {public static void main(String[] args) {MyArray myArray = new MyArray();myArray.setPos(0,10);myArray.setPos(1,"hello");myArray.setPos(2,10.0);double ret = myArray.getPos(2);System.out.println(ret);}
}
報錯如下:
?如要創建double類型變量對數組元素進行接收,則需要進行強轉:
double ret = (double)myArray.getPos(2);
上文設計的數組具有以下特點:
① 任何數據都可以存儲; ② 獲取數據時必須進行強制類型轉換;
這并非我們需要實現的泛型數組,通常需要實現的泛型數組需求是:存儲一種類型的數據;
泛型編程是將類型作為參數傳遞的編程方式,目的是指定當前容器后可以存放某一種類型的數據;
2.2 泛型類語法
// 泛型的一般使用方法
class 泛型類名稱<類型形參列表>{
}
class ClassName<T1,T2...Tn>{
}// 泛型類的繼承
class 泛型類名稱<類型形參列表> extends 繼承類{
}
class ClassName<T1,T2,...Tn> extends ParentClass<T1>{
}
注:① 類名后的<T>代表占位符,表示當前類是一個泛型類,
類型形參一般使用一個大寫字母表示,常用名稱有:
E表示Element,K表示Key,V表示Value,N表示Number,T表示Type;
② 泛型當中不能實例化一個泛型類型的數組,如:
T[] ts = new T[5];
是錯誤的;
③ 泛型類實例化的同時會指定當前泛型類的指數參數類型,如果二者沖突,就會報錯,
這也是泛型編程的一個意義所在:泛型編程在程序編譯時,存儲數據時自動進行類型檢查;?
2.3 泛型類示例
class MyArray<T>{public T[] obj = (T[])new Object[3];public T getPos(int pos){return obj[pos];}public void setObj(int pos, T val){obj[pos]=val;}
}
public class Demo1 {public static void main(String[] args) {// 實例化對象的同時指定當前泛型類的指定參數類型// 指定參數的類型必須是引用類型,如int非法而Integer合法MyArray<Integer> myArray1 = new MyArray<Integer>();myArray1.setObj(0,10);myArray1.setObj(1,78);myArray1.setObj(2,66);double ret1= myArray1.getPos(1);System.out.println(ret1);System.out.println("-------------------");MyArray<String> myArray2 = new MyArray<String>();myArray2.setObj(0,"hello");myArray2.setObj(1,"world");myArray2.setObj(2,"java");String ret2 = myArray2.getPos(1);System.out.println(ret2);}
}
輸出結果為:
2.4 裸類型(Raw Type)
裸類型是一個泛型類但沒有帶類型實參,例如:
class MyArray<T>{public T[] obj = (T[])new Object[3];public T getPos(int pos){return obj[pos];}public void setObj(int pos, T val){obj[pos]=val;}
}
public class Demo1 {public static void main(String[] args) {MyArray list = new MyArray();}
}
但請注意:裸類型是為了兼容老版本的API,在編程時請避免裸類型的使用;
2.5 泛型類的編譯
2.5.1 擦除機制
class MyArray<T>{public T[] obj = (T[])new Object[3];public T getPos(int pos){return obj[pos];}public void setObj(int pos, T val){obj[pos]=val;}
}
public class Demo1 {public static void main(String[] args) {MyArray<Integer> myArray = new MyArray<Integer>();myArray.setObj(0,85);int ret = myArray.getPos(0);System.out.println(ret);}
}
查看上文代碼的字節碼文件:
在編譯完成后,運行過程中是沒有泛型的概念的,此時所有的T都被擦除為Object,通過上述的編譯器生成的字節碼文件中是不包含泛型的類型信息的;
2.5.2 泛型類型數組的實例化
java語法規定:在實例化數組時必須提供具體的元素類型,故而不能直接實例化泛型類型數組;
方法1:(上文擦除機制部分示例代碼的在泛型類中創建數組的方法):
class MyArray<E>{public E[] obj = (E[]) new Object[3];
}
但該種寫法存在問題:試運行以下代碼:
class MyArray<E>{public E[] obj = (E[]) new Object[3];public E getPos(int pos){return obj[pos];}public void setObj(int pos,E val){obj[pos]=val;}// 返回數組的方法public E[] getArray(){return obj;}
}
public class Demo1 {public static void main(String[] args) {MyArray<Integer> myArray = new MyArray<Integer>();myArray.setObj(0,10);myArray.setObj(1,85);Integer[] integers = myArray.getArray();}
}
報錯及分析如下:
在繼承與多態章節已經提到過,向下轉型是不安全的;
java的數組實現非常特殊,
此處可以理解為:jvm認為使用一個固定類型如Integer或String等等類型的對象來接收Object類型對象是不安全的;
正確代碼為:(使用反射)
class MyArray<E>{public E[] obj;public MyArray(Class<E>clazz, int capacity){// 參數為數組元素類型與容量obj = (E[])Array.newInstance(clazz, capacity);}public E getPos(int pos){return obj[pos];}public void setObj(int pos, E val){obj[pos]=val;}public E[] getArray(){return obj;}
}
public class Demo1 {public static void main(String[] args) {MyArray<Integer> myArray = new MyArray<>(Integer.class,10);myArray.setObj(0,1997);myArray.setObj(1,85);Integer[] integers = myArray.getArray();}
}
但以上方法并不常用,更推薦方法2:;?
繼承與多態文章鏈接如下:
CSDN
方法2:參考java的ArrayList源碼的get方法:
對于泛型類型數組的實例化,不能直接使用泛型類型作為數組元素的類型,故而可以使用Object類結合強制類型轉換實現泛型類型數組的實例化:
class MyArray<E>{public Object[] obj = new Object[3];public E getPos(int pos){return (E)obj[pos];}public void setPos(int pos, Object val){obj[pos] = val;}
}
2.6 泛型的上界
2.6.1? <E extends N> N為接口
表示實例化對象時指定的參數類型一定已經實現了N接口;
// 寫一個泛型類求一個數組的最大值
class Alg<E extends Comparable<E>>{public E findMax(E[] array){E max=array[0];for(int i=0;i<array.length;i++){if(max.compareTo(array[i])<0){max = array[i];}}return max;}
}
public class Demo2 {public static void main(String[] args) {Alg<Integer> alg = new Alg<Integer>();Integer[] array = {1,4,2,10,9,8,17,5};System.out.println("Array is:");for(Integer x:array){System.out.print(x+" ");}System.out.println();Integer val = alg.findMax(array);System.out.println("The max element in the array is "+val);}
}
輸出結果為:
???
?2.6.2??<E extends Number> Number為類
表示E是Number的子類或E就是Number本身;
參考Number (Java Platform SE 8 ) (oracle.com)
?Integer、Double、Long、Short等等都是Number常見的直接子類:
class A<E extends Number>{A<Number> a1 = new A<>();A<Integer> a2 = new A<>();A<Double> a3 = new A<>();// A<String> a4 = new A<>();
}
3. 泛型方法
3.1 語法
方法限定符 <類型形參列表> 返回值類型 方法名稱(形參列表){...}
3.2 泛型方法示例
3.2.1 普通類的普通泛型方法
class Alg2{public<E extends Comparable> E findMax(E[] array){E max = array[0];for(int i=0;i<array.length;i++){if(max.compareTo(array[i])<1){max=array[i];}}return max;}
}
public class Demo4 {public static void main(String[] args) {Alg2 alg2 = new Alg2();Integer[] arr = {19,9,7,85,25};System.out.println("Array is: ");for(Integer x:arr){System.out.print(x+" ");}System.out.println();Integer val = alg2.<Integer>findMax(arr);System.out.println("The max element in the array is "+val);}
}
輸出結果為:
?注:調用泛型方法時泛型類型可以省略:
Integer val = alg2.<Integer>findMax(arr);
?上行代碼可以簡寫為:
Integer val = alg2.findMax(arr);
編譯器會根據傳遞給泛型方法的參數arr的類型判斷E的類型;
3.2.2 普通類的靜態泛型方法
class Alg2{public static<E extends Comparable> E findMax(E[] array){E max = array[0];for(int i=0;i<array.length;i++){if(max.compareTo(array[i])<1){max=array[i];}}return max;}
}
public class Demo4 {public static void main(String[] args) {Integer[] arr2 = {19,9,7,85,25};System.out.println("Array is: ");for(Integer x:arr2){System.out.print(x+" ");}System.out.println();Integer val = Alg2.findMax(arr2);System.out.println("The max element in the array is "+val);}
}
輸出結果為:
靜態泛型方法不再依賴于類的對象存在,可以使用類名直接調用;