京東面試題 Java相關


  1.JVM的內存結構和管理機制;

JVM實例:一個獨立運行的java程序,是進程級別

JVM執行引擎:用戶運行程序的線程,是JVM實例的一部分

JVM實例的誕生
當啟動一個java程序時.一個JVM實例就誕生了,任何一個擁有public static void main(string[] args)的函數都可以作為實例的運行啟點

      2.  JVM實例運行

main作為程序初始化線程的起點,任何其他線程由其啟動。

JVM有兩種線程:守護線程和非守護線程。守護線程由JVM使用。main啟動后將是非守護線程。

      3.  JVM實例消亡

   當程序中所有非守護線程都中止時,JVM退出;若安全管理器允許,程序也可以使用

Runtime類或者System.exit()退出。
JVM的生命周期
JVM主要包括四個部分:
1.類加載器(ClassLoader):在JVM啟動時或者在類運行時將需要的class加載到JVM中。(右圖表示了從java源文件到JVM的整個過程,可配合理解。 關於類的加載機制,可以參考http://blog.csdn.net/tonytfjing/article/details/47212291)
2.執行引擎:負責執行class文件中包含的字節碼指令(執行引擎的工作機制,這里也不細說了,這里主要介紹JVM結構);
3.內存區(也叫運行時數據區):是在JVM運行的時候操作所分配的內存區。運行時內存區主要可以划分為5個區域
      方法區(Method Area):用於存儲類結構信息的地方,包括常量池、靜態變量、構造函數等。雖然JVM規范把方法區描述為堆的一個邏輯部分, 但它卻有個別名non-heap(非堆),所以大家不要搞混淆了。方法區還包含一個運行時常量池。
      java堆(Heap):存儲java實例或者對象的地方。這塊是GC的主要區域(后面解釋)。從存儲的內容我們可以很容易知道,方法區和堆是被所有java線程共享的。
       java棧(Stack):java棧總是和線程關聯在一起,每當創建一個線程時,JVM就會為這個線程創建一個對應的java棧。在這個java棧中又會包含多個棧幀,每運行一個方法就創建一個棧幀,用於存儲局部變量表、操作棧、方法返回值等。每一個方法從調用直至執行完成的過程,就對應一個棧幀在java棧中入棧到出棧的過程。所以java棧是現成私有的。
        程序計數器(PC Register):用於保存當前線程執行的內存地址。由於JVM程序是多線程執行的(線程輪流切換),所以為了保證線程切換回來后,還能恢復到原先狀態,就需要一個獨立的計數器,記錄之前中斷的地方,可見程序計數器也是線程私有的。
        本地方法棧(Native Method Stack):和java棧的作用差不多,只不過是為JVM使用到的native方法服務的。
4.本地方法接口:主要是調用C或C++實現的本地方法及返回結果。
JVM主要包括四個部分:
Method Area 方法區又稱永久代(Permanent Generation)
       方法區是被所有線程共享,該區域保存所有字段和方法字節碼,以及一些特殊方法如構造函數,接口代碼也在此定義。

       方法區中存放了每個Class的結構信息,包括常量池、字段描述、方法描述等等。

      VM Space 描述中對這個區域的限制非常寬松,除了和Java堆一樣不需要連續的內存,也可以選擇固定大小或者可擴展外,甚至可以選擇不實現垃圾收集。相對來說,垃圾收集行為在這個區域是相對比較少發生的,但並不是某些描述那樣永久代不會發生GC,這里的GC主要是對常量池的回收和對類的卸載,雖然回收的“成績”一般也比較差強人意,尤其是類卸載,條件相當苛刻。

      運行時常量池(Runtime Constant Pool)也方法區存放。Class文件中除了有類的版本、字段、方法、接口等描述等信息外,還有一項信息是常量表(constant_pool table),用於存放編譯期已可知的常量,這部分內容將在類加載后進入方法區(永久代)存放。但是Java語言並不要求常量一定只有編譯期預置入Class的常量表的內容才能進入方法區常量池,運行期間也可將新內容放入常量池(最典型的String.intern()方法)。 運行時常量池是方法區的一部分,自然受到方法區內存的限制,當常量池無法在申請到內存時會拋出OutOfMemoryError異常

      2. PC Register程序計數器

每個線程都有一個程序計數器,就是一個指針,指向方法區中的方法字節碼,由執行引擎讀取下一條指令。每一個Java線程都有一個程序計數器來用於保存程序執行到當前方法的哪一個指令,對於非Native方法,這個區域記錄的是正在執行的VM原語的地址,如果正在執行的是Natvie方法,這個區域則為空(undefined)。此內存區域是唯一一個在VM Spec中沒有規定任何OutOfMemoryError情況的區域。

      3.Native Method Stack 本地方法棧

本地方法棧是為虛擬機使用到的Native方法服務。它的實現的語言、方式與結構並沒有強制規定,甚至有的虛擬機(譬如Sun Hotspot虛擬機)直接就把本地方法棧和JVM棧合二為一。這個區域也會拋出StackOverflowError和OutOfMemoryError異常。

      4.Stack棧也叫棧內存

VM棧的生命周期也是與線程相同。VM棧描述的是Java方法調用的內存模型:每個方法被執行的時候,都會同時創建一個幀(Frame)用於存儲本地變量表、操作棧、動態鏈接、方法出入口等信息。每一個方法的調用至完成,就意味着一個幀在VM棧中的入棧至出棧的過程。

JVM的內存指令區。是在線程創建時創建,它的生命期是跟隨線程的生命期,線程結束棧內存也就釋放,對於棧來說不存在垃圾回收問題,只要線程一結束,該棧就Over。問題出來了:棧中存的是那些數據呢?又什么是格式呢?

棧中的數據都是以棧幀(Stack Frame)的格式存在,棧幀是一個內存區塊,是一個數據集,是一個有關方法(Method)和運行期數據的數據集,當一個方法A被調用時就產生了一個棧幀F1,並被壓入到棧中,A方法又調用了B方法,於是產生棧幀F2也被壓入棧,執行完畢后,先彈出F2棧幀,再彈出F1棧幀,遵循“先進后出”原則。

那棧幀中到底存在着什么數據呢?棧幀中主要保存3類數據:本地變量(Local Variables),包括輸入參數和輸出參數以及方法內的變量;棧操作(Operand Stack),記錄出棧、入棧的操作;棧幀數據(Frame Data),包括類文件、方法等。

VM棧中各個幀的本地變量表部分,本地變量表存放了編譯期可知的各種標量類型(boolean、byte、char、short、int、float、long、double)、對象引用(不是對象本身,僅僅是一個引用指針)、方法返回地址等。其中long和double會占用2個本地變量空間(32bit),其余占用1個。

本地變量表在進入方法時進行分配,當進入一個方法時,這個方法需要在幀中分配多大的本地變量是一件完全確定的事情,在方法運行期間不改變本地變量表的大小。

在VM Spec中對這個區域規定了2中異常狀況:如果線程請求的棧深度大於虛擬機所允許的深度,將拋出StackOverflowError異常;如果VM棧可以動態擴展(VM Spec中允許固定長度的VM棧),當擴展時無法申請到足夠內存則拋出OutOfMemoryError異常。

       5. Heap 堆內存

一個JVM實例只存在一個堆類存,堆內存的大小是可以調節的。

Java堆可以處於物理上不連續的內存空間,它邏輯上是連續的即可,就像我們的磁盤空間一樣。實現時可以選擇實現成固定大小的,也可以是可擴展的,

不過當前所有商業的虛擬機都是按照可擴展來實現的(通過-Xmx和-Xms控制)。如果在堆中無法分配內存,並且堆也無法再擴展時,將會拋出OutOfMemoryError異常。

類加載器讀取了類文件后,需要把類、方法、常變量放到堆內存中,以方便執行器執行,堆內存分為三部分:

1)        Permanent Space永久存儲區(也叫方法區)

永久存儲區是一個常駐內存區域,用於存放JDK自身所攜帶的Class,Interface的元數據,也就是說它存儲的是運行環境必須的類信息,被裝載進此區域的數據是不會被垃圾回收器回收掉的,關閉JVM才會釋放此區域所占用的內存。

2)        Young Generation Space 新生區

新生區是類的誕生、成長、消亡的區域,一個類在這里產生,應用,最后被垃圾回收器收集,結束生命。新生區又分為兩部分:伊甸區(Eden space)和幸存者區(Survivor pace),所有的類都是在伊甸區被new出來的。幸存區有兩個: 0區(Survivor 0 space)和1區(Survivor 1 space)。當伊甸園的空間用完時,程序又需要創建對象,JVM的垃圾回收器將對伊甸園區進行垃圾回收,將伊甸園區中的不再被其他對象所引用的對象進行銷毀。然后將伊甸園中的剩余對象移動到幸存0區。若幸存0區也滿了,再對該區進行垃圾回收,然后移動到1區。那如果1區也滿了呢?再移動到養老區。

3)        Tenure generation space養老區

養老區用於保存從新生區篩選出來的JAVA對象,一般池對象都在這個區域活躍。
   6. 本機直接內存(Direct Memory)

直接內存並不是虛擬機運行時數據區的一部分,它根本就是本機內存而不是VM直接管理的區域。但是這部分內存也會導致OutOfMemoryError異常出現,因此我們放到這里一起描述。在JDK1.4中新加入了NIO類,引入一種基於渠道與緩沖區的I/O方式,它可以通過本機Native函數庫直接分配本機內存,然后通過一個存儲在Java堆里面的DirectByteBuffer對象作為這塊內存的引用進行操作。這樣能在一些場景中顯著提高性能,因為避免了在Java對和本機堆中來回復制數據。

顯然本機直接內存的分配不會受到Java堆大小的限制,但是即然是內存那肯定還是要

受到本機物理內存(包括SWAP區或者Windows虛擬內存)的限制的,一般服務器管理員配置JVM參數時,

會根據實際內存設置-Xmx等參數信息,但經常忽略掉直接內存,使得各個內存區域總和大於物理內存限制(包括物理的和操作系統級的限制),而導致動態擴展時出現OutOfMemoryError異常。
JVM的內存管理

  Java堆和棧的區別

內存泄漏:分配出去的內存回收不了

內存溢出:指系統內存不夠用了
Java內存泄露和內存溢出

       2.JVM的垃圾回收機制;

JVM中自動的對象內存回收機制稱為GC(Garbage Collection)。

為什么要進行垃圾回收?隨着程序的運行,內存中存在的實例對象、變量等信息占據的內存越來越多,如果不及時進行垃圾回收,必然會帶來程序性能的下降,甚至會因為可用內存不足造成一些不必要的系統異常。

哪些“垃圾”需要回收?

在我們上面介紹的六大區中,有三個是不需要進行垃圾回收的:程序計數器、JVM棧、本地方法棧。因為它們的生命周期是和線程同步的,隨着線程的銷毀,它們占用的內存會自動釋放,所以只有方法區和堆需要進行GC。方法區回收也很少,一般是堆需要GC,具體到哪些對象的話,簡單概況一句話:如果某個對象已經不存在任何引用,那么它可以被回收。通俗解釋一下就是說,如果一個對象,已經沒有什么作用了,就可以被當廢棄物被回收了。

什么時候進行垃圾回收?

根據一個經典的引用計數算法,每個對象添加一個引用計數器,每被引用一次,計數器加1,失去引用,計數器減1,當計數器在一段時間內保持為0時,該對象就認為是可以被回收得了。但是,這個算法有明顯的缺陷:當兩個對象相互引用,但是二者已經沒有作用時,按照常規,應該對其進行垃圾回收,但是其相互引用,又不符合垃圾回收的條件,因此無法完美處理這塊內存清理,因此Sun的JVM並沒有采用引用計數算法來進行垃圾回收。而是采用一個叫:根搜索算法
基本思想就是:從一個叫GC Roots的對象開始,向下搜索,如果一個對象不能到達GC Roots對象的時候,說明它已經不再被引用,即可被進行垃圾回收(此處 暫且這樣理解,其實事實還有一些不同,當一個對象不再被引用時,並沒有完全“死亡”,如果類重寫了finalize()方法,且沒有被系統調用過,那么系統會調用一次finalize()方法,以完成最后的工作,在這期間,如果可以將對象重新與任何一個和GC Roots有引用的對象相關聯,則該對象可以“重生”,如果不可以,那么就說明徹底可以被回收了),如上圖中的Object5、Object6、Object7,雖然它們3個依然可能相互引用,但是總體來說,它們已經沒有作用了,這樣就解決了引用計數算法無法解決的問題。
GC基本原理
為將內存中不再被使用的對象進行回收,消耗資源和時間。

1)對新生代對象的收集稱為minor GC

2) 對舊生代對象的收集成為Full  GC

3) 程序中主動調用System.GC()強制執行的GC稱為FULL GC

對象引用類型分為強引用、軟引用、弱引用和虛引用。
1)        強引用:就是我們一般聲明對象是時虛擬機生成的引用,強引用環境下,垃圾回收時需要嚴格判斷當前對象是否被強引用,如果被強引用,則不會被垃圾回收。

2)        軟引用:軟引用一般被做為緩存來使用。與強引用的區別是,軟引用在垃圾回收時,虛擬機會根據當前系統的剩余內存來決定是否對軟引用進行回收。如果剩余內存比較緊張,則虛擬機會回收軟引用所引用的空間;如果剩余內存相對富裕,則不會進行回收。換句話說,虛擬機在發生OutOfMemory時,肯定是沒有軟引用存在的。

3)        弱引用:弱引用與軟引用類似,都是作為緩存來使用。但與軟引用不同,弱引用在進行垃圾回收時,是一定會被回收掉的,因此其生命周期只存在於一個垃圾回收周期內。

4)        "虛引用":就是形同虛設,與其他幾種引用都不同,虛引用並不會決定對象的生命周期。如果一個對象僅持有虛引用,那么它就和沒有任何引用一樣,在任何時候都可能被垃圾回收。

虛引用主要用來跟蹤對象被垃圾回收的活動。虛引用與軟引用和弱引用的一個區別在於:虛引用必須和引用隊列(ReferenceQueue)聯合使用。當垃 圾回收器准備回收一個對象時,如果發現它還有虛引用,就會在回收對象的內存之前,把這個虛引用加入到與之關聯的引用隊列中。程序可以通過判斷引用隊列中是 否已經加入了虛引用,來了解被引用的對象是否將要被垃圾回收。程序如果發現某個虛引用已經被加入到引用隊列,那么就可以在所引用的對象的內存被回收之前采取必要的行動。

強引用不用說,我們系統一般在使用時都是用的強引用。而“軟引用”和“弱引用”比較少見。他們一般被作為緩存使用,而且一般是在內存大小比較受限的情況下做為緩存。因為如果內存足夠大的話,可以直接使用強引用作為緩存即可,同時可控性更高。因而,他們常見的是被使用在桌面應用系統的緩存。

特別注意,在世紀程序設計中一般很少使用弱引用與虛引用,使用軟用的情況較多,這是因為軟引用可以加速JVM對垃圾內存的回收速度,可以維護系統的運行安全,防止內存溢出(OutOfMemory)等問題的產生。

如何進行垃圾回收?
本塊內容以介紹垃圾回收算法為主,因為我們前面有介紹,內存主要被分為三塊,新生代、舊生代、持久代。三代的特點不同,造就了他們所用的GC算法不同,新生代適合那些生命周期較短,頻繁創建及銷毀的對象,舊生代適合生命周期相對較長的對象,持久代在Sun HotSpot中就是指方法區(有些JVM中根本就沒有持久代這中說法)。首先介紹下新生代、舊生代、持久代的概念及特點:

新生代:New Generation或者Young Generation。上面大致分為Eden區和Survivor區,Survivor區又分為大小相同的兩部分:FromSpace 和ToSpace。新建的對象都是用新生代分配內存,Eden空間不足的時候,會把存活的對象轉移到Survivor中,新生代的大小可以由-Xmn來控制,也可以用-XX:SurvivorRatio來控制Eden和Survivor的比例.
舊生代:Old Generation。用於存放新生代中經過多次垃圾回收仍然存活的對象,例如緩存對象。舊生代占用大小為-Xmx值減去-Xmn對應的值。

持久代:Permanent Generation。在Sun的JVM中就是方法區的意思,盡管有些JVM大多沒有這一代。主要存放常量及類的一些信息默認最小值為16MB,最大值為64MB,可通過-XX:PermSize及-XX:MaxPermSize來設置最小值和最大值。
  5.常見的GC算法:

標記-清除算法(Mark-Sweep)
最基礎的GC算法,將需要進行回收的對象做標記,之后掃描,有標記的進行回收,這樣就產生兩個步驟:標記和清除。這個算法效率不高,而且在清理完成后會產生內存碎片,這樣,如果有大對象需要連續的內存空間時,還需要進行碎片整理,所以,此算法需要改進。

         復制算法(Copying)

前面我們談過,新生代內存分為了三份,Eden區和2塊Survivor區,一般Sun的JVM會將Eden區和Survivor區的比例調為8:1,保證有一塊Survivor區是空閑的,這樣,在垃圾回收的時候,將不需要進行回收的對象放在空閑的Survivor區,然后將Eden區和第一塊Survivor區進行完全清理,這樣有一個問題,就是如果第二塊Survivor區的空間不夠大怎么辦?這個時候,就需要當Survivor區不夠用的時候,暫時借持久代的內存用一下。此算法適用於新生代。

         標記-整理(或叫壓縮)算法(Mark-Compact)

和標記-清楚算法前半段一樣,只是在標記了不需要進行回收的對象后,將標記過的對象移動到一起,使得內存連續,這樣,只要將標記邊界以外的內存清理就行了。此算法適用於持久代。

       6.常見的垃圾收集器: 

根據上面說的諸多算法,每個JVM都有不同的實現,我們首先介紹三種實際的垃圾回收器:串行GC(SerialGC)、並行回收GC(Parallel Scavenge)和並行GC(ParNew)。
1)、Serial GC。是最基本、最古老的收集器,但是現在依然被廣泛使用,是一種單線程垃圾回收機制,而且不僅如此,它最大的特點就是在進行垃圾回收的時候,需要將所有正在執行的線程暫停(Stop The World),對於有些應用這是難以接受的,但是我們可以這樣想,只要我們能夠做到將它所停頓的時間控制在N個毫秒范圍內,大多數應用我們還是可以接受的,而且事實是它並沒有讓我們失望,幾十毫米的停頓我們作為客戶機(Client)是完全可以接受的,該收集器適用於單CPU、新生代空間較小及對暫停時間要求不是非常高的應用上,是client級別默認的GC方式,可以通過-XX:+UseSerialGC來強制指定。

2)、ParNew GC。基本和Serial GC一樣,但本質區別是加入了多線程機制,提高了效率,這樣它就可以被用在服務器端(Server)上,同時它可以與CMS GC配合,所以,更加有理由將它置於Server端。

3)、Parallel Scavenge GC。在整個掃描和復制過程采用多線程的方式來進行,適用於多CPU、對暫停時間要求較短的應用上,是server級別默認采用的GC方式,可用-XX:+UseParallelGC來強制指定,用-XX:ParallelGCThreads=4來指定線程數。以下給出幾組使用組合:

4)、CMS (Concurrent Mark Sweep)收集器。該收集器目標就是解決Serial GC 的停頓問題,以達到最短回收時間。常見的B/S架構的應用就適合用這種收集器,因為其高並發、高響應的特點。CMS收集器是基於“標記-清除”算法實現的,整個收集過程大致分為4個步驟:初始標記(CMS initial mark)、並發標記(CMS concurrenr mark)、重新標記(CMS remark)、並發清除(CMS concurrent sweep)。

CMS收集器的優點:並發收集、低停頓,但是CMS還遠遠達不到完美。

5)、G1收集器。相比CMS收集器有不少改進,首先基於標記-整理算法,不會產生內存碎片問題,其次,可以比較精確的控制停頓,此處不再詳細介紹。

6)、Serial Old。Serial Old是Serial收集器的老年代版本,它同樣使用一個單線程執行收集,使用“標記-整理”算法。。

7)、Parallel Old。Parallel Old是Parallel Scavenge收集器的老年代版本,使用多線程和“標記-整理”算法。

8)、RTSJ垃圾收集器,用於Java實時編程,后續會補充介紹。
摘要:https://www.cnblogs.com/dragonsuc/p/4380409.html
JVM垃圾回收機制

       3.Java的類加載機制;

 一個java類的完整的生命周期會經歷加載、連接、初始化、使用、和卸載五個階段
1.加載 
我們編寫一個java的源文件,經過編譯后生成一個后綴名為.class的文件,這結合四字節碼文件,
java虛擬機就識別這種文件,java的生命周期就是class文件從加載到消亡的過程。 
關於加載,其實,就是將源文件的class文件找到類的信息將其加載到方法區中,
然后在堆區中實例化一個java.lang.Class對象,作為方法區中這個類的信息的入口。
但是這一功能是在JVM之外實現的,主要的原因是方便讓應用程序自己決定如何獲取這個類,
在不同的虛擬機實現的方式不一定相同,hotspot虛擬機是采用需要時在加載的方式,
也有其他是先預先加載的。 
2.連接 
一般會跟加載階段和初始化階段交叉進行,過程由三部分組成:驗證、准備和解析三步 
(1)驗證:確定該類是否符合java語言的規范,有沒有屬性和行為的重復,繼承是否合理,總之,就是保證jvm能夠執行 
(2)准備:主要做的就是為由static修飾的成員變量分配內存,並設置默認的初始值 
默認初始值如下:

1.八種基本數據類型默認的初始值是0 
2.引用類型默認的初始值是null 
3.有static final修飾的會直接賦值,例如:static final int x=10;則默認就是10.
(3)解析:這一階段的任務就是把常量池中的符號引用轉換為直接引用,說白了就是jvm會將所有的類或接口名、字段名、方法名轉換為具體的內存地址。 
3.初始化 
這個階段就是將靜態變量(類變量)賦值的過程,即只有static修飾的才能被初始化,執行的順序就是:

父類靜態域或着靜態代碼塊,然后是子類靜態域或者子類靜態代碼塊
4.使用 
在類的使用過程中依然存在三步:對象實例化、垃圾收集、對象終結

(1)對象實例化:就是執行類中構造函數的內容,如果該類存在父類JVM會通過顯示或者隱示的方式先執行父類的構造函數,在堆內存中為父類的實例變量開辟空間,並賦予默認的初始值,然后在根據構造函數的代碼內容將真正的值賦予實例變量本身,然后,引用變量獲取對象的首地址,通過操作對象來調用實例變量和方法 
(2)垃圾收集:當對象不再被引用的時候,就會被虛擬機標上特別的垃圾記號,在堆中等待GC回收 
(3)對象的終結:對象被GC回收后,對象就不再存在,對象的生命也就走到了盡頭
5.類卸載 
即類的生命周期走到了最后一步,程序中不再有該類的引用,該類也就會被JVM執行垃圾回收,從此生命結束…
摘要:https://www.cnblogs.com/ipetergo/p/6441310.html
Java類加載機制

      4.Java的集合類有哪些;

java的集合類

      5、LinkList和ArrayList的區別;

LinkedList和ArrayList的差別主要來自於Array和LinkedList數據結構的不同。如果你很熟悉Array和LinkedList,你很容易得出下面的結論:

1) 因為Array是基於索引(index)的數據結構,它使用索引在數組中搜索和讀取數據是很快的。Array獲取數據的時間復雜度是O(1),但是要刪除數據卻是開銷很大的,因為這需要重排數組中的所有數據。

2) 相對於ArrayList,LinkedList插入是更快的。因為LinkedList不像ArrayList一樣,不需要改變數組的大小,也不需要在數組裝滿的時候要將所有的數據重新裝入一個新的數組,這是ArrayList最壞的一種情況,時間復雜度是O(n),而LinkedList中插入或刪除的時間復雜度僅為O(1)。ArrayList在插入數據時還需要更新索引(除了插入數組的尾部)。

3) 類似於插入數據,刪除數據時,LinkedList也優於ArrayList。

4) LinkedList需要更多的內存,因為ArrayList的每個索引的位置是實際的數據,而LinkedList中的每個節點中存儲的是實際的數據和前后節點的位置。

什么場景下更適宜使用LinkedList,而不用ArrayList

我前面已經提到,很多場景下ArrayList更受歡迎,但是還有些情況下LinkedList更為合適。譬如:

1) 你的應用不會隨機訪問數據。因為如果你需要LinkedList中的第n個元素的時候,你需要從第一個元素順序數到第n個數據,然后讀取數據。

2) 你的應用更多的插入和刪除元素,更少的讀取數據。因為插入和刪除元素不涉及重排數據,所以它要比ArrayList要快。

以上就是關於ArrayList和LinkedList的差別。你需要一個不同步的基於索引的數據訪問時,請盡量使用ArrayList。ArrayList很快,也很容易使用。但是要記得要給定一個合適的初始大小,盡可能的減少更改數組的大小。
摘要:http://www.importnew.com/6629.html
LinkedList和ArrayList的區別

     6、HashMap的實現原理;

HashMap:按照特性來說明一下,儲存的是鍵值對,線程不安全,非Synchronied,儲存的比較快,能夠接受null。按照工作原理來敘述一下,Map的put(key,value)來儲存元素,通過get(key)來得到value值,通過hash算法來計算hascode值,根據hashcode值來決定bucket(桶),儲存結構就算哈希表。 
提問:兩個hashcode相同的時候會發生說明? 
hashcode相同,bucket的位置會相同,也就是說會發生碰撞,哈希表中的結構其實有鏈表(LinkedList),這種沖突通過將元素儲存到LinkedList中,解決碰撞。儲存順序是仿在表頭。 
如果兩個鍵的hashcode相同,如何獲取值對象? 
如果兩個鍵的hashcode相同,我們通過key.equals()找到LinkedList中正確的節點。 
如果HashMap的大小超過了負載因子?怎么辦? 
默認的HashMap里面的負載因子大小是0.75,將會重新創建一個原來HashMap大小的 
兩倍bucket數組。 
重新調整的話會出現什么問題? 
多線程情況下會出現競爭問題,因為你在調節的時候,LinkedList儲存是按照順序儲存,調節的時候回將原來最先儲存的元素(也就是最下面的)遍歷,多線程就好試圖重新調整,這個時候就會出現死循環。 
為什么要用String、Wrapper類,來作為鍵值對?因為他們一般不是不可變的,源碼上面final,使用不可變類,而且重寫了equals和hashcode方法,避免了鍵值對改寫。提高HashMap性能。 
使用CocurrentHashMap代替Hashtable? 
可以,但是Hashtable提供的線程更加安全。
hashMap 面試題

   8、簡單講一下工廠模式的優勢;

工廠模式分為:簡單工廠模式,工廠方法模式和抽象工廠模式
工廠模式,顧名思義,最少有一個生產產品的機器存在的工廠Factory,與此同時,也要有一個構建好的產品模塊Product。所以,我們要用到Factory來創造Product。在簡單工廠模式中,有幾種角色存在。一個是所有產品的父類P,即所有產品的模板;另外一個是繼承了父類P的產品子類p1,p2...;當然,最重要的是Factory,在Factory中可以將這些的產品實例化,根據需求來將Factory和Product產生聯系。
簡單工廠優點:
這樣模塊清晰化,每個部分都各司其職,分工明確,代碼就實現最漸層意義上的“可維護”啦。說到缺點,當我們需要增加一產品,比如在計算機中加入一個新的功能,可以求M的N次方,這樣個小功能我們就要去添加一個新的類,同時我們需要在Factory中改動到switch里面的代碼,這是耦合性很高的表現啦,所以我們就有了“工廠模式”的出現啦。
優缺點
a.在客戶端Client中可以將工廠模式的主要結構看着很清楚,首先我們要有IFactory這個工廠的父接口,所有的子類或者子接口都可以實現它。AddFactory則是子類的代表之一,所以利用java的多態來實現,降低代碼的耦合性。而同時每個子工廠中擁有每條生產獨特產品的生產線。由此,工廠和產品掛上鈎了,聯系上了。每個子工廠生產出來的都是獨特的產品。

b.比“簡單工廠模式”的優缺點

優點:克服了簡單工廠違背開放-封閉原則的缺點,又保留了封裝對象創建過程的優點,降低客戶端和工廠的耦合性,所以說“工廠模式”是“簡單工廠模式”的進一步抽象和推廣。

缺點:每增加一個產品,相應的也要增加一個子工廠,加大了額外的開發量。
View Code

9、Spring的事務管理;


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM