最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

《面試1v1》Java泛型

2023-07-16 16:46 作者:JavaPub  | 我要投稿

我是 javapub,一名 Markdown 程序員從?????,八股文種子選手。

《面試1v1》更新中...?

面試官:小伙子,說(shuō)實(shí)話,泛型這個(gè)機(jī)制一開(kāi)始我也是一頭霧水,搞不太明白它到底要解決什么問(wèn)題。你能不能不那么書呆子,給我普普通通地講一講泛型?

候選人: 好嘞,我們來(lái)聊聊泛型。首先,泛型要解決的最主要的問(wèn)題就是類型不安全。比如說(shuō),你有一個(gè)箱子,可以裝任何東西:

public?class?Box?{
????private?Object?obj;
????
????public?void?set(Object?obj)?{
????????this.obj?=?obj;
????}
????
????public?Object?get()?{
????????return?obj;
????}
}

然后你用它裝了一個(gè)蘋果:

Box?b?=?new?Box();
b.set(new?Apple());

但是當(dāng)你取出來(lái)的時(shí)候,是一個(gè)水果啊,你不知道是蘋果還是香蕉,需要強(qiáng)轉(zhuǎn)類型:

Apple?a?=?(Apple)?b.get();??//?強(qiáng)轉(zhuǎn),可能出現(xiàn)ClassCastException

這就是類型不安全,一旦強(qiáng)轉(zhuǎn)錯(cuò)了類型,程序就GG了。 泛型來(lái)了之后,情況就不一樣了。我們可以這樣定義箱子:

public?class?Box<T>?{??//?<T>就是類型參數(shù)
????private?T?obj;
????
????public?void?set(T?obj)?{
????????this.obj?=?obj;
????}
????
????public?T?get()?{
????????return?obj;
????}
}

然后在用的時(shí)候,指定T的實(shí)際類型,比如:

Box<Apple>?b?=?new?Box<Apple>();?
b.set(new?Apple());
Apple?a?=?b.get();??//?不需要強(qiáng)轉(zhuǎn),類型安全!

所以泛型最大的好處就是讓代碼類型安全,不再需要強(qiáng)制類型轉(zhuǎn)換,避免ClassCastException異常,讓代碼更健壯。它把類型檢查的工作從運(yùn)行時(shí)提前到了編譯時(shí)。

面試官:哇,原來(lái)如此!講解的真的很通俗易懂,我都明白了!那泛型中最容易搞混的兩個(gè)概念是什么?

面試官:最容易搞混的兩個(gè)概念,應(yīng)該是類型參數(shù)和實(shí)際類型參數(shù)吧?

候選人: 對(duì)的,這兩個(gè)概念容易混淆。我們?cè)倥e個(gè)例子:

public?class?Box<T>?{???//?<T>就是類型參數(shù)
????private?T?obj;
}

Box<Apple>?b?=?new?Box<>();???//?Apple就是實(shí)際類型參數(shù)

類型參數(shù)T是在定義泛型類Box時(shí)使用的,代表一個(gè)未知的類型。我們不知道使用者會(huì)替換成什么類型,所以用T表示。 而實(shí)際類型參數(shù)Apple是在實(shí)例化Box時(shí)實(shí)際替換類型參數(shù)T的類型。它給T一個(gè)明確的類型,用于這次實(shí)例化。 所以類型參數(shù)是個(gè)未知的類型占位符,實(shí)際類型參數(shù)是替換類型參數(shù)的具體類型。理解了這兩個(gè)概念的區(qū)別,泛型的很多地方就不會(huì)再混淆了。

面試官:說(shuō)的太好了,我都不好意思問(wèn)你其他的了!那最后兩點(diǎn)疑問(wèn),1)為啥泛型類不能有靜態(tài)方法?2)類型擦除是干嘛的?

候選人: 好的,兩個(gè)很好的疑問(wèn): 1)泛型類不能有靜態(tài)方法的原因是因?yàn)殪o態(tài)方法在類加載的時(shí)候就被創(chuàng)建,而泛型類在實(shí)例化的時(shí)候才能確定類型參數(shù)的實(shí)際類型。這時(shí)候靜態(tài)方法已經(jīng)創(chuàng)建完了,無(wú)法使用這個(gè)實(shí)際類型,所以編譯器不允許這么做。 2)類型擦除就是編譯器刪除所有與類型參數(shù)相關(guān)的信息,并替換為上限(通常是Object類型)的過(guò)程。因?yàn)镴ava在1.5之前并沒(méi)有泛型的概念,所以編譯器會(huì)把所有的泛型類型全部擦除掉,在運(yùn)行時(shí)期間不會(huì)存在任何泛型類型的參數(shù)信息。這也是為什么泛型類不能有基本類型的參數(shù)的原因。 類型擦除有利有弊,好處是可以在1.5之前的VM上運(yùn)行泛型代碼,壞處是導(dǎo)致些許運(yùn)行期間的效率損失,因?yàn)椴脸笏械念愋蛥?shù)都被替換為Object類型。不過(guò)這點(diǎn)性能損失在大部分情況下可以忽略。

面試官:太棒了,你的解釋簡(jiǎn)直讓人眼前一亮!真的學(xué)到很多,謝謝你的精彩講解!

候選人: 謝謝面試官的夸獎(jiǎng),我也在這個(gè)過(guò)程中對(duì)泛型有了更深的理解,非常高興能與你進(jìn)行這次交流與探討。

面試官:在聊了泛型這么多后,還有些細(xì)節(jié)想問(wèn)一下:

1. 泛型中<?>和<? extends T>分別代表什么含義?

候選人: <?>代表一個(gè)未知類型的通配符,可以用在類型參數(shù)的位置,表示接受任何類型。比如:

public?void?print(Box<?>?box)?{
????...
}

這個(gè)方法可以傳遞任何類型的Box進(jìn)來(lái),因?yàn)?lt;?>可以匹配任何類型。 而<? extends T>表示從T類型到其子類型之間的某種類型,它代表的上界類型可能是T,也可能是T的子類型。比如:

public?void?print(Box<??extends?Fruit>?box)?{
????...
}

這個(gè)方法可以傳遞Box或者Box進(jìn)來(lái),因?yàn)锳pple和Orange都是Fruit的子類。但不能傳Box,因?yàn)镕ruit的子類型不包括Object。 所以<?>表示全類型通配,而<? extends T>表示從T到子類型的范圍內(nèi)的某種類型,具有上界的語(yǔ)義。


2. 泛型方法和泛型類有什么區(qū)別?

泛型方法是在普通類中定義帶類型參數(shù)的方法,而泛型類是在定義類本身時(shí)指定類型參數(shù)。比如: 泛型方法:

public?class?Box?{
????public?<T>?void?print(T?t)?{
????????...
????}
}?

泛型類:

public?class?Box<T>?{
????private?T?t;
????...?
}

主要區(qū)別在于泛型類的類型參數(shù)可以用在整個(gè)類的方法和屬性上,而泛型方法的類型參數(shù)只在這個(gè)方法內(nèi)有效。泛型方法更靈活,可以在非泛型類上使用。 除此之外,泛型方法可以有static修飾符,可以在靜態(tài)方法內(nèi)使用類型參數(shù)。而泛型類不能有靜態(tài)方法和靜態(tài)屬性,原因和前面說(shuō)的類型擦除有關(guān)。

3. 泛型的上下限是什么?使用場(chǎng)景又是什么?

泛型的上限是<? extends T>,表示從T到子類型的范圍;下限是<? super T>,表示從T到父類型的范圍。 上限的使用場(chǎng)景是當(dāng)需要獲取T的子類型對(duì)象時(shí),比如從集合中取出元素。下限的使用場(chǎng)景是當(dāng)需要添加T的父類型對(duì)象時(shí),比如往集合中添加元素。

Box<??extends?Fruit>?box1;???//?放入Apple、Orange等
box1.add(new?Apple());??????//?只能添加Fruit的子類型??

Box<??super?Fruit>?box2;????//?放入Fruit、Food等?
box2.add(new?Food());??????//?只能添加Fruit的父類型???

所以上下限主要是為了在廣泛限制類型的同時(shí),也允許滿足某些使用場(chǎng)景的需要,使得泛型更加靈活實(shí)用。

面試官:泛型真的有些復(fù)雜,但你解釋的很通俗易懂,我都差不多明白了。最后兩個(gè)小問(wèn)題:

1. 泛型中的邊界是干嘛的?

邊界是對(duì)類型參數(shù)指定的約束,目的是限制類型參數(shù)能被替換的實(shí)際類型。比如,我們可以這樣定義一個(gè)泛型方法:

public?<T?extends?Number>?void?print(T?t)?{
????System.out.println(t.intValue());
}

這里我們指定T必須是Number或其子類型,如果調(diào)用時(shí)用String類型替換T,則會(huì)編譯錯(cuò)誤,因?yàn)镾tring不符合約束。 邊界有兩種形式:

  • 類名或者接口名,例如T extends Number,表示T必須是Number類型或其子類

  • 另一個(gè)類型參數(shù),例如<T, S extends T>,表示S必須是T或其子類型 所以邊界的作用就是限制類型參數(shù)可以替換的實(shí)際類型,確保在方法中可以正常使用某些操作,避免因?yàn)樘鎿Q錯(cuò)誤類型導(dǎo)致的運(yùn)行錯(cuò)誤。

2. 泛型中通配符和無(wú)邊界的有什么區(qū)別?

無(wú)邊界的表示任何類型,它沒(méi)有任何限制,可以理解為,T可以替換為任何類型。 而通配符有些微的區(qū)別,它表示“未知類型”,也沒(méi)有具體的類型邊界,但它只能在“讀”的場(chǎng)景使用,不能在“寫”的場(chǎng)景使用。因?yàn)榫幾g器無(wú)法確定它到底是哪種類型。 舉個(gè)例子:

public?void?print(Box<??>?box)?{??//?讀操作,ok
????...
}

public?void?add(Box<??>?box,?Object?o)?{?//?寫操作,編譯錯(cuò)誤
????box.set(o);??
}

所以無(wú)邊界的可以出現(xiàn)在讀和寫的操作中,而通配符只能在讀操作場(chǎng)景使用,這是兩者的主要區(qū)別。通常在像泛型方法的定義中,使用無(wú)邊界的會(huì)更靈活,而在一些讀操作的泛型方法中,使用通配符可以更廣泛的匹配不同的Box類型。

面試官:真是一個(gè)很細(xì)致的區(qū)分,我以前也常常搞不清這兩者的差別,你的解釋讓我受益匪淺!謝謝你將這些泛型的概念講解的如此清晰和深入,我對(duì)泛型也有了更全面的認(rèn)識(shí)。真是一個(gè)非常愉快的交流過(guò)程!

候選人: 非常高興能幫到您!我自己在準(zhǔn)備和回答的過(guò)程中,也對(duì)泛型有了更深刻的理解,這種問(wèn)答的形式確實(shí)是學(xué)習(xí)的好方法。謝謝面試官的精彩問(wèn)題,讓這個(gè)過(guò)程變得非常有價(jià)值。我也非常欣賞這次交流,收獲頗豐,祝面試官有一個(gè)美好的一天!

最近我在更新《面試1v1》系列文章,主要以場(chǎng)景化的方式,講解我們?cè)诿嬖囍杏龅降膯?wèn)題,致力于讓每一位工程師拿到自己心儀的offer,感興趣可以關(guān)注JavaPub追更!

??目錄合集:

Gitee:https://gitee.com/rodert/JavaPub

GitHub:https://github.com/Rodert/JavaPub

http://javapub.net.cn

《面試1v1》Java泛型的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國(guó)家法律
小金县| 肃南| 洛宁县| 湖南省| 台北县| 日土县| 中江县| 富蕴县| 阳西县| 年辖:市辖区| 弥勒县| 芦山县| 青州市| 神农架林区| 乌拉特后旗| 集安市| 商洛市| 安福县| 闽清县| 穆棱市| 霍州市| 大英县| 太原市| 开封市| 上栗县| 常熟市| 临汾市| 凉城县| 竹溪县| 诸暨市| 香港 | 广西| 甘洛县| 聂荣县| 大安市| 南阳市| 抚顺县| 德化县| 哈巴河县| 阿拉善左旗| 天祝|