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

史上最全圖文講解Java泛型

 更新時(shí)間:2022年02月15日 09:43:41   作者:mikechen的互聯(lián)網(wǎng)架構(gòu)  
泛型在java中有很重要的地位,在面向?qū)ο缶幊碳案鞣N設(shè)計(jì)模式中有非常廣泛的應(yīng)用,下面這篇文章主要給大家介紹了Java泛型的相關(guān)資料,需要的朋友可以參考下

前言

泛型在java中有很重要的地位,無(wú)論是開(kāi)源框架還是JDK源碼都能看到它。

毫不夸張的說(shuō),泛型是通用設(shè)計(jì)上必不可少的元素,所以真正理解與正確使用泛型,是一門必修課。

一:泛型本質(zhì)

Java 泛型(generics)是 JDK 5 中引入的一個(gè)新特性, 泛型提供了編譯時(shí)類型安全檢測(cè)機(jī)制,該機(jī)制允許程序員在編譯時(shí)檢測(cè)到非法的類型。

泛型的本質(zhì)是參數(shù)化類型,即給類型指定一個(gè)參數(shù),然后在使用時(shí)再指定此參數(shù)具體的值,那樣這個(gè)類型就可以在使用時(shí)決定了。這種參數(shù)類型可以用在類、接口和方法中,分別被稱為泛型類、泛型接口、泛型方法。

二:為什么使用泛型

泛型的好處是在編譯的時(shí)候檢查類型安全,并且所有的強(qiáng)制轉(zhuǎn)換都是自動(dòng)和隱式的,提高代碼的重用率。

(1)保證了類型的安全性。

在沒(méi)有泛型之前,從集合中讀取到的每一個(gè)對(duì)象都必須進(jìn)行類型轉(zhuǎn)換,如果不小心插入了錯(cuò)誤的類型對(duì)象,在運(yùn)行時(shí)的轉(zhuǎn)換處理就會(huì)出錯(cuò)。

比如:沒(méi)有泛型的情況下使用集合:

public static void noGeneric() {
ArrayList names = new ArrayList();
names.add("mikechen的互聯(lián)網(wǎng)架構(gòu)");
names.add(123); //編譯正常
}

有泛型的情況下使用集合:

public static void useGeneric() {
ArrayList<String> names = new ArrayList<>();
names.add("mikechen的互聯(lián)網(wǎng)架構(gòu)");
names.add(123); //編譯不通過(guò)
}

有了泛型后,定義好的集合names在編譯的時(shí)候add(123)就會(huì)編譯不通過(guò)。

相當(dāng)于告訴編譯器每個(gè)集合接收的對(duì)象類型是什么,編譯器在編譯期就會(huì)做類型檢查,告知是否插入了錯(cuò)誤類型的對(duì)象,使得程序更加安全,增強(qiáng)了程序的健壯性。

(2) 消除強(qiáng)制轉(zhuǎn)換

泛型的一個(gè)附帶好處是,消除源代碼中的許多強(qiáng)制類型轉(zhuǎn)換,這使得代碼更加可讀,并且減少了出錯(cuò)機(jī)會(huì)。
還是舉例說(shuō)明,以下沒(méi)有泛型的代碼段需要強(qiáng)制轉(zhuǎn)換:

List list = new ArrayList();
list.add("hello");
String s = (String) list.get(0);

當(dāng)重寫(xiě)為使用泛型時(shí),代碼不需要強(qiáng)制轉(zhuǎn)換:

List<String> list = new ArrayList<String>();
list.add("hello");
String s = list.get(0); // no cast

(3)避免了不必要的裝箱、拆箱操作,提高程序的性能

在非泛型編程中,將筒單類型作為Object傳遞時(shí)會(huì)引起B(yǎng)oxing(裝箱)和Unboxing(拆箱)操作,這兩個(gè)過(guò)程都是具有很大開(kāi)銷的。引入泛型后,就不必進(jìn)行Boxing和Unboxing操作了,所以運(yùn)行效率相對(duì)較高,特別在對(duì)集合操作非常頻繁的系統(tǒng)中,這個(gè)特點(diǎn)帶來(lái)的性能提升更加明顯。

泛型變量固定了類型,使用的時(shí)候就已經(jīng)知道是值類型還是引用類型,避免了不必要的裝箱、拆箱操作。

object a=1;//由于是object類型,會(huì)自動(dòng)進(jìn)行裝箱操作。
 
int b=(int)a;//強(qiáng)制轉(zhuǎn)換,拆箱操作。這樣一去一來(lái),當(dāng)次數(shù)多了以后會(huì)影響程序的運(yùn)行效率。

使用泛型之后

public static T GetValue<T>(T a)
{
  return a;
}
 
public static void Main()
{
  int b=GetValue<int>(1);//使用這個(gè)方法的時(shí)候已經(jīng)指定了類型是int,所以不會(huì)有裝箱和拆箱的操作。
}

(4)提高了代碼的重用性。

三:如何使用泛型

泛型有三種使用方式,分別為:泛型類、泛型接口和泛型方法。

1、泛型類

泛型類:把泛型定義在類上

定義格式:

public class 類名 <泛型類型1,...> {
    
}

注意事項(xiàng):泛型類型必須是引用類型(非基本數(shù)據(jù)類型)

定義泛型類,在類名后添加一對(duì)尖括號(hào),并在尖括號(hào)中填寫(xiě)類型參數(shù),參數(shù)可以有多個(gè),多個(gè)參數(shù)使用逗號(hào)分隔:

public class GenericClass<ab,a,c> {}

當(dāng)然,這個(gè)后面的參數(shù)類型也是有規(guī)范的,不能像上面一樣隨意,通常類型參數(shù)我們都使用大寫(xiě)的單個(gè)字母表示:

T:任意類型 type
E:集合中元素的類型 element
K:key-value形式 key
V: key-value形式 value

示例代碼:

泛型類:

public class GenericClass<T> {
    private T value;
 
 
    public GenericClass(T value) {
        this.value = value;
    }
    public T getValue() {
        return value;
    }
    public void setValue(T value) {
        this.value = value;
    }
}

測(cè)試類:

//TODO 1:泛型類
GenericClass<String> name = new GenericClass<>("mikechen的互聯(lián)網(wǎng)架構(gòu)");
System.out.println(name.getValue());
 
 
GenericClass<Integer> number = new GenericClass<>(123);
System.out.println(number.getValue());

運(yùn)行結(jié)果:

2、泛型接口

泛型方法概述:把泛型定義在方法上

定義格式:

public <泛型類型> 返回類型 方法名(泛型類型 變量名) {
    
}

注意要點(diǎn):

方法聲明中定義的形參只能在該方法里使用,而接口、類聲明中定義的類型形參則可以在整個(gè)接口、類中使用。當(dāng)調(diào)用fun()方法時(shí),根據(jù)傳入的實(shí)際對(duì)象,編譯器就會(huì)判斷出類型形參T所代表的實(shí)際類型。

public interface GenericInterface<T> {
void show(T value);}
}
public class StringShowImpl implements GenericInterface<String> {
@Override
public void show(String value) {
System.out.println(value);
}}
 
public class NumberShowImpl implements GenericInterface<Integer> {
@Override
public void show(Integer value) {
System.out.println(value);
}}

注意:使用泛型的時(shí)候,前后定義的泛型類型必須保持一致,否則會(huì)出現(xiàn)編譯異常:

GenericInterface<String> genericInterface = new NumberShowImpl();//編譯異常

或者干脆不指定類型,那么 new 什么類型都是可以的:

GenericInterface g1 = new NumberShowImpl();
GenericInterface g2 = new StringShowImpl();

3、泛型方法

泛型方法,是在調(diào)用方法的時(shí)候指明泛型的具體類型 。

定義格式:

修飾符<代表泛型的變量>返回值類型 方法名(參數(shù)){}

例如:

/**
?????*
?????* @param t 傳入泛型的參數(shù)
?????* @param <T> 泛型的類型
?????* @return T 返回值為T類型
?????* 說(shuō)明:
?????*???1)public 與 返回值中間<T>非常重要,可以理解為聲明此方法為泛型方法。
?????*???2)只有聲明了<T>的方法才是泛型方法,泛型類中的使用了泛型的成員方法并不是泛型方法。
?????*???3)<T>表明該方法將使用泛型類型T,此時(shí)才可以在方法中使用泛型類型T。
?????*???4)與泛型類的定義一樣,此處T可以隨便寫(xiě)為任意標(biāo)識(shí),常見(jiàn)的如T、E等形式的參數(shù)常用于表示泛型。
?????*/
????public <T> T genercMethod(T t){
????????System.out.println(t.getClass());
????????System.out.println(t);
????????return t;
????}
?
?
public static void main(String[] args) {
????GenericsClassDemo<String> genericString??= new GenericsClassDemo("helloGeneric"); //這里的泛型跟下面調(diào)用的泛型方法可以不一樣。
????String str = genericString.genercMethod("hello");//傳入的是String類型,返回的也是String類型
????Integer i = genericString.genercMethod(123);//傳入的是Integer類型,返回的也是Integer類型
}
?
?
class java.lang.String
hello
?
?
class java.lang.Integer
123

這里可以看出,泛型方法隨著我們的傳入?yún)?shù)類型不同,他得到的類型也不同。泛型方法能使方法獨(dú)立于類而產(chǎn)生變化。

四:泛型通配符

Java泛型的通配符是用于解決泛型之間引用傳遞問(wèn)題的特殊語(yǔ)法, 主要有以下三類:

//表示類型參數(shù)可以是任何類型
public class Apple<?>{}
?
//表示類型參數(shù)必須是A或者是A的子類
public class Apple<T extends A>{}
?
//表示類型參數(shù)必須是A或者是A的超類型
public class Apple<T supers A>{}

1. 無(wú)邊界的通配符(Unbounded Wildcards), 就是, 比如List

無(wú)邊界的通配符的主要作用就是讓泛型能夠接受未知類型的數(shù)據(jù).

2. 固定上邊界的通配符(Upper Bounded Wildcards),采用<? extends E>的形式

使用固定上邊界的通配符的泛型, 就能夠接受指定類及其子類類型的數(shù)據(jù)。

要聲明使用該類通配符, 采用<? extends E>的形式, 這里的E就是該泛型的上邊界。

注意: 這里雖然用的是extends關(guān)鍵字, 卻不僅限于繼承了父類E的子類, 也可以代指顯現(xiàn)了接口E的類

3. 固定下邊界的通配符(Lower Bounded Wildcards),采用<? super E>的形式

使用固定下邊界的通配符的泛型, 就能夠接受指定類及其父類類型的數(shù)據(jù).。

要聲明使用該類通配符, 采用<? super E>的形式, 這里的E就是該泛型的下邊界.。

注意: 你可以為一個(gè)泛型指定上邊界或下邊界, 但是不能同時(shí)指定上下邊界。

五:泛型中KTVE的含義

果點(diǎn)開(kāi)JDK中一些泛型類的源碼,我們會(huì)看到下面這些代碼:

public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
...
}
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable {
...
}

上面這些泛型類定義中的泛型參數(shù)E、K和V都是什么意思呢?其實(shí)這些參數(shù)名稱是可以任意指定,就想方法的參數(shù)名一樣可以任意指定,但是我們通常會(huì)起一個(gè)有意義的名稱,讓別人一看就知道是什么意思。泛型參數(shù)也一樣,E一般是指元素,用來(lái)集合類中。

常見(jiàn)泛型參數(shù)名稱有如下:

E: Element (在集合中使用,因?yàn)榧现写娣诺氖窃?

T:Type(Java 類)

K: Key(鍵)

V: Value(值)

N: Number(數(shù)值類型)

?: 表示不確定的java類型

六:泛型的實(shí)現(xiàn)原理

泛型本質(zhì)是將數(shù)據(jù)類型參數(shù)化,它通過(guò)擦除的方式來(lái)實(shí)現(xiàn),即編譯器會(huì)在編譯期間「擦除」泛型語(yǔ)法并相應(yīng)的做出一些類型轉(zhuǎn)換動(dòng)作。

看一個(gè)例子就應(yīng)該清楚了,例如:

public class Caculate<T> {
private T num;
}

我們定義了一個(gè)泛型類,定義了一個(gè)屬性成員,該成員的類型是一個(gè)泛型類型,這個(gè) T 具體是什么類型,我們也不知道,它只是用于限定類型的。

反編譯一下這個(gè) Caculate 類:

public class Caculate{
public Caculate(){}
private Object num;
}

發(fā)現(xiàn)編譯器擦除 Caculate 類后面的兩個(gè)尖括號(hào),并且將 num 的類型定義為 Object 類型。

那么是不是所有的泛型類型都以 Object 進(jìn)行擦除呢?大部分情況下,泛型類型都會(huì)以 Object 進(jìn)行替換,而有一種情況則不是。那就是使用到了extends和super語(yǔ)法的有界類型,如:

public class Caculate<T extends String> {
private T num;
}

這種情況的泛型類型,num 會(huì)被替換為 String 而不再是 Object。

這是一個(gè)類型限定的語(yǔ)法,它限定 T 是 String 或者 String 的子類,也就是你構(gòu)建 Caculate 實(shí)例的時(shí)候只能限定 T 為 String 或者 String 的子類,所以無(wú)論你限定 T 為什么類型,String 都是父類,不會(huì)出現(xiàn)類型不匹配的問(wèn)題,于是可以使用 String 進(jìn)行類型擦除。

實(shí)際上編譯器會(huì)正常的將使用泛型的地方編譯并進(jìn)行類型擦除,然后返回實(shí)例。但是除此之外的是,如果構(gòu)建泛型實(shí)例時(shí)使用了泛型語(yǔ)法,那么編譯器將標(biāo)記該實(shí)例并關(guān)注該實(shí)例后續(xù)所有方法的調(diào)用,每次調(diào)用前都進(jìn)行安全檢查,非指定類型的方法都不能調(diào)用成功。

實(shí)際上編譯器不僅關(guān)注一個(gè)泛型方法的調(diào)用,它還會(huì)為某些返回值為限定的泛型類型的方法進(jìn)行強(qiáng)制類型轉(zhuǎn)換,由于類型擦除,返回值為泛型類型的方法都會(huì)擦除成 Object 類型,當(dāng)這些方法被調(diào)用后,編譯器會(huì)額外插入一行 checkcast 指令用于強(qiáng)制類型轉(zhuǎn)換,這一個(gè)過(guò)程就叫做『泛型翻譯』。

七:關(guān)于泛型數(shù)組要提一下

看到了很多文章中都會(huì)提起泛型數(shù)組,經(jīng)過(guò)查看sun的說(shuō)明文檔,在java中是”不能創(chuàng)建一個(gè)確切的泛型類型的數(shù)組”的。

也就是說(shuō)下面的這個(gè)例子是不可以的:

List<String>[] ls = new ArrayList<String>[10];

而使用通配符創(chuàng)建泛型數(shù)組是可以的,如下面這個(gè)例子:

List<?>[] ls = new ArrayList<?>[10];

這樣也是可以的:

List<String>[] ls = new ArrayList[10];

下面使用Sun的一篇文檔的一個(gè)例子來(lái)說(shuō)明這個(gè)問(wèn)題:

List<String>[] lsa = new List<String>[10]; // Not really allowed.    
Object o = lsa;    
Object[] oa = (Object[]) o;    
List<Integer> li = new ArrayList<Integer>();    
li.add(new Integer(3));    
oa[1] = li; // Unsound, but passes run time store check    
String s = lsa[1].get(0); // Run-time error: ClassCastException.

這種情況下,由于JVM泛型的擦除機(jī)制,在運(yùn)行時(shí)JVM是不知道泛型信息的,所以可以給oa[1]賦上一個(gè)ArrayList而不會(huì)出現(xiàn)異常,

但是在取出數(shù)據(jù)的時(shí)候卻要做一次類型轉(zhuǎn)換,所以就會(huì)出現(xiàn)ClassCastException,如果可以進(jìn)行泛型數(shù)組的聲明,

上面說(shuō)的這種情況在編譯期將不會(huì)出現(xiàn)任何的警告和錯(cuò)誤,只有在運(yùn)行時(shí)才會(huì)出錯(cuò)。

而對(duì)泛型數(shù)組的聲明進(jìn)行限制,對(duì)于這樣的情況,可以在編譯期提示代碼有類型安全問(wèn)題,比沒(méi)有任何提示要強(qiáng)很多。

下面采用通配符的方式是被允許的:數(shù)組的類型不可以是類型變量,除非是采用通配符的方式,因?yàn)閷?duì)于通配符的方式,最后取出數(shù)據(jù)是要做顯式的類型轉(zhuǎn)換的。

List<?>[] lsa = new List<?>[10]; // OK, array of unbounded wildcard type.    
Object o = lsa;    
Object[] oa = (Object[]) o;    
List<Integer> li = new ArrayList<Integer>();    
li.add(new Integer(3));    
oa[1] = li; // Correct.    
Integer i = (Integer) lsa[1].get(0); // OK

八:最后

以上我就分別從Java泛型的誕生,再到泛型的使用,以及泛型的實(shí)現(xiàn)原理等六個(gè)方面進(jìn)行了完整詳解,希望對(duì)你有所用!

到此這篇關(guān)于Java泛型的文章就介紹到這了,更多相關(guān)Java泛型內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 解決在微服務(wù)環(huán)境下遠(yuǎn)程調(diào)用feign和異步線程存在請(qǐng)求數(shù)據(jù)丟失問(wèn)題

    解決在微服務(wù)環(huán)境下遠(yuǎn)程調(diào)用feign和異步線程存在請(qǐng)求數(shù)據(jù)丟失問(wèn)題

    這篇文章主要介紹了解決在微服務(wù)環(huán)境下遠(yuǎn)程調(diào)用feign和異步線程存在請(qǐng)求數(shù)據(jù)丟失問(wèn)題,主要包括無(wú)異步線程得情況下feign遠(yuǎn)程調(diào)用,異步情況下丟失上下文問(wèn)題,需要的朋友可以參考下
    2022-05-05
  • iBatis習(xí)慣用的16條SQL語(yǔ)句

    iBatis習(xí)慣用的16條SQL語(yǔ)句

    iBatis 是apache 的一個(gè)開(kāi)源項(xiàng)目,一個(gè)O/R Mapping 解決方案,iBatis 最大的特點(diǎn)就是小巧,上手很快.這篇文章主要介紹了iBatis習(xí)慣用的16條SQL語(yǔ)句的相關(guān)資料,需要的朋友可以參考下
    2016-10-10
  • Java中的CyclicBarrier同步屏障詳解

    Java中的CyclicBarrier同步屏障詳解

    這篇文章主要介紹了Java中的CyclicBarrier同步屏障詳解,CyclicBarrier也叫同步屏障,在JDK1.5被引入,可以讓一組線程達(dá)到一個(gè)屏障時(shí)被阻塞,直到最后一個(gè)線程達(dá)到屏障時(shí),屏障才會(huì)開(kāi)門,所有被阻塞的線程才會(huì)繼續(xù)執(zhí)行,需要的朋友可以參考下
    2023-09-09
  • java中List移除元素的四種方式

    java中List移除元素的四種方式

    本文主要介紹了java中List移除元素的四種方式,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2024-08-08
  • Spring中的事件監(jiān)聽(tīng)器使用學(xué)習(xí)記錄

    Spring中的事件監(jiān)聽(tīng)器使用學(xué)習(xí)記錄

    Spring框架中的事件監(jiān)聽(tīng)機(jī)制是一種設(shè)計(jì)模式,它允許你定義和觸發(fā)事件,同時(shí)允許其他組件監(jiān)聽(tīng)這些事件并在事件發(fā)生時(shí)作出響應(yīng),這篇文章主要介紹了Spring中的事件監(jiān)聽(tīng)器使用學(xué)習(xí),需要的朋友可以參考下
    2024-07-07
  • 關(guān)于application.yml數(shù)據(jù)庫(kù)配置方式

    關(guān)于application.yml數(shù)據(jù)庫(kù)配置方式

    這篇文章主要介紹了關(guān)于application.yml數(shù)據(jù)庫(kù)配置方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • Java形參和實(shí)參的實(shí)例之Integer類型與Int類型用法說(shuō)明

    Java形參和實(shí)參的實(shí)例之Integer類型與Int類型用法說(shuō)明

    這篇文章主要介紹了Java形參和實(shí)參的實(shí)例之Integer類型與Int類型用法說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-10-10
  • Hibernate基于ThreadLocal管理Session過(guò)程解析

    Hibernate基于ThreadLocal管理Session過(guò)程解析

    這篇文章主要介紹了Hibernate基于ThreadLocal管理Session過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-10-10
  • 解決子線程無(wú)法訪問(wèn)父線程中通過(guò)ThreadLocal設(shè)置的變量問(wèn)題

    解決子線程無(wú)法訪問(wèn)父線程中通過(guò)ThreadLocal設(shè)置的變量問(wèn)題

    這篇文章主要介紹了解決子線程無(wú)法訪問(wèn)父線程中通過(guò)ThreadLocal設(shè)置的變量問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-07-07
  • SpringBoot整合JPA 基礎(chǔ)使用示例教程

    SpringBoot整合JPA 基礎(chǔ)使用示例教程

    JPA(Java Persistence API)是Java提供的一套數(shù)據(jù)持久化標(biāo)準(zhǔn),通過(guò)對(duì)象-關(guān)系映射(ORM)技術(shù)實(shí)現(xiàn)Java對(duì)象與數(shù)據(jù)庫(kù)表之間的映射,它提供了一系列注解和接口,簡(jiǎn)化了數(shù)據(jù)庫(kù)操作,使得開(kāi)發(fā)者可以通過(guò)操作對(duì)象來(lái)進(jìn)行數(shù)據(jù)庫(kù)交互,而無(wú)需編寫(xiě)復(fù)雜的SQL語(yǔ)句
    2024-10-10

最新評(píng)論

贞丰县| 赤壁市| 上犹县| 合川市| 邵阳县| 安溪县| 剑河县| 左云县| 临海市| 确山县| 合江县| 双流县| 怀集县| 临夏市| 云林县| 汉中市| 广南县| 永城市| 松溪县| 祁连县| 襄城县| 吕梁市| 临夏市| 卫辉市| 夏邑县| 牟定县| 安阳市| 墨脱县| 永丰县| 历史| 靖远县| 永顺县| 长治市| 四会市| 治多县| 前郭尔| 定南县| 东乡县| 滕州市| 靖安县| 漳平市|