天天看點

java面試題(十)1)、Java泛型的特點有哪些?2)Map、Set、List、Queue、Stack的特點與用法3)你知道JVM常見垃圾回收算法的方法嗎?4)java中哪些地方可以存儲資料

1)、Java泛型的特點有哪些?

特點:

(1)将運作時間的問題ClassCastException轉到了編譯時期。

(2)避免了強制轉換的麻煩。

(3)泛型可以用于明确一個集合中存儲什麼類型的元素

(4)運作時,會将泛型去掉,生成的class檔案中是不帶有泛型的,這個稱為泛型的擦除。

1、什麼是泛型?

泛型的定義:泛型==參數化類型。 

       泛型是JDK 1.5的一項新特性,它的本質是參數化類型(Parameterized Type)的應用,也就是說所操作的資料類型被指定為一個參數,在用到的時候再指定具體的類型。

2、泛型如何實作?

      泛型是通過類型擦除來實作的,編譯器在編譯時擦除了所有類型相關的資訊,是以在運作時不存在任何類型相關的資訊。例如List<String>在運作時僅用一個List來表示。這樣做的目的,是確定能和Java 5之前的版本開發二進制類庫進行相容。你無法在運作時通路到類型參數,因為編譯器已經把泛型類型轉換成了原始類型。

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

  這是另一個非常流行的Java泛型面試題。限定通配符對類型進行了限制。有兩種限定通配符,一種是<? extends T>它通過確定類型必須是T的子類來設定類型的上界,另一種是<? super T>它通過確定類型必須是T的父類來設定類型的下界。泛型類型必須用限定内的類型來進行初始化,否則會導緻編譯錯誤。另一方面<?>表示了非限定通配符,因為<?>可以用任意類型來替代。

4. List<? extends T>和List <? super T>之間有什麼差別 ?

  這和上一個面試題有聯系,有時面試官會用這個問題來評估你對泛型的了解,而不是直接問你什麼是限定通配符和非限定通配符。這兩個List的聲明都是限定通配符的例子,List<? extends T>可以接受任何繼承自T的類型的List,而List<? super T>可以接受任何T的父類構成的List。例如List<? extends Number>可以接受List<Integer>或List<Float>。

2)Map、Set、List、Queue、Stack的特點與用法

Map

  • Map是鍵值對,鍵Key是唯一不能重複的,一個鍵對應一個值,值可以重複。
  • TreeMap可以保證順序。
  • HashMap不保證順序,即為無序的。
  • Map中可以将Key和Value單獨抽取出來,其中KeySet()方法可以将所有的keys抽取正一個Set。而Values()方法可以将map中所有的values抽取成一個集合。

Set

  • 不包含重複元素的集合,set中最多包含一個null元素。
  • 隻能用Lterator實作單項周遊,Set中沒有同步方法。

List

  • 有序的可重複集合。
  • 可以在任意位置增加删除元素。
  • 用Iterator實作單向周遊,也可用ListIterator實作雙向周遊。

Queue

  • Queue遵從先進先出原則。
  • 使用時盡量避免add()和remove()方法,而是使用offer()來添加元素,使用poll()來移除元素,它的優點是可以通過傳回值來判斷是否成功。
  • LinkedList實作了Queue接口。
  • Queue通常不允許插入null元素。

Stack

  • Stack遵從後進先出原則。
  • Stack繼承自Vector。
  • 它通過五個操作對類Vector進行擴充,允許将向量視為堆棧,它提供了通常的push和pop操作,以及取堆棧頂點的peek()方法、測試堆棧是否為空的empty方法等。

用法

  • 如果涉及堆棧,隊列等操作,建議使用List。
  • 對于快速插入和删除元素的,建議使用LinkedList。
  • 如果需要快速随機通路元素的,建議使用ArrayList。

更為精煉的總結

Collection 是對象集合, Collection 有兩個子接口 List 和 Set

List 可以通過下标 (1,2..) 來取得值,值可以重複。 

Set 隻能通過遊标來取值,并且值是不能重複的。

ArrayList , Vector , LinkedList 是 List 的實作類

  • ArrayList 是線程不安全的, Vector 是線程安全的,這兩個類底層都是由數組實作的。
  • LinkedList 是線程不安全的,底層是由連結清單實作的。

Map 是鍵值對集合

  • HashTable 和 HashMap 是 Map 的實作類。
  • HashTable 是線程安全的,不能存儲 null 值。
  • HashMap 不是線程安全的,可以存儲 null 值。

Stack類:繼承自Vector,實作一個後進先出的棧。提供了幾個基本方法,push、pop、peak、empty、search等。

Queue接口:提供了幾個基本方法,offer、poll、peek等。已知實作類有LinkedList、PriorityQueue等。

3)你知道JVM常見垃圾回收算法的方法嗎?

GC算法

1.标記-壓縮算法(或稱為标記-整理算法,Java堆中老年代的垃圾回收算法)

對于新生代,大部分對象都不會存活,是以在新生代中使用複制算法較為高效,而對于老年代來講,大部分對象可能會繼續存活下去,如果此時還是利用複制算法,效率則會降低。标記-壓縮算法首先還是“标記”,标記過後,将不用回收的記憶體對象壓縮到記憶體一端,此時即可直接清除邊界處的記憶體,這樣就能避免複制算法帶來的效率問題,同時也能避免記憶體碎片化的問題。老年代的垃圾回收稱為“Major GC”。

2.複制算法(Java堆中新生代的垃圾回收算法)(注意圖檔下方群)

此GC算法實際上解決了标記-清除算法帶來的“記憶體碎片化”問題。首先還是先标記處待回收記憶體和不用回收的記憶體,下一步将不用回收的記憶體複制到新的記憶體區域,這樣舊的記憶體區域就可以全部回收,而新的記憶體區域則是連續的。它的缺點就是會損失掉部分系統記憶體,因為你總要騰出一部分記憶體用于複制。

在上文《JVM入門——運作時資料區》提到過在Java堆中被分為了新生代和老年代,這樣的劃分是友善GC。Java堆中的新生代就使用了GC複制算法。在新生代中又分為了三個區域:Eden 空間、To Survivor空間、From Survivor空間。不妨将注意力回到這張圖的左邊新生代部分:

java面試題(十)1)、Java泛型的特點有哪些?2)Map、Set、List、Queue、Stack的特點與用法3)你知道JVM常見垃圾回收算法的方法嗎?4)java中哪些地方可以存儲資料

568765867

新的對象執行個體被建立的時候通常在Eden空間,發生在Eden空間上的GC稱為Minor GC,當在新生代發生一次GC後,會将Eden和其中一個Survivor空間的記憶體複制到另外一個Survivor中,如果反複幾次有對象一直存活,此時記憶體對象将會被移至老年代。可以看到新生代中Eden占了大部分,而兩個Survivor實際上占了很小一部分。這是因為大部分的對象被建立過後很快就會被GC(這裡也許運用了是二八原則)。

3.标記-清除算法

等待被回收對象的“标記”過程在上文已經提到過,如果在被标記後直接對對象進行清除,會帶來另一個新的問題——記憶體碎片化。如果下次有比較大的對象執行個體需要在堆上配置設定較大的記憶體空間時,可能會出現無法找到足夠的連續記憶體而不得不再次觸發垃圾回收。

77、GC是什麼? 為什麼要有GC?   

GC是垃圾收集的意思(Gabage Collection),記憶體處理是程式設計人員容易出現問題的地方,忘記或者錯誤的記憶體回收會導緻程式或系統的不穩定甚至崩潰,Java提供的GC功能可以自動監測對象是否超過作用域進而達到自動回收記憶體的目的,Java語言沒有提供釋放已配置設定記憶體的顯示操作方法。

78、垃圾回收的優點和原理。并考慮2種回收機制。

Java語言中一個顯著的特點就是引入了垃圾回收機制,使c++程式員最頭疼的記憶體管理的問題迎刃而解,它使得Java程式員在編寫程式的時候不再需要考慮記憶體管理。由于有個垃圾回收機制,Java中的對象不再有"作用域"的概念,隻有對象的引用才有"作用域"。垃圾回收可以有效的防止記憶體洩露,有效的使用可以使用的記憶體。垃圾回收器通常是作為一個單獨的低級别的線程運作,不可預知的情況下對記憶體堆中已經死亡的或者長時間沒有使用的對象進行清除和回收,程式員不用實時的調用垃圾回收器對某個對象或所有對象進行垃圾回收。回收機制有分代複制垃圾回收和标記垃圾回收,增量垃圾回收。

79、垃圾回收器的基本原理是什麼?垃圾回收器可以馬上回收記憶體嗎?有什麼辦法主動通知虛拟機進行垃圾回收?

對于GC來說,當程式員建立對象時,GC就開始監控這個對象的位址、大小以及使用情況。通常,GC采用有向圖的方式記錄和管理堆(heap)中的所有對象。通過這種方式确定哪些對象是"可達的",哪些對象是"不可達的"。當GC确定一些對象為"不可達"時,GC就有責任回收這些記憶體空間。可以。程式員可以手動執行System.gc(),通知GC運作,但是Java語言規範并不保證GC一定會執行。

4)java中哪些地方可以存儲資料

   在JAVA 中,有六個不同的地方可以存儲資料:

1. 寄存器(register)。這是最快的存儲區,因為它位于不同于其他存儲區的地方——處理器内部。但是寄存器的數量極其有限,是以寄存器由編譯器根據需求進行配置設定。你不能直接控制,也不能在程式中感覺到寄存器存在的任何迹象。 

2. 堆棧(stack)。位于通用RAM中,但通過它的“堆棧指針”可以從處理器哪裡獲得支援。堆棧指針若向下移動,則配置設定新的記憶體;若向上移動,則釋放那些記憶體。這是一種快速有效的配置設定存儲方法,僅次于寄存器。建立程式時候,JAVA 編譯器必須知道存儲在堆棧内所有資料的确切大小和生命周期,因為它必須生成相應的代碼,以便上下移動堆棧指針。這一限制限制了程式的靈活性,是以雖然某些JAVA 資料存儲在堆棧中——特别是對象引用,但是JAVA 對象不存儲其中。 

3. 堆(heap)。一種通用性的記憶體池(也存在于RAM中),用于存放是以的JAVA 對象。堆不同于堆棧的好處是:編譯器不需要知道要從堆裡配置設定多少存儲區域,也不必知道存儲的資料在堆裡存活多長時間。是以,在堆裡配置設定存儲有很大的靈活性。當你需要建立一個對象的時候,隻需要new寫一行簡單的代碼,當執行這行代碼時,會自動在堆裡進行存儲配置設定。當然,為這種靈活性必須要付出相應的代碼。用堆進行存儲配置設定比用堆棧進行存儲存儲需要更多的時間。 深入探讨java中引用的行為

4. 靜态存儲(static storage)。這裡的“靜态”是指“在固定的位置”。靜态存儲裡存放程式運作時一直存在的資料。你可用關鍵字static來辨別一個對象的特定元素是靜态的,但JAVA 對象本身從來不會存放在靜态存儲空間裡。 

5. 常量存儲(constant storage)。常量值通常直接存放在程式代碼内部,這樣做是安全的,因為它們永遠不會被改變。有時,在嵌入式系統中,常量本身會和其他部分分割離開,是以在這種情況下,可以選擇将其放在ROM中 

6. 非RAM存儲。如果資料完全存活于程式之外,那麼它可以不受程式的任何控制,在程式沒有運作時也可以存在。 

就速度來說,有如下關系: 

    寄存器 < 堆棧 < 堆 < 其他 

『上面這段話摘取之《Thinking in Java 》』 

在這裡,主要要說下堆與堆棧的關系: 

      堆:堆是heap,是所謂的動态記憶體,其中的記憶體在不需要時可以回收,以配置設定給新的記憶體請求,其記憶體中的資料是無序的,即先配置設定的和随後配置設定的記憶體并沒有什麼必然的位置關系,釋放時也可以沒有先後順序。一般由使用者自由配置設定,malloc配置設定的就是堆,需要手動釋放。 

      堆棧:就是STACK。實際上是隻有一個出入口的隊列,即後進先出(First     In     Last     Out),先配置設定的記憶體必定後釋放。一般由,由系統自動配置設定,存放存放函數的參數值,局部變量等,自動清除。 

還有,堆是全局的,堆棧是每個函數進入的時候分一小塊,函數傳回的時候就釋放了,靜态和全局變量,new     得到的變量,都放在堆中,局部變量放在堆棧中,是以函數傳回,局部變量就全沒了。 

其實在實際應用中,堆棧多用來存儲方法的調用。而對則用于對象的存儲。 

       JAVA 中的基本類型,其實需要特殊對待。因為,在JAVA 中,通過new建立的對象存儲在“堆”中,是以用new 建立一個小的、簡單的變量,如基本類型等,往往不是很有效。是以,在JAVA 中,對于這些類型,采用了與C、C++相同的方法。也就是說,不用new 來建立,而是建立一個并非是“引用”的“自動”變量。這個變量擁有它的“值”,并置于堆棧中,是以更高效。