java泛型常見面試題

背景:泛型這個知識點平時用的很少,可是在面試的時候很容就被問到,因此仍是要準備一些基礎的知識儲備。java

面試舊敵之 Java 泛型 :主要概念及特色面試

「泛型」 意味着編寫的代碼能夠被不一樣類型的對象所重用。編程

泛型是在JDK1.5以後出現的。數組

泛型的本質是參數化類型,也就是說所操做的數據類型被指定爲一個參數。安全

能夠看到,使用 Object 來實現通用、不一樣類型的處理,有這麼兩個缺點:性能

  1. 每次使用時都須要強制轉換成想要的類型
  2. 在編譯時編譯器並不知道類型轉換是否正常,運行時才知道,不安全

根據《Java 編程思想》中的描述,泛型出現的動機在於:spa

有許多緣由促成了泛型的出現,而最引人注意的一個緣由,就是爲了建立容器類.net

實際上引入泛型的主要目標有如下幾點:翻譯

    • 類型安全 
      • 泛型的主要目標是提升 Java 程序的類型安全
      • 編譯時期就能夠檢查出因 Java 類型不正確致使的 ClassCastException 異常
      • 符合越早出錯代價越小原則
    • 消除強制類型轉換 
      • 泛型的一個附帶好處是,使用時直接獲得目標類型,消除許多強制類型轉換
      • 所得即所需,這使得代碼更加可讀,而且減小了出錯機會
    • 潛在的性能收益 
      • 因爲泛型的實現方式,支持泛型(幾乎)不須要 JVM 或類文件更改
      • 全部工做都在編譯器中完成
      • 編譯器生成的代碼跟不使用泛型(和強制類型轉換)時所寫的代碼幾乎一致,只是更能確保類型安全而已

泛型類 code

泛型接口

泛型方法

泛型的通配符

無限制通配符

extends 關鍵字聲明瞭類型的上界,表示參數化的類型多是所指定的類型,或者是此類型的子類

super 關鍵字聲明瞭類型的下界,表示參數化的類型多是指定的類型,或者是此類型的父類

通配符比較

經過上面的例子咱們能夠知道,無限制通配符 < ?> 和 Object 有些類似,用於表示無限制或者不肯定範圍的場景。

兩種有限制通配形式 < ? super E> 和 < ? extends E> 也比較容易混淆,咱們再來比較下。

你瞭解泛型通配符與上下界嗎?

ps:上下限用圖說明

它們的目的都是爲了使方法接口更爲靈活,能夠接受更爲普遍的類型。

  • < ? super E> 用於靈活寫入或比較,使得對象能夠寫入父類型的容器,使得父類型的比較方法能夠應用於子類對象。
  • < ? extends E> 用於靈活讀取,使得方法能夠讀取 E 或 E 的任意子類型的容器對象。

用《Effective Java》 中的一個短語來加深理解:

爲了得到最大限度的靈活性,要在表示 生產者或者消費者 的輸入參數上使用通配符,使用的規則就是:生產者有上限、消費者有下限:

PECS: producer-extends, costumer-super

所以使用通配符的基本原則:

  • 若是參數化類型表示一個 T 的生產者,使用 < ? extends T>;
  • 若是它表示一個 T 的消費者,就使用 < ? super T>;
  • 若是既是生產又是消費,那使用通配符就沒什麼意義了,由於你須要的是精確的參數類型。

小總結一下:

  • T 的生產者的意思就是結果會返回 T,這就要求返回一個具體的類型,必須有上限纔夠具體;
  • T 的消費者的意思是要操做 T,這就要求操做的容器要夠大,因此容器須要是 T 的父類,即 super T;

泛型的類型擦除

Java 中的泛型和 C++ 中的模板有一個很大的不一樣:

  • C++ 中模板的實例化會爲每一種類型都產生一套不一樣的代碼,這就是所謂的代碼膨脹。
  • Java 中並不會產生這個問題。虛擬機中並無泛型類型對象,全部的對象都是普通類。

(摘自:blog.csdn.net/fw0124/arti…

在 Java 中,泛型是 Java 編譯器的概念,用泛型編寫的 Java 程序和普通的 Java 程序基本相同,只是多了一些參數化的類型同時少了一些類型轉換。

實際上泛型程序也是首先被轉化成通常的、不帶泛型的 Java 程序後再進行處理的,編譯器自動完成了從 Generic Java 到普通 Java 的翻譯,Java 虛擬機運行時對泛型基本一無所知。

當編譯器對帶有泛型的java代碼進行編譯時,它會去執行類型檢查和類型推斷,而後生成普通的不帶泛型的字節碼,這種普通的字節碼能夠被通常的 Java 虛擬機接收並執行,這在就叫作 類型擦除(type erasure)

泛型的規則

  • 泛型的參數類型只能是類(包括自定義類),不能是簡單類型。
  • 同一種泛型能夠對應多個版本(由於參數類型是不肯定的),不一樣版本的泛型類實例是不兼容的。
  • 泛型的類型參數能夠有多個
  • 泛型的參數類型可使用 extends 語句,習慣上稱爲「有界類型」
  • 泛型的參數類型還能夠是通配符類型,例如 Class

泛型的使用場景

當類中要操做的引用數據類型不肯定的時候,過去使用 Object 來完成擴展,JDK 1.5後推薦使用泛型來完成擴展,同時保證安全性。

總結

1.上面說到使用 Object 來達到複用,會失去泛型在安全性和直觀表達性上的優點,那爲何 ArrayList 等源碼中的還能看到使用 Object 做爲類型?

根據《Effective Java》中所述,這裏涉及到一個 「移植兼容性」

泛型出現時,Java 平臺即將進入它的第二個十年,在此以前已經存在了大量沒有使用泛型的 Java 代碼。人們認爲讓這些代碼所有保持合法,而且可以與使用泛型的新代碼互用,很是重要。

這樣都是爲了兼容,新代碼裏要使用泛型而不是原始類型。

2.泛型是經過擦除來實現的。所以泛型只在編譯時強化它的類型信息,而在運行時丟棄(或者擦除)它的元素類型信息。擦除使得使用泛型的代碼能夠和沒有使用泛型的代碼隨意互用。

3.若是類型參數在方法聲明中只出現一次,能夠用通配符代替它。

好比下面的 swap 方法,用於交換指定 List 中的兩個位置的元素:

private  void swap(List list, int i, int j) {
    //...
}

只出現了一次 類型參數,沒有必要聲明,徹底能夠用通配符代替:

private void swap(List list, int i, int j){
    //...
}

對比一下,第二種更加簡單清晰吧。

4.數組中不能使用泛型

這多是 Java 泛型面試題中最簡單的一個了,固然前提是你要知道 Array 事實上並不支持泛型,這也是爲何 Joshua Bloch 在 《Effective Java》一書中建議使用 List 來代替 Array,由於 List 能夠提供編譯期的類型安全保證,而 Array 卻不能。

5.Java 中 List 和原始類型 List 之間的區別?

原始類型和帶參數類型 之間的主要區別是:

  • 在編譯時編譯器不會對原始類型進行類型安全檢查,卻會對帶參數的類型進行檢查
  • 經過使用 Object 做爲類型,能夠告知編譯器該方法能夠接受任何類型的對象,好比String 或 Integer
  • 你能夠把任何帶參數的類型傳遞給原始類型 List,但卻不能把 List< String> 傳遞給接受 List< Object> 的方法,由於泛型的不可變性,會產生編譯錯誤。

這道題的考察點在於對泛型中原始類型的正確理解。

ps:這個地方還沒理解。。。。

相關文章
相關標籤/搜索