前言: 這是我最一年學習java的一部分的回顧總結
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 裝箱和拆箱
int i = 10;
// 裝箱操作,新建一個 Integer 類型對象,將 i 的值放入對象的某個屬性中
Integer ii = Integer.valueOf(i);
Integer ij = new Integer(i);
// 拆箱操作,將 Integer 對象中的值取出,放到一個基本數據類型中
int j = ii.intValue();
裝箱(Boxing):是將基本數據類型轉換為對應的包裝類對象的過程。
拆箱(Unboxing):則是將包裝類對象轉換為對應的基本數據類型的過程。
注意: 裝箱和拆箱在 Java 中自動進行,這為開發者提供了便利,但也需要注意一些性能和空指針異常的問題。例如,如果一個包裝類對象為 null ,在進行拆箱操作時就會拋出空指針異常。
1.3 自動裝箱和自動拆箱
從上面裝箱拆箱使用過程中,可以發現有不少的代碼量,為了減少開發者的負擔,java提供的自動機制
int i = 10;
Integer ii = i; // 自動裝箱
Integer ij = (Integer)i; // 自動裝箱
int j = ii; // 自動拆箱
int k = (int)ii; // 自動拆箱
思考:
下面代碼輸出的是什么,為什么?
public static void main(String[] args) {
Integer a = 127;
Integer b = 127;
Integer c = 128;
Integer d = 128;
System.out.println(a == b);
System.out.println(c == d);
}
我們先看結果
a == b是true
c == d是false
進入debug可以發現
a和b對象的地址是一樣的
c和d對象的地址是不一樣的
原因:
在 Java 中,對于值在 -128
到 127
之間的 Integer
對象,會進行緩存。當使用 Integer.valueOf()
方法創建對象時,如果值在這個范圍內,會直接從緩存中獲取已有的對象,所以 a
和 b
指向的是同一個對象,a == b
結果為 true
。
而對于值不在 -128
到 127
之間的 Integer
對象,每次使用 Integer.valueOf()
方法創建都會生成新的對象。所以 c
和 d
是兩個不同的對象,c == d
結果為 false
。
2.泛型
2.1什么是泛型?
一般的類和方法,只能使用具體的類型:
要么是基本類型,要么是自定義的類。如果要編寫可以應用于多種類型的代碼,這種刻板的限制對代碼的束縛就會很大。-----來源《Java編程思想》對泛型的介紹。
在 Java 中,泛型是一種參數化類型的機制。
泛型使得可以在定義類、接口和方法時使用類型參數,從而增強了代碼的類型安全性和可讀性,并減少了類型轉換的需求。
增強類型安全:通過指定泛型類型,編譯器可以在編譯階段就檢查類型的正確性。例如,如果定義了一個泛型集合 List< String > ,那么就不能向其中添加非 String 類型的元素,否則會在編譯時出錯。
提高代碼的可讀性:使得代碼的意圖更加清晰。當看到一個泛型類或方法的定義時,能很容易明白其處理的數據類型。
減少類型轉換:使用泛型可以避免繁瑣的類型轉換。
例如,定義一個泛型類 Box
通俗來講,泛型:就是適用于許多許多類型。從代碼上講,就是對類型實現了參數化。
2.2引出泛型
實現一個類,類中包含一個數組成員,使得數組中可以存放任何類型的數據,也可以根據成員方法返回數組中某個下標的值?
思路:
- 我們以前學過的數組,只能存放指定類型的元素
例如:
int[] array = new int[10];
String[] strs = new String[10];
- 所有類的父類,默認為Object類。數組是否可以創建為Object?
例如:
class MyArray{public Object[] array = new Object[10];public Object getPos(int pos){return this.array[pos];}public void setVal(int pos,Object val){this.array[pos] = val;}}
public class TestDemo {public static void main(String[] args) {MyArray myArray = new MyArray();myArray.setVal(0,10);myArray.setVal(1,"hello");//字符串也可以存放String ret = myArray.getPos(1);//編譯報錯System.out.println(ret);}
}
問題:以上代碼實現后 發現
- 任何類型數據都可以存放
- 1號下標本身就是字符串,但是確編譯報錯。必須進行強制類型轉換
雖然在這種情況下,當前數組任何數據都可以存放,但是,更多情況下,我們還是希望他只能夠持有一種數據類
型。而不是同時持有這么多類型。所以,泛型的主要目的:就是指定當前的容器,要持有什么類型的對象。讓編譯
器去做檢查。此時,就需要把類型,作為參數傳遞。需要什么類型,就傳入什么類型。
2.3泛型類的語法
class 泛型類名稱<類型形參列表> {
// 這里可以使用類型參數
}
class ClassName<T1, T2, ..., Tn> {
}
class 泛型類名稱<類型形參列表> extends 繼承類/* 這里可以使用類型參數 */ {
// 這里可以使用類型參數
}
class ClassName<T1, T2, ..., Tn> extends ParentClass<T1> {
// 可以只使用部分類型參數
}
上述代碼進行改寫如下:
class MyArray<T>{public T[]array =(T[]) new Object[10];public T getPos(int pos){return this.array[pos];}public void setVal(int pos,T val){this.array[pos] = val;}}public static void main(String[] args) {MyArray<String> myArray = new MyArray();String ret = myArray.getPos(1);myArray.setVal(2,"byte");myArray.setVal(1,1);}
代碼解釋:
-
類名后的 代表占位符,表示當前類是一個泛型類
了解: 【規范】類型形參一般使用一個大寫字母表示,常用的名稱有:
E 表示 Element
K 表示 Key
V 表示 Value
N 表示 Number
T 表示 Type
S, U, V 等等 - 第二、第三、第四個類型 -
不能new泛型類型的數組
例如:
T[] ts = new T[5];//是不對的
- 類型后加入 < Integer > 指定當前類型
MyArray<String> myArray = new MyArray();
- 不需要進行強制轉換
String ret = myArray.getPos(1);
- 代碼編譯報錯,編譯器會在存放元素的時
候幫助我們進行類型檢查。
myArray.setVal(1,1);
2.4泛型類的使用
示例:
MyArray<Integer> list = new MyArray<Integer>();
注意:泛型只能接受類,所有的基本數據類型必須使用包裝類
2.5類型推導(Type Inference)
當編譯器可以根據上下文推導出類型實參時,可以省略類型實參的填寫
MyArray<Integer> list = new MyArray<>(); // 可以推導出實例化需要的類型實參為 Integer
總結:
- 泛型是將數據類型參數化,進行傳遞
- 使用 表示當前類是一個泛型類。
- 泛型目前為止的優點:數據類型參數化,編譯時自動進行類型檢查和轉換
2.6泛型的上界
語法
class 泛型類名稱<類型形參 extends 類型邊界> {
...
}
示例:
public class MyArray<E extends Number> {
...
}
只接受 Number 的子類型作為 E 的類型實參
MyArray< Integer > l1; // 正常,因為 Integer 是 Number 的子類型
MyArray< String > l2; // 編譯錯誤,因為 String 不是 Number 的子類型