1. 程式人生 > >【Java面試題七】Java泛型篇

【Java面試題七】Java泛型篇

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

        泛型是Java SE 1.5的新特性,泛型的本質是引數化型別,也就是說所操作的資料型別被指定為一個引數。

好處:

        1、型別安全,提供編譯期間的型別檢測

        2、前後相容

        3、泛化程式碼,程式碼可以更多的重複利用

        4、效能較高,用GJ(泛型JAVA)編寫的程式碼可以為java編譯器和虛擬機器帶來更多的型別資訊,這些資訊對java程式做進一步優化提供條件。

2,Java的泛型是如何工作的 ? 什麼是型別擦除 ?如何工作?

       1、型別檢查:在生成位元組碼之前提供型別檢查

        2、型別擦除:所有型別引數都用他們的限定型別替換,包括類、變數和方法(型別擦除)

        3、如果型別擦除和多型性發生了衝突時,則在子類中生成橋方法解決

        4、如果呼叫泛型方法的返回型別被擦除,則在呼叫該方法時插入強制型別轉換

型別擦除:

        所有型別引數都用他們的限定型別替換:

比如T->Object   ? extends BaseClass->BaseClass

如何工作:

        泛型是通過型別擦除來實現的,編譯器在編譯時擦除了所有型別相關的資訊,所以在執行時不存在任何型別相關的資訊。例如 List<String>在執行時僅用一個List來表示。這樣做的目的,是確保能和Java 5之前的版本開發二進位制類庫進行相容。你無法在執行時訪問到型別引數,因為編譯器已經把泛型型別轉換成了原始型別。根據你對這個泛型問題的回答情況,你會得到一些後續提問,比如為什麼泛型是由型別擦除來實現的或者給你展示一些會導致編譯器出錯的錯誤泛型程式碼。

3,你可以把List<String>傳遞給一個接受List<Object>引數的方法嗎?

        對任何一個不太熟悉泛型的人來說,這個Java泛型題目看起來令人疑惑,因為乍看起來String是一種Object,所以 List<String>應當可以用在需要List<Object>的地方,但是事實並非如此。真這樣做的話會導致編譯錯誤。如果你再深一步考慮,你會發現Java這樣做是有意義的,因為List<Object>可以儲存任何型別的物件包括String, Integer等等,而List<String>卻只能用來儲存String s。

List<Object> objectList;

List<String> stringList;

objectList = stringList; //compilation error incompatible types

4,如何阻止Java中的型別未檢查的警告?

        如果你把泛型和原始型別混合起來使用,例如下列程式碼,java 5的javac編譯器會產生型別未檢查的警告,例如

List<String> rawList = newArrayList()

注意: Hello.java使用了未檢查或稱為不安全的操作;

這種警告可以使用@SuppressWarnings(“unchecked”)註解來遮蔽。

5,Java中List<Object>和原始型別List之間的區別?

        原始型別和帶引數型別<Object>之間的主要區別是,在編譯時編譯器不會對原始型別進行型別安全檢查,卻會對帶引數的型別進行檢查,通過使用Object作為型別,可以告知編譯器該方法可以接受任何型別的物件,比如String或Integer。

        這道題的考察點在於對泛型中原始型別的正確理解。它們之間的第二點區別是,你可以把任何帶引數的型別傳遞給原始型別List,但卻不能把List<String>傳遞給接受 List<Object>的方法,因為會產生編譯錯誤。

6,編寫一段泛型程式來實現LRU快取?

        對於喜歡Java程式設計的人來說這相當於是一次練習。給你個提示,LinkedHashMap可以用來實現固定大小的LRU快取,當LRU快取已經滿了的時候,它會把最老的鍵值對移出快取。

        LinkedHashMap提供了一個稱為removeEldestEntry()的方法,該方法會被put() 和putAll()呼叫來刪除最老的鍵值對。當然,如果你已經編寫了一個可執行的JUnit測試,你也可以隨意編寫你自己的實現程式碼。

7,Array中可以用泛型嗎?

        這可能是Java泛型面試題中最簡單的一個了,當然前提是你要知道Array事實上並不支援泛型,這也是為什麼Joshua Bloch在Effective Java一書中建議使用List來代替Array,因為List可以提供編譯期的型別安全保證,而Array卻不能。

8,如何編寫一個泛型方法,讓它能接受泛型引數並返回泛型型別?

        編寫泛型方法並不困難,你需要用泛型型別來替代原始型別,比如使用T, E or K,V等被廣泛認可的型別佔位符。最簡單的情況下,一個泛型方法可能會像這樣:

   public V put(K key, V value) {

   return cahe.put(key,value);

}

9,C++模板和java泛型之間有何不同?

        java泛型實現根植於“型別消除”這一概念。當原始碼被轉換為Java虛擬機器位元組碼時,這種技術會消除引數化型別。有了Java泛型,我們可以做的事情也並沒有真正改變多少;他只是讓程式碼變得漂亮些。鑑於此,Java泛型有時也被稱為“語法糖”。

        這和 C++模板截然不同。在 C++中,模板本質上就是一套巨集指令集,只是換了個名頭,編譯器會針對每種型別建立一份模板程式碼的副本。

由於架構設計上的差異,Java泛型和C++模板有很多不同點:

   C++模板可以使用int等基本資料型別。Java則不行,必須轉而使用Integer。

    在Java中,可以將模板的引數型別限定為某種特定型別。

    在C++中,型別引數可以例項化,但java不支援。

    在Java中,型別引數不能用於靜態方法(?)和變數,因為它們會被不同型別引數指定的例項共享。在C++,這些類時不同的,因此型別引數可以用於靜態方法和靜態變數。

        在Java中,不管型別引數是什麼,所有的例項變數都是同一型別。型別引數會在執行時被抹去。在C++中,型別引數不同,例項變數也不同。