面試中Java泛型問題一文搞定
微信公眾號:大黃奔跑
關注我,可瞭解更多有趣的面試相關問題。
寫在之前
本文主要向面試角度講述一個問題在面試中該如何回答,如果只想看問題答案,可以重點留意灰色背景的問題,其他的文字主要是擴充套件內容。
關於泛型,Jdk1.5
以後推出來的特性,已經推出被Java程式設計師熱捧,因為其實實在在解決一些編碼問題。下面我們來看看如此重要的泛型,在面試中會怎麼考察呢。
面試問題概覽
下面我羅列一些大廠面試中,對於泛型一些常見問題。
- 泛型瞭解嗎?介紹一下泛型?【快手】
- 泛型實現的原理,為什麼要實現泛型【阿里】
- 泛型實現原理 【去哪兒】
java
的泛型,super
和extend
的區別?泛型擦除的原理【華為】- 什麼是泛型,什麼時候需要利用泛型【位元組跳動】
java
的泛型,有什麼缺點【騰訊】
可以看到泛型在各個大廠面試中已經成為了筆考題目,回想自己當初校招,因為泛型回答不好而錯失阿里Offer,至今仍然悔恨不已。
真實面試回顧
一個身著灰色格子襯衫,拿著閃著碩大的?logo小哥迎面走來,我心想,logo還自帶發光的,這尼瑪肯定是p7大佬了,但是剛開始我們們還是得淡定不是。
大黃同學是吧,我看你簡歷上面寫熟悉
Java
基礎以及常見的Java
特性。那你先說說你對泛型的理解吧
此刻我們們得淡定,雖然自己準備過,也得慢慢道來。
面試官您好,泛型是JDK1.5之後提出的新概念,本意是讓同一套程式碼可以適應更多的型別。
大家都寫過議論文對吧,對於一個問題需要正反兩面說,優點、缺點,一一道來。
缺點:在沒有泛型之前一旦某個介面定義了引數為某個型別,則實現了該介面的方法必須採用同樣型別引數,不利於程式的擴充套件。
優點:
- 比如在建立容器的時候,指定容器持有何種型別,以便在編譯期間就能夠保證型別的正確性,而不是將錯誤留到執行的時候。
- 無論是建立類、方法的時候都可以泛化引數,可以做到程式碼的複用。
泛型的出現最主要目的為了更好的建立容器類
比如下面這個例子中,在定義Dog
類的時候不需要指定其屬性a的含義,只需要利用T
來表示,在new
該物件的時候指定對應的型別即可。
/**
*
* @param <T>
*/
public class Dog<T> {
private T a;
public Dog(T a) {
this.a = a;
}
public void set(T a) {
this.a = a;
}
public T get() {
return a;
}
public static void main(String[] args) {
// 在例項化的時候才確定型別
Dog<Integer> h3 = new Dog<Integer>(new Integer(3));
// 然後就可以不用強制型別轉化了
Integer a = h3.get();
// h3.set("Not an Automobile"); // Error
// h3.set(1.11); // Error
}
}
面試官摸了摸筆記本,心想,回答的還可以,繼續追問。
你剛才說泛型消除了型別之間的差異,那你知道jdk底層是如何做到的嗎?
大黃:
jdk是通過型別擦除來實現泛型的,編譯器在編譯之後會擦除了所有型別相關的資訊,所以在執行時不存在任何型別相關的資訊。比如如果編寫
List<String> names = new Arraylist<>();
在執行的時候,僅用List來表示。
先不要高興,面試官可能會繼續追問。
面試官:既然你說編譯時就已經擦除了,為什麼要用型別擦除呢?
大黃:這麼做是為了向前相容,為了相容jdk1.5以前的程式,確保能和Java 5之前的版本開發二進位制類庫進行相容。
好小夥,這都知道,看來之前做足了功課,還得繼續問問,試試深淺。
面試官:你知道泛型中的萬用字元吧,那你說一下什麼是限定萬用字元和非限定萬用字元。
大黃:限定萬用字元,是限定型別必須是某一個型別的子類。
比如:List<? extends Fruit> flist
表示具有任何從Fruit繼承的型別的列表。
而限定萬用字元:<? super Fruit> 確保型別必須是Fruit的父類來設定型別的下界。
大黃小提醒
不要意味new
一個List<? extends Fruit>
的flist
就可以把任何的水果塞到flist
裡面,很遺憾的告訴你,下面的程式編譯不通過的。
/**
* 泛型中萬用字元的應用
*/
public class GenericsAndCovariance {
public static void main(String[] args) {
// new一個水果籃子
List<? extends Fruit> flist = new ArrayList<Apple>();
// 下面編譯不會通過
flist.add(new Apple());
// 下面編譯不會通過
flist.add(new Orange());
// 下面編譯同樣不會通過
// flist.add(new Fruit());
}
}
看到這個程式是否感覺到有點懵,我自己建立的水果籃子,我無法往裡面放蘋果、橙子,甚至連籃子物件也不能放。那我這個籃子還有屁用哦。
是的,確實沒有什麼用,似乎和預期不太一樣。
但是想想也合乎道理。如果不知道list
持有什麼物件,那麼怎麼樣才能安全地向其中新增物件呢?如果允許了,則獲取元素的時候,就會出錯。
下面面試繼續:
面試官:那你再說說泛型可以用到什麼地方?
大黃:泛型即可用用於類定義中、介面的定義、方法的定義、同時也廣泛用於容器中。
- 在類中使用泛型,可以讓類中的屬性由泛型定義,而不需要提前知道屬性的型別。
- 在介面中使用泛型,可以讓介面方法的返回值按照各自實現自由定義。這也是常見的工廠設計模式的一種應用。
- 在方法中利用泛型,可以做到方法的複用,同一個方法可以傳入不同型別引數。實現時只需要將泛型引數列表置於返回值之前即可,JDK1.8中Stream中很多流式方法的定義採用了泛型。
記得加上這句話
我覺得,使用泛型機制最吸引人的地方,在使用容器的地方,比如List、Set、Map,因為在1.5以前,在泛型出現之前,當將一個物件放到容器中,這個物件會預設的轉化為Object型別,因此會丟失掉型別資訊,可能將一個Apple物件放到Orange容器中,然後試圖從Orange容器中獲取物件時,得到的是一個Apple,強制轉化必然出問題,會丟擲RuntimeException,但是有了泛型之後,這種問題在寫程式碼,也就是說在編譯期間就會暴露,而不是在執行時暴露。
比如下面定義介面用於生成不同的物件:
/**
* 定義一個生成器
* @param <T>
*/
public interface Generator<T> {
/**
* 定義生成下一個物件
* @return
*/
T next();
}
/**
* 利用泛型生成器來生成費波列切數
*/
public class Fibonacci implements Generator<Integer> {
private int count = 0;
public static void main(String[] args) {
Fibonacci gen = new Fibonacci();
for (int i = 0; i < 18; i++)
System.out.print(gen.next() + " ");
}
/**
* 實現介面的方法,返回值為Integer
* @return
*/
public Integer next() {
return fib(count++);
}
/**
* 定義費波列切數
* @param n
* @return
*/
private int fib(int n) {
if (n < 2) return 1;
return fib(n - 2) + fib(n - 1);
}
}
泛型方法的應用:
public class GenericMethods {
public static void main(String[] args) {
GenericMethods gm = new GenericMethods();
gm.f("");
gm.f(1);
gm.f(gm);
}
/**
* 定義泛型方法
* @param x 方法的引數為泛型
* @param <T>
*/
public <T> void f(T x) {
System.out.println(x.getClass().getName());
}
}
回答到這裡,關於泛型的問題基本上總結的差不多了。
總結
本身主要圍繞開頭的幾個真正的面試題展開,簡單來說,泛型是什麼?為什麼要有泛型?泛型如何實現的?泛型有哪些用處。
個人觀點,一個技術從剛開始學習的時候,從四方面思考,能夠事半功倍。
最後大黃分享多年面試心得。面試中,面對一個問題,大概按照總分的邏輯回答即可。先直接丟擲結論,然後舉例論證自己的結論。一定要第一時間抓住面試官的心裡,否則容易給人抓不著重點或者不著邊際的印象。
番外
另外,關注大黃奔跑公眾號,第一時間收穫獨家整理的面試實戰記錄及面試知識點總結。
我是大黃,一個只會寫HelloWorld
的程式設計師,我們們下期見。
相關文章
- Java泛型型別擦除問題Java泛型型別
- 【Java面試題】之泛型相關Java面試題泛型
- java泛型應該注意的問題。Java泛型
- Java 基礎 一文搞懂泛型Java泛型
- Java中的泛型Java泛型
- 一篇搞定面試中的跨域問題面試跨域
- Java中的泛型方法Java泛型
- 15個問題告訴你如何使用Java泛型Java泛型
- Golang面試:泛型Golang面試泛型
- 【搞定 Java 併發面試】面試最常問的 Java 併發基礎常見面試題總結!Java面試題
- 提給程式設計師的10道Java泛型面試題程式設計師Java泛型面試題
- 【java】【泛型】泛型geneticJava泛型
- java泛型之泛型方法。Java泛型
- 【006期】JavaSE面試題(六):泛型Java面試題泛型
- Java 中泛型的協變Java泛型
- Java 中泛型的全面解析Java泛型
- 深入解析Java中的泛型Java泛型
- 遇到個小問題,Java泛型真的是雞肋嗎?Java泛型
- Java泛型知識點:泛型類、泛型介面和泛型方法Java泛型
- java泛型之泛型陣列。Java泛型陣列
- Java 泛型Java泛型
- Java泛型Java泛型
- java中泛型之型別萬用字元(?)Java泛型型別字元
- Java中建立泛型型別的例項Java泛型型別
- Java 中的泛型方法及 FunctionJava泛型Function
- Java 泛型中易混淆的地方Java泛型
- Java 泛型中的萬用字元Java泛型字元
- C++中泛型使用導致的膨脹問題C++泛型
- 一文理解TS泛型泛型
- 面試官:十問泛型,你能扛住嗎?面試泛型
- java面試問題Java面試
- Java中泛型的詳細解析,深入分析泛型的使用方式Java泛型
- Java 泛型原理Java泛型
- Java+泛型Java泛型
- java泛型一二Java泛型
- Java(7)泛型Java泛型
- Java-泛型Java泛型
- java泛型剖析Java泛型