最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java?SE之了解泛型

 更新時間:2023年01月16日 15:46:45   作者:程序猿教你打籃球  
這篇文章主要介紹了Java?SE之了解泛型,文章內(nèi)容詳細,簡單易懂,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

如何創(chuàng)建可以存放各種類型的數(shù)組?

根據(jù)JavaSE的語法知識儲備,如果現(xiàn)在讓你們創(chuàng)建如標題一樣的數(shù)組,你會怎么創(chuàng)建呢?

答案是:使用 Object 類來定義數(shù)組,因為 Object 是所有類的父類, 可以接收任意子類對象,也即實現(xiàn)了向上轉(zhuǎn)型,于是我們就寫出了這樣的代碼:

private Object[] array = new Object[3];

那么這種方法可取嗎?

顯然是可取的,但只是使用起來會很不方便,具體不方便在哪,我們接著往后看,在這里我們要寫一個類,里面提供了獲取array指定下標的數(shù)據(jù),和設(shè)置array指定下標的數(shù)據(jù),于是寫出了這樣的代碼:

public class DrawForth {
    private Object[] array = new Object[3];
 
    public void setPosArray(int pos, Object o) {
        this.array[pos] = o;
    }
    public Object getPosValue(int pos) {
        return this.array[pos];
    }
}

代碼到這里仍然是正確的,那我們就要去使用這個類,也就是在main方法中用這個類實例對象,去操作里面的數(shù)組,所以main方法的代碼就是這個樣子:

public static void main(String[] args) {
        DrawForth draw = new DrawForth();
        draw.setPosArray(0, 123);
        draw.setPosArray(1, "hello");
        draw.setPosArray(2, 12.5);
 
        int a = (int)draw.getPosValue(0);
        String str = (String)draw.getPosValue(1);
        double d = (double)draw.getPosValue(1);
    }

看到這里,你是不是就發(fā)現(xiàn)這樣做很不方便呢?

當(dāng)我們往數(shù)組里面設(shè)置數(shù)據(jù)的時候開心了,想設(shè)置成什么類型就是什么類型,但是!當(dāng)我們要獲取對應(yīng)位置的元素就麻煩了,我們必須知道他是什么類型,然后進行強制類型轉(zhuǎn)換才能接收,(返回是Object類型所以需要強轉(zhuǎn)),難道往后每次取數(shù)據(jù)的時候我還得看一看是什么類型嗎?

泛型的概念

淺聊泛型

泛型是在JDK1.5引入的新的語法,通過上面的例子,由此我們就引出了泛型,泛型簡單來說就是把類型當(dāng)成參數(shù)傳遞,指定當(dāng)前容器,你想持有什么類型的對象,你就傳什么類型過去,讓編譯器去做類型檢查!

從而實現(xiàn)類型參數(shù)化(不能是基本數(shù)據(jù)類型)

泛型的簡單語法

class Test1<類型形參列表> {
 
}
class Test2<類型形參1, 類型形參2, ...> {
 
}

類型形參列表的命名規(guī)范

類名后面的 <類型形參列表> 這是一個占位符,表示當(dāng)前類是一個泛型類,形參列表里面如何寫?

通常用一個大寫字母表示,當(dāng)然,你也可以怎么開心怎么來,但是小心辦公室談話警告哈(dog),這里有幾個常用的名稱:

  • E:表示 Element
  • K:表示 Key
  • V:表示 Value
  • N:表述 Number
  • T:表示 Type
  • S,U,V表示,第二,第三,第四個類型

使用泛型知識創(chuàng)建數(shù)組

這里就來修改一下剛開始的代碼,使用到泛型的知識,那么我們就可以這樣修改:

public class DrawForth<T> {
    //private T[] array = new T[3]; error
    private T[] array = (T[])new Object[3];
 
    public void setPosArray(int pos, T o) {
        this.array[pos] = o;
    }
 
    public T getPosValue(int pos) {
        return this.array[pos];
    }
 
    public static void main(String[] args) {
        DrawForth<Integer> draw = new DrawForth<>();
        draw.setPosArray(0, 123);
        //draw.setPosArray(1, "hello"); error
        //draw.setPosArray(2, 12.5); error
        draw.setPosArray(1, 1234);
        draw.setPosArray(2, 12345);
 
        int a = draw.getPosValue(0);
        int b = draw.getPosValue(1);
        int c = draw.getPosValue(2);
    }
}

如上修改之后的代碼,我們可以得到以下知識點:

  • <T> 是一個占位符,僅表示這個類是泛型類
  • 不能 new 泛型數(shù)組,此代碼的寫法也不是最好的方法!
  • 實例化泛型類的語法是:類名<類型實參>變量名 = new 泛型類<類型實參>(構(gòu)造方法實參);
  • 注意:new 泛型類<>尖括號中可以省略類型實參,編譯器可以根據(jù)上下文推導(dǎo)!
  • 編譯時自動進行類型檢查和轉(zhuǎn)換。

什么是裸類型

裸類型就是指在實例化泛型類對象的時候,沒有傳類型實參,比如下面的代碼就是一個裸類型:

DrawForth draw = new DrawForth();

我現(xiàn)在可以告訴你,這樣做編譯完全正常,但我們不要去使用裸類型,因為這是為了兼容老版本的 API 保留的機制,畢竟泛型是 Java1.5 新增的語法。

泛型是如何編譯的?

泛型的擦除機制

如果我們要看泛型是如何編譯的,可以通過命令 javap -c 字節(jié)碼文件 來進行查看:

如上代碼是 2.4 段落中的代碼,奇怪,明明傳的實參是 Integer 類型,最后所有的 T 卻變成了 Object 類型,這就是擦除機制

所以在Java中,泛型機制是在編譯級別實現(xiàn)的,運行期間不會包含任何泛型信息。

提示:類型擦除,不一定是把 T 變成 Object(泛型的上界會提到)

再談為什么不能實例化泛型數(shù)組?

知道了擦除機制后,那么 T[] array = new T[3]; 是不對的,編譯的時候,替換為Object,不是相當(dāng)于:Object[] array = new Object[3]嗎?

在Java中,數(shù)組是一個很特殊的類型,數(shù)組是在運行時存儲和檢查類型信息, 泛型則是在編譯時檢查類型錯誤。

而且Java設(shè)定擦除機制就只針對變量的類型和返回值的類型,所以在編譯時候壓根不會擦除 new T[3]; 這個 T ,所以自然編譯就會報錯!

我們前面通過強制類型轉(zhuǎn)換的方式創(chuàng)建了泛型數(shù)組,說過那樣寫并不好,正確的方式是通過反射創(chuàng)建指定類型的數(shù)組,由于現(xiàn)在沒學(xué)習(xí)到反射,這里先放著就行。

什么是泛型的上界?

有了擦除機制的學(xué)習(xí),泛型在運行時都會被擦除成 Object 但是并不是所有的都是這樣,泛型的上界就是對泛型類傳入的類型變量做一定的約束,可以通過類型邊界來進行約束。

語法:

class 泛型類名稱<類型形參 extends 類型邊界> {
    //...code
}

這里我們來舉兩個例子:

例1:

這里簡單分析一下,Student 繼承了 Person 類,而 Teacher 沒有繼承 Person 類,接著 Test 類給定了泛型的上界, 那么 Test 類中 <> 里面是什么意思呢?

表示只接收 Person 或 Person 的子類作為 T 的類型實參。

通過 main 方法中的例子也可也看出,類型傳參只能傳 Person 或 Person 的子類。

例2:

還是簡單分析一下,Student 類實現(xiàn)了 Comparable 接口,而 Teacher 類并沒有實現(xiàn), 接著 Test 類給定了泛型的上界, 那么 Test 類中 <> 里面是什么意思呢?

表示 T 接收的類型必須是實現(xiàn) Comparable 這個接口的!

通過 main 方法中的例子也可也看出,類型傳參只能傳實現(xiàn)了 Comparable 接口的類 。

注意:如果泛型類沒有指定邊界,則可以默認視為 T extends Object。

再談擦除機制

如果給泛型設(shè)置了上界,則會擦除到邊界處,也就不會擦除成 Object!

class Person {}
 
class Student extends Person {}
 
public class Main<T extends Person> {
    T array[] = (T[])new Object[10];
    public static void main(String[] args) {
        Main<Student> main = new Main<>();
    }
}

這里 Main 方法中設(shè)定了泛型的上界,傳的類型實參必須是Person的子類,所以編譯時會不會被擦除成 Person呢?下面我們查看一下對應(yīng)的字節(jié)碼文件: 

顯而易見,確實被擦除成了泛型的上界! 

包裝類的知識

基本數(shù)據(jù)類型和包裝類

在Java中,由于基本類型不是繼承自O(shè)bject,為了在泛型代碼中可以支持基本類型,Java給每個基本類型都對應(yīng)了 一個包裝類型。

裝箱和拆箱

裝箱和拆箱也可也被稱為裝包和拆包。

裝箱:將一個基本數(shù)據(jù)類型值放入對象的某個屬性中。

拆箱:將一個包裝類型中的值取出放到一個基本數(shù)據(jù)類型中。

這里我們舉例來更清楚的認識裝箱和拆箱:

public class Test {
    public static void main(String[] args) {
        int a = 10;
        Integer integer1 = new Integer(a); //手動裝箱
        Integer integer2 = Integer.valueOf(100); //手動裝箱
 
        int b = integer1.intValue(); //手動拆箱
    }
}

自動裝箱和拆箱

由上面的例子我們可以看出,手動裝箱和拆箱會帶來不少的代碼量,為了減少開發(fā)者的負擔(dān),Java中提供了自動轉(zhuǎn)換機制,比如:

public class Test {
    public static void main(String[] args) {
        Integer integer = 100; //自動裝箱
        int a = integer; //自動拆箱
    }
}

一道面試題

以下代碼輸出什么? 

public class Test {
 
    public static void main(String[] args) {
        Integer a1 = 100;
        Integer a2 = 100;
        System.out.println(a1 == a2);
        Integer a3 = 200;
        Integer a4 = 200;
        System.out.println(a3 == a4);
    }
}

結(jié)果是:true false 

為什么是這樣的答案?這里我們?nèi)タ匆幌聦?yīng)的字節(jié)碼文件再分析:

通過觀察字節(jié)碼文件,我們可以看到,在自動裝箱的過程中,調(diào)用了 Integer.valueOf 方法,那么我們就去看一看 valueOf 方法中做了一件什么事:

通過查看源碼,我們也能看出此方法將始終緩存 -128到127范圍內(nèi)的值, 通過查看對應(yīng)的 low 和 high 值也可也發(fā)現(xiàn) low為 -128,high為127,cache 是一個緩存數(shù)組。

接著我們來閱讀下這段代碼的操作,如果傳入的值是介于 -128和127 之間,則直接返回緩存數(shù)組對應(yīng)下標的值,比如傳入的值是 -127 也就返回 chache[-127+(-(-128))],也即1下標位置的值!

如果超出了 -128到127 的范圍則是新 new 一個對象返回,只要是 new 就一定是一個新對象,地址也是唯一的。

而且引用類型用 == 比較,比較的是引用的對象的地址,看完上面的介紹,你能弄明白為什么輸出 true 和 false 嗎?

泛型方法

定義泛型方法的語法:

方法限定符 <類型形參列表> 返回值類型 方法名稱(形參列表) {

        //...code

}  

普通泛型方法

這里我們就舉一個很簡單的例子:

public class Test {
    public <T> T getValue(T value) {
        return value;
    }
    public static void main(String[] args) {
        Test test = new Test();
        int ret = test.<Integer>getValue(150); //不使用類型推導(dǎo)
        System.out.println(ret);
 
        double d = test.getValue(12.5); //使用類型推導(dǎo)
        System.out.println(d);
    }
}

這就是泛型方法,這里面有個關(guān)鍵詞,類型推導(dǎo),什么是類型推導(dǎo)呢?

類型推導(dǎo)就是編譯器會根據(jù)你傳參的數(shù)據(jù),自動推斷出你要傳遞的類型實參,你也可以不使用類型推導(dǎo),他們的效果都是一樣的。

靜態(tài)泛型方法

既然有普通泛型方法,同理,也有靜態(tài)的泛型方法,也就是在修飾符后面加上 static,靜態(tài)泛型方法跟普通靜態(tài)方法一樣,都是通過類名訪問,不依賴于對象:

public class Test {
    public static<T> T getValue(T value) {
        return value;
    }
    public static void main(String[] args) {
        int ret = Test.<Integer>getValue(150); //不使用類型推導(dǎo)
        System.out.println(ret);
 
        double d = getValue(12.5); //使用類型推導(dǎo)(靜態(tài)方法可以直接訪問同類中靜態(tài)方法,可以不借助類名)
        System.out.println(d);
    }
}

通配符 

引出通配符 

我們先來看這樣的一段代碼:

class Message<T> {
    private T message ;
    public T getMessage() {
        return message;
    }
    public void setMessage(T message) {
        this.message = message;
    }
}
public class TestDemo {
    public static void fun(Message<String> temp){
        System.out.println(temp.getMessage());
    }
    public static void main(String[] args) {
        Message<String> message = new Message<>();
        message.setMessage("歡迎來到籃球哥的博客!");
        fun(message);
    }
}

如果你仔細觀察,TestDemo 類中的 fun 方法是有局限性的,他的形參就限制了傳過來的 Missage類的類型必須是String,也就是說,形參能接收的對象的類型參數(shù)必須是String類型。

所以如果我們 new Missage對象時,類型實參傳的是 Integer 呢?fun方法就會報錯:

所以為了解決以上的問題,就有了通配符的概念!

認識通配符

泛型T是確定的類型,一旦傳類型了,就定下來了,而通配符的出現(xiàn),就會使得更靈活,或者說更不確定,就好像他是一個垃圾箱,可以接收所有的泛型類型,但又不能讓用戶隨意更改!

通配符:? 

現(xiàn)在我們就把上面的代碼更改一下,運用上通配符:

public class TestDemo {
    public static void fun(Message<?> temp){
        System.out.println(temp.getMessage());
    }
    public static void main(String[] args) {
        Message<Integer> message1 = new Message<>();
        message1.setMessage(123);
        fun(message1);
        Message<String> message2 = new Message<>();
        message2.setMessage("歡迎來到籃球哥的博客!");
        fun(message2);
    }
}

這樣我們的代碼就不會出錯,但是,你不能通過 fun 方法去修改你傳遞對象的內(nèi)容,為什么呢?

站在 fun 的角度,他使用了 ? 接收可以任意泛型類,所以他不能確定自己接收了什么對象的!也就無法對對象的值進行更改! 

這樣代碼還是不夠好,如果真的什么泛型類都能接收,那不是亂套了,所以在此基礎(chǔ)上,又增加了通配符的上界和下界!

通配符的上界

語法:<? extends 上界>   例如:<? extends Person>

表示只能接收的實參類型是 Person 或者 Person的子類!

圖例:

這里我們寫一段偽代碼,更改上面用例的方法:

public static void fun(Message<? extends Person> temp){
        //temp.setMessage(new Student()); //仍然無法修改!
        //temp.setMessage(new Person()); //仍然無法修改!
        Person person = temp.getMessage();
        System.out.println(person);
}

為什么還是不能修改對象的屬性呢?

因為 temp 接收的是 Person 或 Person的子類,此時接收的是哪個子類無法確定,也就無法設(shè)置對象的屬性。

因為我們知道只能接收 Person以及他的子類,所以我們就可以拿 Person 類型來接收 getMessage 的對象,因為 Person是他們的父類,獲取的是子類對象就可以實現(xiàn)向上轉(zhuǎn)型,是安全的。

總結(jié): 通配符的上界,不能進行寫入數(shù)據(jù),只能進行讀取數(shù)據(jù)。

通配符的下界

語法:<? extends 下界>   例如:<? super Person> 

表示只能接收的實參類型是 Person 或者 Person的父類!

圖例: 

這里我們寫一段偽代碼,更改上面用例的方法:

public static void fun(Message<? super Person> temp){
        temp.setMessage(new Student()); //可以修改,因為添加的是他的子類
        temp.setMessage(new Person()); //可以修改,因為添加的是他本身
        //Person person = temp.getMessage(); // 不能接收,不知道獲取的是哪個父類
        System.out.println(temp.getMessage()); //只能輸出
}

為啥下界就可以設(shè)置對象的屬性呢?

因為只能接收本身以及父類的類型,所以我們可以setMessage 傳子類對象,但是不能傳遞父類,因為修改成子類對象是向上轉(zhuǎn)型是安全的,如果 setMessaget 傳父類對象的話就是向下轉(zhuǎn)型則不安全!

為啥不能 getMessage呢?因為你不知道形參接收的類型是哪個父類,只能去輸出內(nèi)容!

總結(jié):通配符的下界,不能進行讀取數(shù)據(jù),只能寫入數(shù)據(jù)。 

以上就是Java SE之了解泛型的詳細內(nèi)容,更多關(guān)于Java SE泛型的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Myeclipse鏈接Oracle等數(shù)據(jù)庫時lo exception: The Network Adapter could not establish the connection

    Myeclipse鏈接Oracle等數(shù)據(jù)庫時lo exception: The Network Adapter coul

    今天小編就為大家分享一篇關(guān)于Myeclipse鏈接Oracle等數(shù)據(jù)庫時lo exception: The Network Adapter could not establish the connection,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-03-03
  • Java NegativeArraySizeException異常解決方案

    Java NegativeArraySizeException異常解決方案

    這篇文章主要介紹了Java NegativeArraySizeException異常解決方案,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • JavaEE在線人數(shù)管理系統(tǒng)

    JavaEE在線人數(shù)管理系統(tǒng)

    這篇文章主要為大家分享了JavaEE在線人數(shù)管理系統(tǒng),顯示在線人數(shù)、在線人詳細信息、管理員踢人等功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-09-09
  • SpringCloud 服務(wù)注冊IP錯誤的解決

    SpringCloud 服務(wù)注冊IP錯誤的解決

    這篇文章主要介紹了SpringCloud 服務(wù)注冊IP錯誤的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • MVC頁面之間參數(shù)傳遞實現(xiàn)過程圖解

    MVC頁面之間參數(shù)傳遞實現(xiàn)過程圖解

    這篇文章主要介紹了MVC頁面之間參數(shù)傳遞實現(xiàn)過程圖解,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-11-11
  • spring mvc高級技術(shù)實例詳解

    spring mvc高級技術(shù)實例詳解

    前面學(xué)習(xí)了簡單的Spring Web知識,接著學(xué)習(xí)更高階的Web技術(shù)。下面這篇文章主要給大家介紹了spring mvc高級技術(shù)的相關(guān)資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考借鑒,下面隨著小編來一起看看吧
    2018-09-09
  • 深入理解Spring AOP

    深入理解Spring AOP

    這篇文章主要介紹了深入理解Spring AOP,詳細的介紹了spring aop的具體實現(xiàn)與理論
    2017-01-01
  • 詳解java構(gòu)建者模式Builder

    詳解java構(gòu)建者模式Builder

    這篇文章主要介紹了java構(gòu)建者模式Builder,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • SpringBoot使用JavaMailSender實現(xiàn)發(fā)送郵件+Excel附件

    SpringBoot使用JavaMailSender實現(xiàn)發(fā)送郵件+Excel附件

    項目審批完畢后,需要發(fā)送郵件通知相關(guān)人員,并且要附帶數(shù)據(jù)庫表生成的Excel表格,這就要求不光是郵件發(fā)送功能,還要臨時生成Excel表格做為附件,本文詳細介紹了SpringBoot如何使用JavaMailSender實現(xiàn)發(fā)送郵件+Excel附件,需要的朋友可以參考下
    2023-10-10
  • SSH框架網(wǎng)上商城項目第2戰(zhàn)之基本增刪查改、Service和Action的抽取

    SSH框架網(wǎng)上商城項目第2戰(zhàn)之基本增刪查改、Service和Action的抽取

    SSH框架網(wǎng)上商城項目第2戰(zhàn)之基本增刪查改、Service和Action的抽取,感興趣的小伙伴們可以參考一下
    2016-05-05

最新評論

盘山县| 海原县| 石林| 舒兰市| 封开县| 洞头县| 房产| 新竹县| 布尔津县| 饶阳县| 贵港市| 贵港市| 裕民县| 安吉县| 赞皇县| 博湖县| 眉山市| 龙州县| 颍上县| 南川市| 青龙| 湘潭市| 胶州市| 台中县| 达尔| 彝良县| 定襄县| 萨迦县| 垫江县| 民县| 大田县| 汝南县| 黄冈市| 开江县| 道真| 佳木斯市| 屯门区| 西宁市| 衡东县| 洛扎县| 天镇县|