自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Java泛型需要注意的問(wèn)題

開發(fā) 后端
本文通過(guò)參考《Java編程思想》對(duì)泛型使用過(guò)程中需要注意的問(wèn)題進(jìn)行了總結(jié)以及給大家提供一些泛型相關(guān)的面試題供大家學(xué)習(xí)使用。

[[348505]]

 我們都知道Java 在 1.5 引入了泛型機(jī)制,泛型的本質(zhì)是參數(shù)化類型,也就是說(shuō)變量的類型是一個(gè)參數(shù),在使用時(shí)再指定為具體類型。泛型可以用于類、接口、方法,通過(guò)使用泛型可以使代碼更簡(jiǎn)單、安全。本文通過(guò)參考《Java編程思想》對(duì)泛型使用過(guò)程中需要注意的問(wèn)題進(jìn)行了總結(jié)以及給大家提供一些泛型相關(guān)的面試題供大家學(xué)習(xí)使用。

 

 

泛型相關(guān)問(wèn)題

1、泛型類型引用傳遞問(wèn)題

在 Java 中,像下面形式的引用傳遞是不允許的:

  1. ArrayList<String> arrayList1=new ArrayList<Object>();//編譯錯(cuò)誤 
  2. ArrayList<Object> arrayList1=new ArrayList<String>();//編譯錯(cuò)誤 

我們先看第一種情況,將第一種情況拓展成下面的形式:

  1. ArrayList<Object> arrayList1=new ArrayList<Object>(); 
  2. arrayList1.add(new Object()); 
  3. arrayList1.add(new Object()); 
  4. ArrayList<String> arrayList2=arrayList1;//編譯錯(cuò)誤 

實(shí)際上,在第 4 行代碼處,就會(huì)有編譯錯(cuò)誤。那么,我們先假設(shè)它編譯沒(méi)錯(cuò)。那么當(dāng)我們使用arrayList2 引用用 get()方法取值的時(shí)候,返回的都是 String 類型的對(duì)象,可是它里面實(shí)際上已經(jīng)被我們存放了 Object 類型的對(duì)象,這樣,就會(huì)有 ClassCastException 了。所以為了避免這種極易出現(xiàn)的錯(cuò)誤,Java 不允許進(jìn)行這樣的引用傳遞。(這也是泛型出現(xiàn)的原因,就是為了解決類型轉(zhuǎn)換的問(wèn)題,我們不能違背它的初衷)。

 

在看第二種情況,將第二種情況拓展成下面的形式:

  1. ArrayList<String> arrayList1=new ArrayList<String>(); 
  2. arrayList1.add(new String()); 
  3. arrayList1.add(new String()); 
  4. ArrayList<Object> arrayList2=arrayList1;//編譯錯(cuò)誤 

沒(méi)錯(cuò),這樣的情況比第一種情況好的多,最起碼,在我們用 arrayList2 取值的時(shí)候不會(huì)出現(xiàn)ClassCastException,因?yàn)槭菑?String 轉(zhuǎn)換為 Object。可是,這樣做有什么意義呢,泛型出現(xiàn)的原因,就是為了解決類型轉(zhuǎn)換的問(wèn)題。我們使用了泛型,到頭來(lái),還是要自己強(qiáng)轉(zhuǎn),違背了泛型設(shè)計(jì)的初衷。所以 java 不允許這么干。再說(shuō),你如果又用 arrayList2 往里面 add()新的對(duì)象,那么到時(shí)候取得時(shí)候,我怎么知道我取出來(lái)的到底是 String 類型的,還是 Object 類型的呢?所以,要格外注意泛型中引用傳遞問(wèn)題。

2、泛型類型變量不能是基本數(shù)據(jù)類型

就比如,沒(méi)有 ArrayList<double>,只有 ArrayList<Double>。因?yàn)楫?dāng)類型擦除后,ArrayList 的原始類中的類型變量(T)替換為 Object,但 Object 類型不能存儲(chǔ) double 值。

 

3、運(yùn)行時(shí)類型查詢

  1. ArrayList<String> arrayList=new ArrayList<String>();if( arrayList instanceof ArrayLi 

因?yàn)轭愋筒脸螅珹rrayList<String>只剩下原始類型,泛型信息 String 不存在了。

 

4、泛型在靜態(tài)方法和靜態(tài)類中的問(wèn)題

泛型類中的靜態(tài)方法和靜態(tài)變量不可以使用泛型類所聲明的泛型類型參數(shù)

  1. public class Test2<T> { 
  2. public static T one; //編譯錯(cuò)誤 
  3. public static T show(T one){ //編譯錯(cuò)誤 
  4. return null
  5. } } 

因?yàn)榉盒皖愔械姆盒蛥?shù)的實(shí)例化是在定義泛型類型對(duì)象(例如 ArrayList<Integer>)的時(shí)候指定的,而靜態(tài)變量和靜態(tài)方法不需要使用對(duì)象來(lái)調(diào)用。對(duì)象都沒(méi)有創(chuàng)建,如何確定這個(gè)泛型參數(shù)是何種類型,所以當(dāng)然是錯(cuò)誤的。

 

但是要注意區(qū)分下面的一種情況:

  1. public class Test2<T> { 
  2. public static <T >T show(T one){//這是正確的 
  3. return null
  4. } } 

因?yàn)檫@是一個(gè)泛型方法,在泛型方法中使用的 T 是自己在方法中定義的 T,而不是泛型類中的 T。

 

泛型相關(guān)面試題

1. Java 中的泛型是什么 ? 使用泛型的好處是什么?

泛型是一種參數(shù)化類型的機(jī)制。它可以使得代碼適用于各種類型,從而編寫更加通用的代碼,例如集合框架。泛型是一種編譯時(shí)類型確認(rèn)機(jī)制。它提供了編譯期的類型安全,確保在泛型類型(通常為泛型集合)上只能使用正確類型的對(duì)象,避免了在運(yùn)行時(shí)出現(xiàn) ClassCastException。

 

2、Java 的泛型是如何工作的 ? 什么是類型擦除 ?

泛型的正常工作是依賴編譯器在編譯源碼的時(shí)候,先進(jìn)行類型檢查,然后進(jìn)行類型擦除并且在類型參數(shù)出現(xiàn)的地方插入強(qiáng)制轉(zhuǎn)換的相關(guān)指令實(shí)現(xiàn)的。

編譯器在編譯時(shí)擦除了所有類型相關(guān)的信息,所以在運(yùn)行時(shí)不存在任何類型相關(guān)的信息。例如List<String>在運(yùn)行時(shí)僅用一個(gè) List 類型來(lái)表示。為什么要進(jìn)行擦除呢?這是為了避免類型膨脹。

 

3. 什么是泛型中的限定通配符和非限定通配符 ?

限定通配符對(duì)類型進(jìn)行了限制。有兩種限定通配符,一種是<? extends T>它通過(guò)確保類型必須是 T 的子類來(lái)設(shè)定類型的上界,另一種是<? super T>它通過(guò)確保類型必須是 T 的父類來(lái)設(shè)定類型的下界。泛型類型必須用限定內(nèi)的類型來(lái)進(jìn)行初始化,否則會(huì)導(dǎo)致編譯錯(cuò)誤。另一方面<?>表示了非限定通配符,因?yàn)?lt;?>可以用任意類型來(lái)替代。

 

4. List<? extends T>和 List <? super T>之間有什么區(qū)別 ?

這和上一個(gè)面試題有聯(lián)系,有時(shí)面試官會(huì)用這個(gè)問(wèn)題來(lái)評(píng)估你對(duì)泛型的理解,而不是直接問(wèn)你什么是限定通配符和非限定通配符。這兩個(gè) List 的聲明都是限定通配符的例子,List<? extends T>可以接受任何繼承自 T 的類型的 List,而 List<? super T>可以接受任何 T 的父類構(gòu)成的 List。例如 List<?extends Number>可以接受 List<Integer>或 List<Float>。在本段出現(xiàn)的連接中可以找到更多信息。

 

5. 如何編寫一個(gè)泛型方法,讓它能接受泛型參數(shù)并返回泛型類型?

編寫泛型方法并不困難,你需要用泛型類型來(lái)替代原始類型,比如使用 T, E or K,V 等被廣泛認(rèn)可的類型占位符。泛型方法的例子請(qǐng)參閱 Java 集合類框架。最簡(jiǎn)單的情況下,一個(gè)泛型方法可能會(huì)像這樣:

  1. public V put(K key, V value) { 
  2. return cache.put(key, value); 

6. Java 中如何使用泛型編寫帶有參數(shù)的類?

這是上一道面試題的延伸。面試官可能會(huì)要求你用泛型編寫一個(gè)類型安全的類,而不是編寫一個(gè)泛型方法。關(guān)鍵仍然是使用泛型類型來(lái)代替原始類型,而且要使用 JDK 中采用的標(biāo)準(zhǔn)占位符。

 

7. 編寫一段泛型程序來(lái)實(shí)現(xiàn) LRU 緩存?

對(duì)于喜歡 Java 編程的人來(lái)說(shuō)這相當(dāng)于是一次練習(xí)。給你個(gè)提示,LinkedHashMap 可以用來(lái)實(shí)現(xiàn)固定大小的 LRU 緩存,當(dāng) LRU 緩存已經(jīng)滿了的時(shí)候,它會(huì)把最老的鍵值對(duì)移出緩存。LinkedHashMap 提供了一個(gè)稱為 removeEldestEntry()的方法,該方法會(huì)被 put()和 putAll()調(diào)用來(lái)刪除最老的鍵值對(duì)。

 

8. 你可以把 List<String>傳遞給一個(gè)接受 List<Object>參數(shù)的方法嗎?(見(jiàn)上面說(shuō)明)

對(duì)任何一個(gè)不太熟悉泛型的人來(lái)說(shuō),這個(gè) Java 泛型題目看起來(lái)令人疑惑,因?yàn)檎Э雌饋?lái) String 是一種Object,所以 List<String>應(yīng)當(dāng)可以用在需要 List<Object>的地方,但是事實(shí)并非如此。真這樣做的話會(huì)導(dǎo)致編譯錯(cuò)誤。如果你再深一步考慮,你會(huì)發(fā)現(xiàn) Java 這樣做是有意義的,因?yàn)?List<Object>可以存儲(chǔ)任何類型的對(duì)象包括 String, Integer 等等,而 List<String>卻只能用來(lái)存儲(chǔ) Strings。

 

9. Array 中可以用泛型嗎?

這可能是 Java 泛型面試題中最簡(jiǎn)單的一個(gè)了,當(dāng)然前提是你要知道 Array 事實(shí)上并不支持泛型,這也是為什么 Joshua Bloch 在 Effective Java 一書中建議使用 List 來(lái)代替 Array,因?yàn)?List 可以提供編譯期的類型安全保證,而 Array 卻不能。

 

10. 如何阻止 Java 中的類型未檢查的警告?

如果你把泛型和原始類型混合起來(lái)使用,例如下列代碼,Java 5 的 javac 編譯器會(huì)產(chǎn)生類型未檢查的警告,例如 List<String> rawList = new ArrayList()注意: Hello.java 使用了未檢查或稱為不安全的操作;這種警告可以使用@SuppressWarnings("unchecked")注解來(lái)屏蔽。

 

11、Java 中 List<Object>和原始類型 List 之間的區(qū)別?

原始類型和帶參數(shù)類型<Object>之間的主要區(qū)別是,在編譯時(shí)編譯器不會(huì)對(duì)原始類型進(jìn)行類型安全檢查,卻會(huì)對(duì)帶參數(shù)的類型進(jìn)行檢查,通過(guò)使用 Object 作為類型,可以告知編譯器該方法可以接受任何類型的對(duì)象,比如 String 或 Integer。這道題的考察點(diǎn)在于對(duì)泛型中原始類型的正確理解。它們之間的第二點(diǎn)區(qū)別是,你可以把任何帶參數(shù)的泛型類型傳遞給接受原始類型 List 的方法,但卻不能把List<String>傳遞給接受 List<Object>的方法,因?yàn)闀?huì)產(chǎn)生編譯錯(cuò)誤。

 

12、Java 中 List<?>和 List<Object>之間的區(qū)別是什么?

這道題跟上一道題看起來(lái)很像,實(shí)質(zhì)上卻完全不同。List<?> 是一個(gè)未知類型的 List,而List<Object>其實(shí)是任意類型的 List。你可以把 List<String>, List<Integer>賦值給 List<?>,卻不能把 List<String>賦值給 List<Object>。

  1. List<?> listOfAnyType; 
  2. List<Object> listOfObject = new ArrayList<Object>(); 
  3. List<String> listOfString = new ArrayList<String>(); 
  4. List<Integer> listOfInteger = new ArrayList<Integer>(); 
  5. listOfAnyType = listOfString; //legal 
  6. listOfAnyType = listOfInteger; //legal 
  7. listOfObjectType = (List<Object>) listOfString; //compiler error - in-convertible ty 

13、List<String>和原始類型 List 之間的區(qū)別.

該題類似于“原始類型和帶參數(shù)類型之間有什么區(qū)別”。帶參數(shù)類型是類型安全的,而且其類型安全是由編譯器保證的,但原始類型 List 卻不是類型安全的。你不能把 String 之外的任何其它類型的 Object 存入String 類型的 List 中,而你可以把任何類型的對(duì)象存入原始 List 中。使用泛型的帶參數(shù)類型你不需要進(jìn)行類型轉(zhuǎn)換,但是對(duì)于原始類型,你則需要進(jìn)行顯式的類型轉(zhuǎn)換。

  1. List listOfRawTypes = new ArrayList(); 
  2. listOfRawTypes.add("abc"); 
  3. listOfRawTypes.add(123); //編譯器允許這樣 - 運(yùn)行時(shí)卻會(huì)出現(xiàn)異常 
  4. String item = (String) listOfRawTypes.get(0); //需要顯式的類型轉(zhuǎn)換 
  5. item = (String) listOfRawTypes.get(1); //拋 ClassCastException,因?yàn)?nbsp;Integer 不能被轉(zhuǎn)換為 String 
  6. List<String> listOfString = new ArrayList(); 
  7. listOfString.add("abcd"); 
  8. listOfString.add(1234); //編譯錯(cuò)誤,比在運(yùn)行時(shí)拋異常要好 
  9. item = listOfString.get(0); //不需要顯式的類型轉(zhuǎn)換 - 編譯器自動(dòng)轉(zhuǎn)換 

 

通配符

通配符上界

常規(guī)使用

  1. public class Test { 
  2. public static void printIntValue(List<? extends Number> list) { 
  3. for (Number number : list) { 
  4. System.out.print(number.intValue()+" "); 
  5. System.out.println(); 
  6. public static void main(String[] args) { 
  7. List<Integer> integerList=new ArrayList<Integer>(); 
  8. integerList.add(2); 
  9. integerList.add(2); 
  10. printIntValue(integerList); 
  11. List<Float> floatList=new ArrayList<Float>(); 
  12. floatList.add((float) 3.3); 
  13. floatList.add((float) 0.3); 
  14. printIntValue(floatList); 
  15. } } 

輸出:

2 2

3 0

非法使用

  1. public class Test { 
  2. public static void fillNumberList(List<? extends Number> list) { 
  3. list.add(new Integer(0));//編譯錯(cuò)誤 
  4. list.add(new Float(1.0));//編譯錯(cuò)誤 
  5. public static void main(String[] args) { 
  6. List<? extends Number> list=new ArrayList(); 
  7. list.add(new Integer(1));//編譯錯(cuò)誤 
  8. list.add(new Float(1.0));//編譯錯(cuò)誤 
  9. } } 

List<? extends Number>可以代表 List<Integer>或 List<Float>,為什么不能像其中加入 Integer或者 Float 呢?

首先,我們知道 List<Integer>之中只能加入 Integer。并且如下代碼是可行的:

  1. List<? extends Number> list1=new ArrayList<Integer>(); 
  2. List<? extends Number> list2=new ArrayList<Float>(); 

假設(shè)前面的例子沒(méi)有編譯錯(cuò)誤,如果我們把 list1 或者 list2 傳入方法 fillNumberList,顯然都會(huì)出現(xiàn)類型不匹配的情況,假設(shè)不成立。

 

因此,我們得出結(jié)論:不能往 List<? extends T> 中添加任意對(duì)象,除了 null。

 

那為什么對(duì) List<? extends T>進(jìn)行迭代可以呢,因?yàn)樽宇惐囟ㄓ懈割愊嗤慕涌?,這正是我們所期望的。

 

通配符下界

常規(guī)使用

  1. public class Test { 
  2. public static void fillNumberList(List<? super Number> list) { 
  3. list.add(new Integer(0)); 
  4. list.add(new Float(1.0)); 
  5. public static void main(String[] args) { 
  6. List<? super Number> list=new ArrayList(); 
  7. list.add(new Integer(1)); 
  8. list.add(new Float(1.1)); 
  9. } } 

可以添加 Number 的任何子類,為什么呢?

List<? super Number>可以代表 List<T>,其中 T 為 Number 父類,(雖然 Number 沒(méi)有父類)。如果說(shuō),T 為 Number 的父類,我們想 List<T>中加入 Number 的子類肯定是可以的。

 

非法使用

對(duì) List<? superT>進(jìn)行迭代是不允許的。為什么呢?你知道用哪種接口去迭代 List 嗎?只有用 Object類的接口才能保證集合中的元素都擁有該接口,顯然這個(gè)意義不大。其應(yīng)用場(chǎng)景略。

 

無(wú)界通配符

知道了通配符的上界和下界,其實(shí)也等同于知道了無(wú)界通配符,不加任何修飾即可,單獨(dú)一個(gè)“?”。如List<?>,“?”可以代表任意類型,“任意”也就是未知類型。

 

List<Object>與 List<?>并不等同,List<Object>是 List<?>的子類。還有不能往 List<?> list里添加任意對(duì)象,除了 null。

 

常規(guī)使用

1、當(dāng)方法是使用原始的 Object 類型作為參數(shù)時(shí),如下:

  1. public static void printList(List<Object> list) { 
  2. for (Object elem : list) 
  3. System.out.println(elem + ""); 
  4. System.out.println(); 

可以選擇改為如下實(shí)現(xiàn):

  1. public static void printList(List<?> list) { 
  2. for (Object elem: list) 
  3. System.out.print(elem + ""); 
  4. System.out.println(); 

這樣就可以兼容更多的輸出,而不單純是 List<Object>,如下:

  1. List<Integer> li = Arrays.asList(1, 2, 3); 
  2. List<String> ls = Arrays.asList("one""two""three"); 
  3. printList(li); 
  4. printList(ls); 

 

責(zé)任編輯:姜華 來(lái)源: 淺羽的IT小屋
相關(guān)推薦

2009-04-23 14:30:19

UML建模

2013-09-29 10:36:08

VMware虛擬化

2023-10-04 00:03:00

SQL數(shù)據(jù)庫(kù)

2021-07-30 09:00:40

鴻蒙HarmonyOS應(yīng)用

2014-12-23 13:50:46

多播組播

2009-08-10 15:56:35

802局域網(wǎng)網(wǎng)橋兼容性

2013-09-03 13:01:01

團(tuán)隊(duì)管理團(tuán)隊(duì)

2021-02-05 17:35:07

數(shù)據(jù)高管CIO技術(shù)

2010-04-21 10:04:33

Oracle移植

2011-05-26 17:37:11

Ajax

2016-12-26 18:51:34

AndroidJavascriptJSONObject

2015-10-26 10:24:11

數(shù)據(jù)中心DCIM

2016-02-24 09:47:04

WiFi信號(hào)

2013-08-21 09:38:21

802.11acMeru5G wifi

2009-11-10 14:15:40

2010-07-12 13:00:49

UML建模

2012-07-04 14:40:37

Ajax

2013-05-14 09:17:10

企業(yè)無(wú)線網(wǎng)無(wú)縫漫游無(wú)線網(wǎng)

2017-03-17 11:00:08

數(shù)字化陳勇Gartner

2009-07-16 09:25:27

Java常量定義
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)