面試整理-Java綜合高階篇(吐血整理)

哪吒小子發表於2017-12-12

Java面試總結

  • 交流或更多內容請關注我的公眾號:nezha_blog
  • 我的技術部落格:nezha.github.io

微信公眾號

1.你用過哪些集合類?

大公司最喜歡問的Java集合類面試題 40個Java集合面試問題和答案 java.util.Collections 是一個包裝類。它包含有各種有關集合操作的靜態多型方法。 java.util.Collection 是一個集合介面。它提供了對集合物件進行基本操作的通用介面方法。

Collection ├List │├LinkedList │├ArrayList │└Vector │ └Stack └Set Map ├Hashtable ├HashMap └WeakHashMap


ArrayList、HashMap、TreeMap和HashTable類提供對元素的隨機訪問。

執行緒安全

Vector HashTable(不允許插空值)

非執行緒安全

ArrayList LinkedList HashMap(允許插入空值) HashSet TreeSet TreeMap(基於紅黑樹的Map實現)

2.你說說 arraylist 和 linkedlist 的區別?

ArrayList和LinkedList兩者都實現了List介面,但是它們之間有些不同。 (1)ArrayList是由Array所支援的基於一個索引的資料結構,所以它提供對元素的隨機訪問 (2)與ArrayList相比,在LinkedList中插入、新增和刪除一個元素會更快 (3)LinkedList比ArrayList消耗更多的記憶體,因為LinkedList中的每個節點儲存了前後節點的引用

3.HashMap 底層是怎麼實現的?還有什麼處理雜湊衝突的方法?

處理雜湊衝突的方法:

解決HashMap一般沒有什麼特別好的方式,要不擴容重新hash要不優化衝突的連結串列結構

1.開放定地址法-線性探測法 2.開放定地址法-平方探查法 3.連結串列解決-可以用紅黑樹提高查詢效率

image.png

HashMap簡介 HashMap 是一個雜湊表,它儲存的內容是鍵值對(key-value)對映。 HashMap 繼承於AbstractMap,實現了Map、Cloneable、java.io.Serializable介面。 HashMap 的實現不是同步的,這意味著它不是執行緒安全的,但可以用 Collections的synchronizedMap方法使HashMap具有執行緒安全的能力。它的key、value都可以為null。此外,HashMap中的對映不是有序的。 HashMap 的例項有兩個引數影響其效能:“初始容量” 和 “載入因子”。初始容量預設是16。預設載入因子是 0.75, 這是在時間和空間成本上尋求一種折衷。載入因子過高雖然減少了空間開銷,但同時也增加了查詢成本. HashMap是陣列+連結串列+紅黑樹(JDK1.8增加了紅黑樹部分)實現的,當連結串列長度太長(預設超過8)時,連結串列就轉換為紅黑樹.

image.png

Java8系列之重新認識HashMap 功能實現-方法

  1. 確定雜湊桶陣列索引位置 :這裡的Hash演算法本質上就是三步:取key的hashCode值、高位運算、取模運算。
方法一:
static final int hash(Object key) {   //jdk1.8 & jdk1.7
     int h;
     // h = key.hashCode() 為第一步 取hashCode值
     // h ^ (h >>> 16)  為第二步 高位參與運算
     return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
方法二:
static int indexFor(int h, int length) {  //jdk1.7的原始碼,jdk1.8沒有這個方法,但是實現原理一樣的
     return h & (length-1);  //第三步 取模運算
}
複製程式碼
  1. 分析HashMap的put方法
    image.png
  1. 擴容機制:原來的兩倍

4.熟悉什麼演算法,還有說說他們的時間複雜度?

經典排序演算法總結與實現

image.png

5.ArrayList和Vector的底層程式碼和他們的增長策略,它們是如何進行擴容的?

ArrayList 預設陣列大小是10,其中ensureCapacity擴容,trimToSize容量調整到適中,擴充套件後陣列大小為((原陣列長度*1.5)與傳遞引數中較大者. Vector的擴容,是可以指定擴容因子,同時Vector擴容策略是:1.原來容量的2倍,2.原來容量+擴容引數值。

詳細內容可以配合閱讀原始碼

6.jvm 原理。程式執行區域劃分

問:Java執行時資料區域? 回答:包括程式計數器、JVM棧、本地方法棧、方法區、堆 問:方法區裡存放什麼? 本地方法棧:和jvm棧所發揮的作用類似,區別是jvm棧為jvm執行java方法(位元組碼)服務,而本地方法棧為jvm使用的native方法服務。 JVM棧:區域性變數表、運算元棧、動態連結、方法出口。 方法區:用於儲存已被虛擬機器載入的類資訊,常量、靜態變數、即時編譯器編譯後的程式碼等。 堆:存放物件例項。

7.minor GC 與 Full GC,分別什麼時候會觸發? 。分別採用哪種垃圾回收演算法?簡單介紹演算法

GC(或Minor GC):收集 生命週期短的區域(Young area)。 Full GC (或Major GC):收集生命週期短的區域(Young area)和生命週期比較長的區域(Old area)對整個堆進行垃圾收集。 新生代通常存活時間較短基於Copying演算法進行回收,將可用記憶體分為大小相等的兩塊,每次只使用其中一塊;當這一塊用完了,就將還活著的物件複製到另一塊上,然後把已使用過的記憶體清理掉。在HotSpot裡,考慮到大部分物件存活時間很短將記憶體分為Eden和兩塊Survivor,預設比例為8:1:1。代價是存在部分記憶體空間浪費,適合在新生代使用; 老年代新生代不同,老年代物件存活的時間比較長、比較穩定,因此採用標記(Mark)演算法來進行回收,所謂標記就是掃描出存活的物件,然後再進行回收未被標記的物件,回收後對用空出的空間要麼進行合併、要麼標記出來便於下次進行分配,總之目的就是要減少記憶體碎片帶來的效率損耗。 在執行機制上JVM提供了序列GC(Serial MSC)、並行GC(Parallel MSC)和併發GC(CMS)。

Minor GC ,Full GC 觸發條件

  • Minor GC觸發條件:當Eden區滿時,觸發Minor GC。

  • Full GC觸發條件:

  • (1)呼叫System.gc時,系統建議執行Full GC,但是不必然執行

  • (2)老年代空間不足

  • (3)方法去空間不足

  • (4)通過Minor GC後進入老年代的平均大小大於老年代的可用記憶體

  • (5)由Eden區、From Space區向To Space區複製時,物件大小大於To Space可用記憶體,則把該物件轉存到老年代,且老年代的可用記憶體小於該物件大小

8.HashMap 實現原理

在java程式語言中,最基本的結構就是兩種,一個是陣列,另外一個是模擬指標(引用),所有的資料結構都可以用這兩個基本結構來構造的,HashMap也不例外。HashMap實際上是一個“連結串列雜湊”的資料結構,即陣列和連結串列的結合體。

9.java.util.concurrent 包下使用過哪些

1.阻塞佇列 BlockingQueue( ArrayBlockingQueue, DelayQueue, LinkedBlockingQueue, SynchronousQueue,LinkedTransferQueue,LinkedBlockingDeque) 2.ConcurrentHashMap 3.Semaphore--訊號量 4.CountDownLatch--閉鎖 5.CyclicBarrier--柵欄 6.Exchanger--交換機 7.Executor->ThreadPoolExecutor,ScheduledThreadPoolExecutor

Semaphore semaphore = new Semaphore(1);  
//critical section  
semaphore.acquire();  
...  
semaphore.release();
複製程式碼

8.鎖 Lock--ReentrantLock,ReadWriteLock,Condition,LockSupport

Lock lock = new ReentrantLock();  
lock.lock();  
//critical section  
lock.unlock();
複製程式碼

10.concurrentMap 和 HashMap 區別

1.hashMap可以有null的鍵,concurrentMap不可以有 2.hashMap是執行緒不安全的,在多執行緒的時候需要Collections.synchronizedMap(hashMap),ConcurrentMap使用了重入鎖保證執行緒安全。 3.在刪除元素時候,兩者的演算法不一樣。 ConcurrentHashMapHashtable主要區別就是圍繞著鎖的粒度以及如何鎖,可以簡單理解成把一個大的HashTable分解成多個,形成了鎖分離。

11.訊號量是什麼,怎麼使用?volatile關鍵字是什麼?

訊號量-semaphore:荷蘭著名的電腦科學家Dijkstra 於1965年提出的一個同步機制。是在多執行緒環境下使用的一種設施, 它負責協調各個執行緒, 以保證它們能夠正確、合理的使用公共資源。 整形訊號量:表示共享資源狀態,且只能由特殊的原子操作改變整型量。 同步與互斥:同類程式為互斥關係(印表機問題),不同程式為同步關係(消費者生產者)。


使用volatile關鍵字是解決同步問題的一種有效手段。 java volatile關鍵字預示著這個變數始終是“儲存進入了主存”。更精確的表述就是每一次讀一個volatile變數,都會從主存讀取,而不是CPU的快取。同樣的道理,每次寫一個volatile變數,都是寫回主存,而不僅僅是CPU的快取。 Java 保證volatile關鍵字保證變數的改變對各個執行緒是可見的。

image.png

12.阻塞佇列了解嗎?怎麼使用

面試整理-Java綜合高階篇(吐血整理)

面試整理-Java綜合高階篇(吐血整理)

阻塞佇列 (BlockingQueue)是Java util.concurrent包下重要的資料結構,BlockingQueue提供了執行緒安全的佇列訪問方式:當阻塞佇列進行插入資料時,如果佇列已滿,執行緒將會阻塞等待直到佇列非滿;從阻塞佇列取資料時,如果佇列已空,執行緒將會阻塞等待直到佇列非空。併發包下很多高階同步類的實現都是基於BlockingQueue實現的。

image.png
image.png

以ArrayBlockingQueue為例,我們先來看看程式碼:

public void put(E e) throws InterruptedException {
    if (e == null) throw new NullPointerException();
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        while (count == items.length)
            notFull.await();
        enqueue(e);
    } finally {
        lock.unlock();
    }
}
複製程式碼

put方法的實現可以看出,它先獲取了鎖,並且獲取的是可中斷鎖,然後判斷當前元素個數是否等於陣列的長度,如果相等,則呼叫notFull.await()進行等待,當被其他執行緒喚醒時,通過enqueue(e)方法插入元素,最後解鎖。

/**
* Inserts element at current put position, advances, and signals.
* Call only when holding lock.
*/
private void enqueue(E x) {
    // assert lock.getHoldCount() == 1;
    // assert items[putIndex] == null;
    final Object[] items = this.items;
    items[putIndex] = x;
    if (++putIndex == items.length) putIndex = 0;
    count++;
    notEmpty.signal();
}
複製程式碼

插入成功後,通過notEmpty喚醒正在等待取元素的執行緒。

13.Java中的NIO,BIO,AIO分別是什麼?

IO的方式通常分為幾種,同步阻塞的BIO、同步非阻塞的NIO、非同步非阻塞的AIO

1.BIO,同步阻塞式IO,簡單理解:一個連線一個執行緒.BIO方式適用於連線數目比較小且固定的架構,這種方式對伺服器資源要求比較高,併發侷限於應用中,JDK1.4以前的唯一選擇,但程式直觀簡單易理解。

在JDK1.4之前,用Java編寫網路請求,都是建立一個ServerSocket,然後,客戶端建立Socket時就會詢問是否有執行緒可以處理,如果沒有,要麼等待,要麼被拒絕。即:一個連線,要求Server對應一個處理執行緒。

2.NIO,同步非阻塞IO,簡單理解:一個請求一個執行緒.NIO方式適用於連線數目多且連線比較短(輕操作)的架構,比如聊天伺服器,併發侷限於應用中,程式設計比較複雜,JDK1.4開始支援。

NIO本身是基於事件驅動思想來完成的,其主要想解決的是BIO的大併發問題: 在使用同步I/O的網路應用中,如果要同時處理多個客戶端請求,或是在客戶端要同時和多個伺服器進行通訊,就必須使用多執行緒來處理。也就是說,將每一個客戶端請求分配給一個執行緒來單獨處理。這樣做雖然可以達到我們的要求,但同時又會帶來另外一個問題。由於每建立一個執行緒,就要為這個執行緒分配一定的記憶體空間(也叫工作儲存器),而且作業系統本身也對執行緒的總數有一定的限制。如果客戶端的請求過多,服務端程式可能會因為不堪重負而拒絕客戶端的請求,甚至伺服器可能會因此而癱瘓。

3.AIO,非同步非阻塞IO,簡單理解:一個有效請求一個執行緒.AIO方式使用於連線數目多且連線比較長(重操作)的架構,比如相簿伺服器,充分呼叫OS參與併發操作,程式設計比較複雜,JDK7開始支援。

14.類載入機制是怎樣的

JVM中類的裝載是由ClassLoader和它的子類來實現的,Java ClassLoader是一個重要的Java執行時系統元件。它負責在執行時查詢和裝入類檔案的類。 類載入的五個過程:載入、驗證、準備、解析、初始化。

從類被載入到虛擬機器記憶體中開始,到卸御出記憶體為止,它的整個生命週期分為7個階段,載入(Loading)、驗證(Verification)、準備(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)、卸御(Unloading)。其中驗證、準備、解析三個部分統稱為連線。

面試整理-Java綜合高階篇(吐血整理)

15.什麼是冪等性

所謂冪等,簡單地說,就是對介面的多次呼叫所產生的結果和呼叫一次是一致的。 那麼我們為什麼需要介面具有冪等性呢?設想一下以下情形:

  • 在App中下訂單的時候,點選確認之後,沒反應,就又點選了幾次。在這種情況下,如果無法保證該介面的冪等性,那麼將會出現重複下單問題。
  • 在接收訊息的時候,訊息推送重複。如果處理訊息的介面無法保證冪等,那麼重複消費訊息產生的影響可能會非常大。

16.有哪些 JVM 調優經驗

Jvm引數總結:http://linfengying.com/?p=2470

記憶體引數

引數 作用
-Xmx 堆大小的最大值。當前主流虛擬機器的堆都是可擴充套件的
-Xms 堆大小的最小值。可以設定成和 -Xmx 一樣的值
-Xmn 新生代的大小。現代虛擬機器都是“分代”的,因此堆空間由新生代和老年代組成。新生代增大,相應地老年代就減小。Sun官方推薦新生代佔整個堆的3/8
-Xss 每個執行緒的堆疊大小。該值影響一臺機器能夠建立的執行緒數上限
-XX:MaxPermSize= 永久代的最大值。永久代是 HotSpot 特有的,HotSpot 用永久代來實現方法區
-XX:PermSize= 永久代的最小值。可以設定成和 -XX:MaxPermSize 一樣的值
-XX:SurvivorRatio= Eden 和 Survivor 的比值。基於“複製”的垃圾收集器又會把新生代分為一個 Eden 和兩個 Survivor,如果該引數為8,就表示 Eden
-XX:PretenureSizeThreshold= 直接晉升到老年代的物件大小。大於這個引數的物件將直接在老年代分配。預設值為0,表示不啟用
-XX:HandlePromotionFailure= 是否允許分配擔保失敗。在 JDK 6 Update 24 後該引數已經失效。
-XX:MaxTenuringThreshold= 物件晉升到老年代的年齡。物件每經過一次 Minor GC 後年齡就加1,超過這個值時就進入老年代。預設值為15
-XX:MaxDirectMemorySize= 直接記憶體的最大值。對於頻繁使用 nio 的應用,應該顯式設定該引數,預設值為0

GC引數

垃圾收集器 引數 備註
Serial(新生代) -XX:+UseSerialGC 虛擬機器在 Client 模式下的預設值,開啟此開關後,使用 Serial + Serial Old 的收集器組合。Serial 是一個單執行緒的收集器
ParNew(新生代) -XX:+UseParNewGC 強制使用 ParNew,開啟此開關後,使用 ParNew + Serial Old 的收集器組合。ParNew 是一個多執行緒的收集器,也是 server 模式下首選的新生代收集器
-XX:ParallelGCThreads= 垃圾收集的執行緒數
Parallel Scavenge(新生代) -XX:+UseParallelGC 虛擬機器在 Server 模式下的預設值,開啟此開關後,使用 Parallel Scavenge + Serial Old 的收集器組合
-XX:MaxGCPauseMillis= 單位毫秒,收集器儘可能保證單次記憶體回收停頓的時間不超過這個值。
-XX:GCTimeRatio= 總的用於 gc 的時間佔應用程式的百分比,該引數用於控制程式的吞吐量
-XX:+UseAdaptiveSizePolicy 設定了這個引數後,就不再需要指定新生代的大小(-Xmn)、 Eden 和 Survisor 的比例(-XX:SurvivorRatio)以及晉升老年代物件的年齡(-XX:PretenureSizeThreshold)了,因為該收集器會根據當前系統的執行情況自動調整。當然前提是先設定好前兩個引數。
Serial Old(老年代) Serial Old 是 Serial 的老年代版本,主要用於 Client 模式下的老生代收集,同時也是 CMS 在發生 Concurrent Mode Failure 時的後備方案
Parallel Old(老年代) -XX:+UseParallelOldGC 開啟此開關後,使用 Parallel Scavenge + Parallel Old 的收集器組合。Parallel Old 是 Parallel Scavenge 的老年代版本,在注重吞吐量和 CPU 資源敏感的場合,可以優先考慮這個組合
CMS(老年代) -XX:+UseConcMarkSweepGC 開啟此開關後,使用 ParNew + CMS 的收集器組合。
-XX:CMSInitiatingOccupancyFraction= CMS 收集器在老年代空間被使用多少後觸發垃圾收集
-XX:+UseCMSCompactAtFullCollection 在完成垃圾收集後是否要進行一次記憶體碎片整理
-XX:CMSFullGCsBeforeCompaction= 在進行若干次垃圾收集後才進行一次記憶體碎片整理

附圖:可以配合使用的收集器組合

image.png

上面有7中收集器,分為兩塊,上面為新生代收集器,下面是老年代收集器。如果兩個收集器之間存在連線,就說明它們可以搭配使用。

其他引數

引數 作用
-verbose:class 列印類載入過程
-XX:+PrintGCDetails 發生垃圾收集時列印 gc 日誌,該引數會自動帶上 -verbose:gc 和 -XX:+PrintGC
-XX:+PrintGCDateStamps / -XX:+PrintGCTimeStamps 列印 gc 的觸發事件,可以和 -XX:+PrintGC 和 -XX:+PrintGCDetails 混用
-Xloggc: gc 日誌路徑
-XX:+HeapDumpOnOutOfMemoryError 出現 OOM 時 dump 出記憶體快照用於事後分析
-XX:HeapDumpPath= 堆轉儲快照的檔案路徑

17.分散式 CAP 瞭解嗎?

一致性(Consistency) 可用性(Availability) 分割槽容忍性(Partition tolerance)

18.Java中HashMap的key值要是為類物件則該類需要滿足什麼條件?

需要同時重寫該類的hashCode()方法和它的equals()方法。

當程式試圖將一個 key-value 對放入 HashMap 中時,程式首先根據該 key 的 hashCode() 返回值決定該 Entry 的儲存位置:如果兩個 Entry 的 key 的 hashCode() 返回值相同,那它們的儲存位置相同。如果這兩個 Entry 的 key 通過 equals 比較返回 true,新新增 Entry 的 value 將覆蓋集合中原有 Entry 的 value,但 key 不會覆蓋。如果這兩個 Entry 的 key 通過 equals 比較返回 false,新新增的 Entry 將與集合中原有 Entry 形成 Entry 鏈,而且新新增的 Entry 位於 Entry 鏈的頭部——具體說明繼續看 addEntry() 方法的說明。

19.java 垃圾回收會出現不可回收的物件嗎?怎麼解決記憶體洩露問題?怎麼定位問題源?

一般不會有不可回收的物件,因為現在的GC會回收不可達記憶體。

20.終止執行緒有幾種方式?終止執行緒標記變數為什麼是 valotile 型別?

1.執行緒正常執行完畢,正常結束 2.監視某些條件,結束執行緒的不間斷執行 3.使用interrupt方法終止執行緒

在定義exit時,使用了一個Java關鍵字volatile,這個關鍵字的目的是使exit同步,也就是說在同一時刻只能由一個執行緒來修改exit的值

21.用過哪些併發的資料結構? cyclicBarrier 什麼功能?訊號量作用?資料庫讀寫阻塞怎麼解決

  • 主要有鎖機制,然後基於CAS的concurrent包。
  • CyclicBarrier的字面意思是可迴圈使用(Cyclic)的屏障(Barrier)。它要做的事情是,讓一組執行緒到達一個屏障(也可以叫同步點)時被阻塞,直到最後一個執行緒到達屏障時,屏障才會開門,所有被屏障攔截的執行緒才會繼續幹活。CyclicBarrier預設的構造方法是CyclicBarrier(int parties),其參數列示屏障攔截的執行緒數量,每個執行緒呼叫await方法告訴CyclicBarrier我已經到達了屏障,然後當前執行緒被阻塞。 CountDownLatch的計數器只能使用一次。而CyclicBarrier的計數器可以使用reset() 方法重置。
  • **Semaphore(訊號量)是用來控制同時訪問特定資源的執行緒數量,它通過協調各個執行緒,以保證合理的使用公共資源。**很多年以來,我都覺得從字面上很難理解Semaphore所表達的含義,只能把它比作是控制流量的紅綠燈,比如XX馬路要限制流量,只允許同時有一百輛車在這條路上行使,其他的都必須在路口等待,所以前一百輛車會看到綠燈,可以開進這條馬路,後面的車會看到紅燈,不能駛入XX馬路,但是如果前一百輛中有五輛車已經離開了XX馬路,那麼後面就允許有5輛車駛入馬路,這個例子裡說的車就是執行緒,駛入馬路就表示執行緒在執行,離開馬路就表示執行緒執行完成,看見紅燈就表示執行緒被阻塞,不能執行。

22.關於抽象類和介面的關係

簡言之抽象類是一種功能不全的類,介面只是一個抽象方法宣告和靜態不能被修改的資料的集合,兩者都不能被例項化。 從某種意義上說,介面是一種特殊形式的抽象類,在java語言中抽象類表示的是一種繼承關係,一個類只能繼承繼承一個抽象類,而一個類卻可以實現多個介面。在許多情況下,介面確實可以代替抽象類,如果你不需要刻意表達屬性上的繼承的話。

23.堆記憶體和棧記憶體的區別

暫存器:JVM內部虛擬暫存器,存取速度非常快,程式不可控制。 :儲存區域性變數的值包括:1.儲存基本資料型別的值;2.儲存引用變數,即堆區物件的引用(指標)。也可以用來儲存載入方法時的幀。 :用來存放動態產生的資料,比如new出來的物件。注意建立出來的物件只包含屬於各自的成員變數,並不包括成員方法。因為同一個類的物件擁有各自的成員變數,儲存在各自的堆中,但是他們共享該類的方法,並不是每建立一個物件就把成員方法複製一次。 常量池:JVM為每個已載入的型別維護一個常量池,常量池就是這個型別用到的常量的一個有序集合。包括直接常量(基本型別,String)和對其他型別、方法、欄位的符號引用(1)。池中的資料和陣列一樣通過索引訪問。由於常量池包含了一個型別所有的對其他型別、方法、欄位的符號引用,所以常量池在Java的動態連結中起了核心作用。常量池存在於堆中。 程式碼段:用來存放從硬碟上讀取的源程式程式碼。 資料段:用來存放static修飾的靜態成員(在java中static的作用就是說明該變數,方法,程式碼塊是屬於類的還是屬於例項的)。

image.png

24.關於Java檔案的內部類的解釋?匿名內部類是什麼?如何訪問在其外面定義的變數?

java中的內部類總結 靜態內部類不能訪問外部類非靜態的成員 ###25.關於過載和重寫的區別 過載是overload,是一個類中同方法名的不同具體實現。然後重寫是override,是子類重寫父類中的方法。

26.String、StringBuffer與StringBuilder之間區別

1.三者在執行速度方面的比較:StringBuilder > StringBuffer > String

String:字串常量 StringBuffer:字串變數 StringBuilder:字串變數

2.StringBuilder:執行緒非安全的,StringBuffer:執行緒安全的 **對於三者使用的總結: ** 1.如果要操作少量的資料用 = String 2.單執行緒操作字串緩衝區 下操作大量資料 = StringBuilder 3.多執行緒操作字串緩衝區 下操作大量資料 = StringBuffer

27.執行時異常與一般異常有何異同?常見異常

Java提供了兩類主要的異常:runtime exception和checked exception 常見異常:NullPointerException、IndexOutOfBoundsException、ClassNotFoundException,IllegalArgumentException,ClassCastException(資料型別轉換異常) ###28.error和exception有什麼區別? error 表示恢復不是不可能但很困難的情況下的一種嚴重問題。比如說記憶體溢位。不可能指望程式能處理這樣的情況。 exception表示一種設計或實現問題。也就是說,它表示如果程式執行正常,從不會發生的情況。 ###29.Java異常處理機制

image.png

1.捕獲異常:try、catch 和 finally 2.丟擲異常 2.1. throws丟擲異常

methodname throws Exception1,Exception2,..,ExceptionN  
{  }  
複製程式碼

30.java中有幾種方法可以實現一個執行緒?

Java多執行緒學習(吐血超詳細總結) 40個Java多執行緒問題總結


1.class Thread1 extends Thread{},然後重寫run方法 2.class Thread2 implements Runnable{},然後重寫run方法 3.class Thread3 implements Callable{},然後new FutureTask(thread3),再用new Thread(future)封裝。

class Thread1 extends Thread {
    private String name;
    public Thread1(String name) {
        this.name = name;
    }
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(name + "執行--->>>" + i);
        }
    }
    
    public static void main(String[] args) {
        Thread1 mTh11=new Thread1("A");
        Thread1 mTh12=new Thread1("B");
        mTh1.start();
        mTh2.start();
    }
}


class Thread2 implements Runnable {
    private String name;
    private int count = 15;
    public Thread2() {
    }
    public Thread2(String name) {
        this.name = name;
    }
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + "執行  :  " + count--);
        }
    }
    public static void main(String[] args) {
        Thread2 mTh2 = new Thread2();
        new Thread(mTh2, "C").start();
        new Thread(mTh2, "D").start();
    }
}


class MyCallableThread implements Callable<Integer>{
        public Integer call() throws Exception {
        int i = 0;
        for(;i<100;i++)
        {
            System.out.println(Thread.currentThread().getName()+" "+i);
        }
        return i;
    }
    
    public static void main(String[] args) {
        MyCallableThread mct = new MyCallableThread();
        FutureTask<Integer> ft = new FutureTask<Integer>(mct);
        for(int i = 0;i < 100;i++)
        {
            System.out.println(Thread.currentThread().getName()+" 的迴圈變數i的值"+i);
            if(i==20)
            {
                new Thread(ft,"有返回值的執行緒").start();
            }
        }
        try
        {
            System.out.println("子執行緒的返回值:"+ft.get());
        } catch (InterruptedException e)
        {
            e.printStackTrace();
        } catch (ExecutionException e)
        {
            e.printStackTrace();
        }
    }
}

複製程式碼

如果一個類繼承Thread,則不適合資源共享。但是如果實現了Runable介面的話,則很容易的實現資源共享。

31.Java中常用的類,包,介面。

class: 'Date','System','Calender','Math','ArrayList','HashMap' package: 'java.lang','java.util','java.io','java.sql','java.net' interface: 'Collection','Map','List','Runnable','Callable'

32.java在處理執行緒同步時,常用方法有:

1、synchronized關鍵字。 2、Lock顯示加鎖。 3、訊號量Semaphore。 4、CAS演算法 5、concurrent包

33.Spring IOC/AOP?

回答了IOC/DI、AOP的概念。 AOP(Aspect-OrientedProgramming,面向方面程式設計),可以說是OOP(Object-Oriented Programing,物件導向程式設計)的補充和完善。 OOP引入封裝、繼承和多型性等概念來建立一種物件層次結構,用以模擬公共行為的一個集合。當我們需要為分散的物件引入公共行為的時候,OOP則顯得無能為力。 也就是說,OOP允許你定義從上到下的關係,但並不適合定義從左到右的關係。例如日誌功能。日誌程式碼往往水平地散佈在所有物件層次中,而與它所散佈到的物件的核心功能毫無關係。 對於其他型別的程式碼,如安全性、異常處理和透明的持續性也是如此。這種散佈在各處的無關的程式碼被稱為橫切(cross-cutting)程式碼, 在OOP設計中,它導致了大量程式碼的重複,而不利於各個模組的重用。 依賴注入(Dependency Injection)和控制反轉(Inversion of Control)是同一個概念。 當某個角色(可能是一個Java例項,呼叫者)需要另一個角色(另一個Java例項,被呼叫者)的協助時,在傳統的程式設計過程中,通常由呼叫者來建立被呼叫者的例項。 但在Spring裡,建立被呼叫者的工作不再由呼叫者來完成,因此稱為控制反轉;建立被呼叫者 例項的工作通常由Spring容器來完成,然後注入呼叫者,因此也稱為依賴注入。 不管是依賴注入,還是控制反轉,都說明Spring採用動態、靈活的方式來管理各種物件。物件與物件之間的具體實現互相透明。 在理解依賴注入之前,看如下這個問題在各種社會形態裡如何解決:一個人(Java例項,呼叫者)需要一把斧子(Java例項,被呼叫者)。

34.對JVM的垃圾回收的認識?

垃圾回收器的作用是查詢和回收(清理)無用的物件。以便讓JVM更有效的使用記憶體。

35.程式與執行緒的區別,及其通訊方式

執行緒與程式的區別及其通訊方式 區別 1.一個程式至少有一個程式,一個程式至少有一個執行緒. 2.程式在執行過程中擁有獨立的記憶體單元,而多個執行緒共享記憶體 3.執行緒是程式的一個實體,是CPU排程和分派的基本單位

  • 程式間通訊
1.管道(Pipe)及有名管道(named pipe)
2.訊號(Signal)
3.訊息佇列(Message)
4.共享記憶體
5.訊號量(semaphore)
6.套介面(Socket)
複製程式碼

36.JVM如何GC,新生代,老年代,持久代,都儲存哪些東西?

JVM的GC演算法有:引用計數器演算法根搜尋方法

新生成的物件首先都是放在年輕代的。年輕代的目標就是儘可能快速的收集掉那些生命週期短的物件。

在年輕代中經歷了N次垃圾回收後仍然存活的物件,就會被放到年老代中。因此,可以認為年老代中存放的都是一些生命週期較長的物件。

持久代主要存放的是Java類的類資訊

37.JVM分為哪些區,每一個區幹嘛的?

問:Java執行時資料區域? 回答:包括程式計數器、JVM棧、本地方法棧、方法區、堆 問:方法區裡存放什麼? 本地方法棧:和jvm棧所發揮的作用類似,區別是jvm棧為jvm執行java方法(位元組碼)服務,而本地方法棧為jvm使用的native方法服務。 JVM棧:區域性變數表、運算元棧、動態連結、方法出口。 方法區:用於儲存已被虛擬機器載入的類資訊,常量、靜態變數、即時編譯器編譯後的程式碼等。 堆:存放物件例項。

38.GC用的引用可達性分析演算法中,哪些物件可作為GC Roots物件?

  • 虛擬機器棧(棧幀中的本地變數表)中引用的物件;
  • 方法區中類靜態屬性引用的物件;
  • 方法區中常量引用的物件;
  • 本地方法棧中JNI(即一般說的Native方法)引用的物件

39.用什麼工具除錯程式?jmap、jstack,JConsole,用過嗎?

虛擬機器效能監控與調優實戰--部落格

40.執行緒池用過嗎?

執行緒池--併發程式設計網 - ifeve.com

執行緒池(Thread Pool)對於限制應用程式中同一時刻執行的執行緒數很有用。因為每啟動一個新執行緒都會有相應的效能開銷,每個執行緒都需要給棧分配一些記憶體等等。

我們可以把併發執行的任務傳遞給一個執行緒池,來替代為每個併發執行的任務都啟動一個新的執行緒。只要池裡有空閒的執行緒,任務就會分配給一個執行緒執行。線上程池的內部,任務被插入一個阻塞佇列(Blocking Queue ),執行緒池裡的執行緒會去取這個佇列裡的任務。當一個新任務插入佇列時,一個空閒執行緒就會成功的從佇列中取出任務並且執行它。

41.作業系統如何進行分頁排程?--要考LRU

1.最講置換原則-OPT 2.先進先出原則-FIFO 3.最近最少使用置換演算法-LRU 4.時鐘置換演算法

//擴充套件一下LinkedHashMap這個類,讓他實現LRU演算法
class LRULinkedHashMap<K,V> extends LinkedHashMap<K,V>{
    //定義快取的容量
    private int capacity;
    private static final long serialVersionUID = 1L;
    //帶引數的構造器
    LRULinkedHashMap(int capacity){
        //呼叫LinkedHashMap的構造器,傳入以下引數
        super(16,0.75f,true);
        //傳入指定的快取最大容量
        this.capacity=capacity;
    }
    //實現LRU的關鍵方法,如果map裡面的元素個數大於了快取最大容量,則刪除連結串列的頂端元素
    @Override
    public boolean removeEldestEntry(Map.Entry<K, V> eldest){
        System.out.println(eldest.getKey() + "=" + eldest.getValue());
        return size()>capacity;
    }
}

複製程式碼

42.講講LinkedHashMap

Java8 LinkedHashMap工作原理及實現

LinkedHashMap是通過雜湊表和連結串列實現的,它通過維護一個連結串列來保證對雜湊表迭代時的有序性,而這個有序是指鍵值對插入的順序。

LinkedHashMap 的大致實現如下圖所示,當然連結串列和雜湊表中相同的鍵值對都是指向同一個物件,這裡把它們分開來畫只是為了呈現出比較清晰的結構。

image.png

LinkedHashMap是Hash表和連結串列的實現,並且依靠著雙向連結串列保證了迭代順序是插入的順序。

三個重點實現的函式

在HashMap中提到了下面的定義:

// Callbacks to allow LinkedHashMap post-actions
//1.把當前節點e移至連結串列的尾部。因為使用的是雙向連結串列,所以在尾部插入可以以O(1)的時間複雜度來完成。並且只有當accessOrder設定為true時,才會執行這個操作。在HashMap的putVal方法中,就呼叫了這個方法。
void afterNodeAccess(Node<K,V> p) { }
//2.afterNodeInsertion方法是在雜湊表中插入了一個新節點時呼叫的,它會把連結串列的頭節點刪除掉,刪除的方式是通過呼叫HashMap的removeNode方法。通過afterNodeInsertion方法和afterNodeAccess方法,是不是就可以簡單的實現一個基於最近最少使用(LRU)的淘汰策略了?當然,我們還要重寫removeEldestEntry方法,因為它預設返回的是false。
void afterNodeInsertion(boolean evict) { }
//3.這個方法是當HashMap刪除一個鍵值對時呼叫的,它會把在HashMap中刪除的那個鍵值對一併從連結串列中刪除,保證了雜湊表和連結串列的一致性。
void afterNodeRemoval(Node<K,V> p) { }
複製程式碼

LinkedHashMap繼承於HashMap,因此也重新實現了這3個函式,顧名思義這三個函式的作用分別是:節點訪問後、節點插入後、節點移除後做一些事情。

43.執行緒同步與阻塞的關係?同步一定阻塞嗎?阻塞一定同步嗎?,同步和非同步有什麼區別?

同步與非同步:主要是保證互斥的訪問臨界資源的情況 阻塞與非阻塞:主要是從 CPU 的消耗上來說的

44.int與Integer的區別,分別什麼場合使用

1、Integer是int提供的封裝類,而int是Java的基本資料型別
2、Integer預設值是null,而int預設值是0;
3、宣告為Integer的變數需要例項化,而宣告為int的變數不需要例項化;
4、Integer是物件,用一個引用指向這個物件,而int是基本型別,直接儲存數值。

複製程式碼

int是基本資料型別,Integer是包裝類,類似HashMap這樣的結構必須使用包裝類,因為包裝類繼承自Object,都需要實現HashCode,所以可以使用在HashMap這類資料結構中。

45.RPC的詳細過程

RPC主要的重點有: 動態代理,主要是invoke反射原理 序列化,使用Thrift的效率高 通訊方式,使用NettyNIO能提高效率 服務發現,使用zookeeper可以實現

  • 1)服務消費方(client)呼叫以本地呼叫方式呼叫服務;
  • 2)client stub接收到呼叫後負責將方法、引數等組裝成能夠進行網路傳輸的訊息體;
  • 3)client stub找到服務地址,並將訊息傳送到服務端;
  • 4)server stub收到訊息後進行解碼;
  • 5)server stub根據解碼結果呼叫本地的服務;
  • 6)本地服務執行並將結果返回給server stub;
  • 7)server stub將返回結果打包成訊息併傳送至消費方;
  • 8)client stub接收到訊息,並進行解碼;
  • 9)服務消費方得到最終結果。

相關文章