面試題總結-Java部分

1 集合

1.1 hashmap原理

HashMap是基於哈希表實現的,每個元素是一個key-value對,實現了Serializable、Cloneable接口,容許使用null值和null鍵。不保證映射的順序,內部經過單鏈表解決衝突問題,容量超過(容量*加載因子)時,會自動增加。(除了不一樣步和容許使用null以外,HashMap類與Hashtable大體相同)。HashMap不是線程安全的。前端

1.2 ConcurrentHashMap實現原理

首先將數據分紅一段一段的存儲,而後給每一段數據配一把鎖,當一個線程佔用鎖訪問其中一個段(Segment)數據的時候,其餘段的數據也能被其餘線程訪問。 有些方法須要跨段,好比size()和containsValue(),它們可能須要鎖定整個表而而不只僅是某個段,這須要按順序鎖定全部段,操做完畢後,又按順序釋放全部段的鎖。 Segment繼承了ReetrantLock,表示Segment是一個可重入鎖,所以ConcurrentHashMap經過可重入鎖對每一個分段進行加鎖。java

1.3 Hashmap hashtable區別

  • 繼承的父類不一樣
    Hashtable繼承自Dictionary類,而HashMap繼承自AbstractMap類。但兩者都實現了Map接口。
  • 線程安全性不一樣
    Hashtable 中的方法是Synchronize的,而HashMap中的方法在缺省狀況下是非Synchronize的。在多線程併發的環境下,能夠直接使用Hashtable,不須要本身爲它的方法實現同步,但使用HashMap時就必需要本身增長同步處理。
  • 是否提供contains方法
    HashMap把Hashtable的contains方法去掉了,改爲containsValue和containsKey,由於contains方法容易讓人引發誤解。Hashtable則保留了contains,containsValue和containsKey三個方法,其中contains和containsValue功能相同。
  • key和value是否容許null值
    Hashtable中,key和value都不容許出現null值。HashMap中,null能夠做爲鍵,這樣的鍵只有一個;能夠有一個或多個鍵所對應的值爲null。
  • 兩個遍歷方式的內部實現上不一樣
    Hashtable、HashMap都使用了 Iterator。而因爲歷史緣由,Hashtable還使用了Enumeration的方式 。
  • hash值不一樣
    哈希值的使用不一樣,HashTable直接使用對象的hashCode。而HashMap從新計算hash值。
  • 內部實現使用的數組初始化和擴容方式不一樣
    HashTable在不指定容量的狀況下的默認容量爲11,而HashMap爲16,Hashtable不要求底層數組的容量必定要爲2的整數次冪,而HashMap則要求必定爲2的整數次冪。Hashtable擴容時,將容量變爲原來的2倍加1,而HashMap擴容時,將容量變爲原來的2倍。

1.4 hashmap的resize

當hashmap中的元素愈來愈多的時候,碰撞的概率也就愈來愈高(由於數組的長度是固定的),因此爲了提升查詢的效率,就要對hashmap的數組進行擴容,數組擴容以後,最消耗性能的點就出現了:原數組中的數據必須從新計算其在新數組中的位置,並放進去,這就是resize。
那麼hashmap何時進行擴容呢?當hashmap中的元素個數超過數組大小loadFactor時,就會進行數組擴容,loadFactor的默認值爲0.75,也就是說,默認狀況下,數組大小爲16,那麼當hashmap中元素個數超過160.75=12的時候,就把數組的大小擴展爲216=32,即擴大一倍,而後從新計算每一個元素在數組中的位置,而這是一個很是消耗性能的操做,因此若是咱們已經預知hashmap中元素的個數,那麼預設元素的個數可以有效的提升hashmap的性能。好比說,咱們有1000個元素new HashMap(1000), 可是理論上來說new HashMap(1024)更合適,不過上面annegu已經說過,即便是1000,hashmap也自動會將其設置爲1024。 可是new HashMap(1024)還不是更合適的,由於0.751000 < 1000, 也就是說爲了讓0.75 * size > 1000, 咱們必須這樣new HashMap(2048)才最合適,既考慮了&的問題,也避免了resize的問題。git

2 數據結構

2.1 鏈表和數組內存中區別

  • 1.佔用的內存空間
    鏈表存放的內存空間能夠是連續的,也能夠是不連續的,數組則是連續的一段內存空間。通常狀況下存放相同多的數據數組佔用較小的內存,而鏈表還須要存放其前驅和後繼的空間。
  • 2.長度的可變性
    鏈表的長度是按實際須要能夠伸縮的,而數組的長度是在定義時要給定的,若是存放的數據個數超過了數組的初始大小,則會出現溢出現象。
  • 3.對數據的訪問
    鏈表方便數據的移動而訪問數據比較麻煩;數組訪問數據很快捷而移動數據比較麻煩。 鏈表和數組的差別決定了它們的不一樣使用場景,若是須要不少對數據的訪問,則適合使用數組;若是須要對數據進行不少移位操做,則設和使用鏈表。

3 線程

3.1 synchronized使用

  • 修飾一個方法
  • 修飾一個代碼塊
  • 修改一個靜態的方法
  • 修飾一個類

3.2 synchronized與Lock的區別

  • Lock是一個接口,而synchronized是Java中的關鍵字,synchronized是內置的語言實現;
  • synchronized在發生異常時,會自動釋放線程佔有的鎖,所以不會致使死鎖現象發生;而Lock在發生異常時,若是沒有主動經過unLock()去釋放鎖,則極可能形成死鎖現象,所以使用Lock時須要在finally塊中釋放鎖;
  • Lock可讓等待鎖的線程響應中斷,而synchronized卻不行,使用synchronized時,等待的線程會一直等待下去,不可以響應中斷;
  • 經過Lock能夠知道有沒有成功獲取鎖,而synchronized卻沒法辦到。
  • Lock能夠提升多個線程進行讀操做的效率。
  • 在性能上來講,若是競爭資源不激烈,二者的性能是差很少的,而當競爭資源很是激烈時(即有大量線程同時競爭),此時Lock的性能要遠遠優於synchronized。因此說,在具體使用時要根據適當狀況選擇

3.3 volatile

volatile做爲java中的關鍵詞之一,用以聲明變量的值可能隨時會別的線程修改,使用volatile修飾的變量會強制將修改的值當即寫入主存,主存中值的更新會使緩存中的值失效(非volatile變量不具有這樣的特性,非volatile變量的值會被緩存,線程A更新了這個值,線程B讀取這個變量的值時可能讀到的並非是線程A更新後的值)。volatile會禁止指令重排。volatile具備可見性、有序性,不具有原子性。算法

3.4 線程等待

在Object.java中,定義了wait(), notify()和notifyAll()等接口。wait()的做用是讓當前線程進入等待狀態,同時,wait()也會讓當前線程釋放它所持有的鎖。而notify()和notifyAll()的做用,則是喚醒當前對象上的等待線程;notify()是喚醒單個線程,而notifyAll()是喚醒全部的線程。小程序

3.5 ThreadPoolExecutor執行順序

  • 當線程數小於核心線程數時,建立線程。
  • 當線程數大於等於核心線程數,且任務隊列未滿時,將任務放入任務隊列。
  • 當線程數大於等於核心線程數,且任務隊列已滿
    • 若線程數小於最大線程數,建立線程
    • 若線程數等於最大線程數,拋出異常,拒絕任務

3.6 ThreadPoolExecutor參數默認值

  • corePoolSize=1
  • queueCapacity=Integer.MAX_VALUE
  • maxPoolSize=Integer.MAX_VALUE
  • keepAliveTime=60s
  • allowCoreThreadTimeout=false
  • rejectedExecutionHandler=AbortPolicy()

3.7 線程池類型:

  • newCachedThreadPool:數組

    • 底層:
      返回ThreadPoolExecutor實例,corePoolSize爲0;maximumPoolSize爲Integer.MAX_VALUE;keepAliveTime爲60L;unit爲TimeUnit.SECONDS;workQueue爲SynchronousQueue(同步隊列)
    • 通俗:
      當有新任務到來,則插入到SynchronousQueue中,因爲SynchronousQueue是同步隊列,所以會在池中尋找可用線程來執行,如有能夠線程則執行,若沒有可用線程則建立一個線程來執行該任務;若池中線程空閒時間超過指定大小,則該線程會被銷燬。
    • 適用:
      執行不少短時間異步的小程序或者負載較輕的服務器
  • newFixedThreadPool:緩存

    • 底層:
      返回ThreadPoolExecutor實例,接收參數爲所設定線程數量nThread,corePoolSize爲nThread,maximumPoolSize爲nThread;keepAliveTime爲0L(不限時);unit爲:TimeUnit.MILLISECONDS;WorkQueue爲:new LinkedBlockingQueue() 無解阻塞隊列
    • 通俗:
      建立可容納固定數量線程的池子,每隔線程的存活時間是無限的,當池子滿了就不在添加線程了;若是池中的全部線程均在繁忙狀態,對於新任務會進入阻塞隊列中(無界的阻塞隊列)
    • 適用:
      執行長期的任務,性能好不少
  • newSingleThreadExecutor:安全

    • 底層:
      FinalizableDelegatedExecutorService包裝的ThreadPoolExecutor實例,corePoolSize爲1;maximumPoolSize爲1;keepAliveTime爲0L;unit爲:TimeUnit.MILLISECONDS;workQueue爲:new LinkedBlockingQueue() 無解阻塞隊列
    • 通俗:
      建立只有一個線程的線程池,且線程的存活時間是無限的;當該線程正繁忙時,對於新任務會進入阻塞隊列中(無界的阻塞隊列)
    • 適用:
      一個任務一個任務執行的場景
  • newScheduledThreadPool:服務器

    • 底層:
      建立ScheduledThreadPoolExecutor實例,corePoolSize爲傳遞來的參數,maximumPoolSize爲Integer.MAX_VALUE;keepAliveTime爲0;unit爲:TimeUnit.NANOSECONDS;workQueue爲:new DelayedWorkQueue() 一個按超時時間升序排序的隊列
    • 通俗:
      建立一個固定大小的線程池,線程池內線程存活時間無限制,線程池能夠支持定時及週期性任務執行,若是全部線程均處於繁忙狀態,對於新任務會進入DelayedWorkQueue隊列中,這是一種按照超時時間排序的隊列結構
    • 適用:
      週期性執行任務的場景

4 Java虛擬機

4.1 工做原理

從宏觀上介紹一下Java虛擬機的工做原理。首先Java源文件通過前端編譯器(javac或ECJ)將.java文件編譯爲Java字節碼文件,而後JRE加載Java字節碼文件,載入系統分配給JVM的內存區,而後執行引擎解釋或編譯類文件,再由即時編譯器將字節碼轉化爲機器碼。網絡

4.2 運行時數據區

程序計數器:      
線程私有,用來指示當前線程所執行的字節碼的行號,就是用來標記線程如今執行的代碼的位置;
對Java方法,它存儲的是字節碼指令的地址;對於Native方法,該計數器的值爲空。
棧:      
線程私有,一個方法的執行和退出就是用一個棧幀的入棧和出棧表示的,一般咱們不容許你使用遞歸就是由於,方法就是一個棧,太多的方法只執行而沒有退出就會致使棧溢出,不過能夠經過尾遞歸優化。棧又分爲虛擬機棧和本地方法棧,一個對應Java方法,一個對應Native方法。
堆:      
用來給對象分配內存的,幾乎全部的對象實例(包括數組)都在上面分配。它是垃圾收集器的主要管理區域,所以也叫GC堆。它其實是一塊內存區域,因爲一些收集算法的緣由,又將其細化分爲新生代和老年代等。
方法區:      
方法區由多線程共享,用來存儲類信息、常量、靜態變量、即便編譯後的代碼等數據。運行時常量池是方法區的一部分,它用於存放編譯器生成的各類字面量和符號引用,好比字符串常量等。
複製代碼

4.3 是否回收判斷

  • 引用記數法
    給對象添加一個引用計數器,被引用時計數器加1,引用失效時減1。 這種方法不經常使用,由於它難以解決兩個變量相互引用的問題。
  • 可達性分析
    經過一系列GC Roots的對象做爲起始點,從節點向下搜索, 當一個對象沒有任何一條可到GC Roots的引用鏈,則該對象可回收。

4.4 垃圾回收算法

  • 標記-清除算法,這種算法直接在內存中把須要回收的對象「摳」出來。 好好的內存被它搞成了馬蜂窩,因此效率不高,清除以後會產生內容碎片,形成內存不連續,當分配較大內存對象時可能會因內存不足而觸發垃圾收集動做。

  • 複製算法:將內存分紅兩塊,一次只在一塊內存中進行分配,垃圾回收一次以後, 就將該內存中的未被回收的對象移動到另外一塊內存中,而後將該內存一次清理掉。 好比將內存分紅A和B,先在A中分配,當垃圾回收的時候把A中須要回收的內存清理掉,而後把不須要清理的全部對象複製到B裏面。 複製算法常被用來回收新生代,並且分配空間也不是1:1,而是較大的Eden空間和較小的Survivor空間。在HotSpot中,其比例是8:1。

    • 標記整理算法:相似於標記-清除算法,只是回收了以後,它要對內存空間進行整理,以使得剩餘的對象佔用連續的存儲空間。

    • 分代收集算法:上面是三種基本的垃圾回收算法,但實際上,咱們一般根據對象存活週期的不一樣將內存劃分紅幾塊,而後根據其特色採用不一樣的回收算法。

4.5 內存分配與回收策略

對象內存分配,往大方向上講,就是在堆上分配,對象主要分配在新生代的Eden區上,若是啓動了本地線程分配緩衝,將按線程優先在TLAB上分配,少數狀況下可能直接分配在老年代。

  • 對象優先在Eden分配:
    大多數狀況下,對象在新生代Eden區非中分配,當Eden區沒有足夠空間時,虛擬機發起一次Minor GC。

  • 大對象直接進入老年代:
    大對象指須要大量連續內存空間的Java對象,好比很大的數組或者字符串。常常出現大對象會致使內存還有很多空間時就提早觸發垃圾收集來獲取足夠的連續空間來安置它們。 虛擬機提供了-XX:PretenureSizeThreshold參數,當對象的大小大於它的值的時候將直接分配在老年代。這樣作是爲了不在Eden區和Survivor區之間發生大量的內存複製。

  • 長期存活對象將進入老年代:
    若對象出生在Eden區並通過一次Minor GC後仍然存活,而且能被Survivor容納,將被移動到Survivor空間中,而且對象年齡將加1。 在Survivor中,每熬過一次Minor GC,則年齡加1,當年齡達到必定程度時(默認15歲),就會被晉升到老年代。該年齡的閾值經過參數-XX:MaxTenuringThreshold設置。

5 HTTP

5.1 Http鏈接複用原理

當一個http請求完成後,tcp鏈接不會當即釋放,若是有新的http請求,而且host和上次同樣,那麼能夠複用tcp鏈接,省去從新鏈接的過程。

5.2 Http中keep alive與TCP區別

  • HTTP Keep-Alive
    在HTTP 1.0之前,每一個http請求都要求打開一個TCP socket鏈接,而且使用一次以後就斷開這個TCP鏈接,這會致使頻繁地建立和銷燬TCP。HTTP 1.1經過使用keep-alive能夠改善這種狀態,即在一次TCP鏈接中能夠持續發送多份數據而不會斷開鏈接。
  • TCP KEEPALIVE
    這是TCP協議棧爲了檢測鏈接情況的保活機制,當TCP空閒必定時間後會發送心跳包給對方,若是對端回覆ACK後,就認爲對端是存活的,重置定時器;若是對端回覆RST應答(對端崩潰或者其餘緣由,致使的復位),那就關閉該鏈接;若是對端無任何迴應,那就會出發超時重傳,直到達到重傳的次數,若是對端依然沒有回覆,那麼就關閉該鏈接。

HTTP位於網絡協議棧的應用層,而TCP位於網絡協議棧的傳輸層,二者的KEEP-ALIVE雖然名稱相同,可是做用不一樣。HTTP是爲了重用TCP,避免每次請求,都重複建立TCP;而TCP的KEEP-ALIVE是一種保活機制,檢測對端是否依然存活。

6 git

  • git cherry-pick
    將某一段commit粘貼到另外一個分支上
  • git revert
    經過反作建立一個新的版本,這個版本的內容與咱們要回退到的目標版本同樣,可是HEAD指針是指向這個新生成的版本,而不是目標版本。 若是咱們想恢復以前的某一版本(該版本不是merge類型),可是又想保留該目標版本後面的版本,記錄下這整個版本變更流程,就能夠用這種方法。
  • git rebase
    合併多個commit爲一個完整commit
  • git reset
    修改HEAD的位置,即將HEAD指向的位置改變爲以前存在的某個版本。若是想恢復到以前某個提交的版本,且那個版本以後提交的版本咱們都不要了,就能夠用這種方法。

7 基本數據類型相關

7.1 replace() replaceAll() replaceFirst()

  • replace(CharSequence target, CharSequence replacement) 用replacement替換掉target。這兩個參數都是字符串
  • replaceAll(String regex, String replacement) 用replacement全部regex匹配的字符串。很明顯regex參數是個正則匹配式,replacement是個字符串。
  • replaceFirst(String regex, String replacement),基本和replaceAll相同,區別是隻替換第一個匹配項

7.2 java中int與Integer用==比較詳解

①、不管如何,Integer與new Integer不會相等。不會經歷拆箱過程,由於它們存放內存的位置不同。(要看具體位置,能夠看看這篇文章:點擊打開連接)

②、兩個都是非new出來的Integer,若是數在-128到127之間,則是true,不然爲false。

③、兩個都是new出來的,則爲false。

④、int和integer(new或非new)比較,都爲true,由於會把Integer自動拆箱爲int,其實就是至關於兩個int類型比較。

8 其餘

8.1 xml解析都有哪些 區別

  • SAX
    sax是一個用於處理xml事件驅動的「推」模型;
    優勢:解析速度快,佔用內存少,它須要哪些數據再加載和解析哪些內容。
    缺點:它不會記錄標籤的關係,而是須要應用程序本身處理,這樣就會增長程序的負擔。
  • DOM
    dom是一種文檔對象模型;
    優勢:dom能夠以一種獨立於平臺和語言的方式訪問和修改一個文檔的內容和結構,dom技術使得用戶頁面能夠動態的變化,如動態顯示隱藏一個元素,改變它的屬性,增長一個元素等,dom可使頁面的交互性大大加強。
    缺點:dom解析xml文件時會將xml文件的全部內容以文檔樹方式存放在內存中。
  • PULL pull和sax很類似,區別在於:pull讀取xml文件後觸發相應的事件調用方法返回的是數字,且pull能夠在程序中控制,想解析到哪裏就能夠中止解析。 (SAX解析器的工做方式是自動將事件推入事件處理器進行處理,所以你不能控制事件的處理主動結束;而Pull解析器的工做方式爲容許你的應用程序代碼主動從解析器中獲取事件,正由於是主動獲取事件,所以能夠在知足了須要的條件後再也不獲取事件,結束解析。pull是一個while循環,隨時能夠跳出,而sax不是,sax是隻要解析了,就必須解析完成。)
相關文章
相關標籤/搜索