本身當初找工做時參加過衆多一線互聯網公司的Java研發面試,這段時間處於寒冬,然而前幾天跳槽找工做,兩天面了3家,已經拿了兩個offer,以爲能夠和你們分享下:java
下面爲拼多多、餓了麼、螞蟻金服、哈囉出行等公司給我留下較深印象的一些java面試題mysql
其它常常問的HashMap底層實現原理,常規的多線程問題考的太多了,沒什麼新意就不寫了程序員
平時不能光抱着應用Java的目的去學習,要深刻了解每一個知識點背後底層實現原理,爲何這麼設計,好比問爛的HashMap 既然有hash進行排位還須要equals()做用是什麼?就這個問題照樣能問倒一些人,因此必定要摳細節,真的把每一個知識點搞懂面試
寫在前面的話:算法
1.面試主要分爲兩塊:一塊是考查工程師對基礎知識(包括了技術廣度、深度、對技術的熱情度等)的掌握程度,由於基礎知識決定了一個技術人員發展的上限;另外一塊是考察工程師的工程能力,好比:作過哪些項目?遇到最難的問題怎樣解決的?說說最有成就感的一項任務?工程能力是考察工程師當下能爲公司帶來的利益。其它考覈方面:抗壓性、合做能力...暫且不說。sql
2.Java只是一門語言,即便是Java工程師也不能侷限於Java,要從面嚮對象語言自己,甚至從整個計算機體系,從工程實際出發看Java。數據庫
3.不少知識在通常公司的開發中是用不到的,常有人戲稱:「面試造火箭,工做擰螺絲」,但這只是一般狀況下公司對程序員的標準——迅速產出,完成任務。我的觀點:工程師爲了本身職業的發展不能侷限於公司對本身的要求,不能停留在應用層面,要可以很好地掌握基礎知識,要多看源碼,本身多實踐,學成記得產出,好比多爲開源社區貢獻代碼,幫助初學者指路等。編程
有沒有發現一個有意思的事情:「面試造火箭,工做擰螺絲」的背後實際上是考察者心裏深處廣泛都承認基礎知識的重要性(這一點僅爲我的觀點,不展開講哈)。設計模式
--如下爲解答大綱,部分做了擴展數組
1. 這題是一道思想題目,每天會碰到private,有沒有想過這個問題?談談對java設計的認識程度,主要抓住兩點:1.java的private修飾符並非爲了絕對安全性設計的,更可能是對用戶常規使用java的一種約束;2.從外部對對象進行常規調用時,可以看到清晰的類結構。
2. 先說結論: 基類靜態代碼塊,基類靜態成員字段(並列優先級,按照代碼中出現的前後順序執行,且只有第一次加載時執行)——>派生類靜態代碼塊,派生類靜態成員字段(並列優先級,按照代碼中出現的前後順序執行,且只有第一次加載時執行)——>基類普通代碼塊,基類普通成員字段(並列優勢級,按代碼中出現前後順序執行)——>基類構造函數——>派生類普通代碼塊,派生類普通成員字段(並列優勢級,按代碼中出現前後順序執行)——>派生類構造函數
代碼驗證:
class Log {
public static String initLog(String log) { System.out.println(log);return null; }
}
/**
* 基類
*/
class Base {
static { System.out.println("Base Static Block 1"); }
private static String staticValue = Log.initLog("Base Static Fiels");
static { System.out.println("Base Static Block 2"); }
{ System.out.println("Base Normal Block 1"); }
private String value = Log.initLog("Base Normal Field");
{ System.out.println("Base Normal Block 2"); }
Base() { System.out.println("Base Constructor"); }
}
/**
* 派生類
*/
public class Derived extends Base {
static { System.out.println("Static Block 1"); }
private static String staticValue = Log.initLog("Static Fiels");
static { System.out.println("Static Block 2"); }
{ System.out.println("Normal Block 1"); }
private String value = Log.initLog("Normal Field");
{ System.out.println("Normal Block 2"); }
Derived() { System.out.println("Derived Constructor"); }
/**
* 主線程
*/
public static void main(String[] args) {
Derived derived = new Derived();
}
控制檯結果輸出:
Base Static Block 1
Base Static Fiels
Base Static Block 2
Static Block 1
Static Fiels
Static Block 2
Base Normal Block 1
Base Normal Field
Base Normal Block 2
Base Constructor
Normal Block 1
Normal Field
Normal Block 2
Derived Constructor
3. 方法區是jvm規範裏要求的,永久區是Hotspot虛擬機對方法區的具體實現,前者是規範,後者是實現方式。jdk1.8做了改變。本題看看對方在思想層面對jvm的理解程度,很基礎的一個題目。
4. 文件中有幾個類編譯後就有幾個class文件。
5. 成員變量是能夠不經初始化的,在類加載過程的準備階段便可給它賦予默認值,但局部變量使用前須要顯式賦予初始值,javac不是推斷不出不能夠這樣作,而是沒有這樣作,對於成員變量而言,其賦值和取值訪問的前後順序具備不肯定性,對於成員變量能夠在一個方法調用前賦值,也能夠在方法調用後進行,這是運行時發生的,編譯器肯定不了,交給jvm去作比較合適。而對於局部變量而言,其賦值和取值訪問順序是肯定的。這樣設計是一種約束,盡最大程度減小使用者犯錯的可能(假使局部變量可使用默認值,可能總會無心間忘記賦值,進而致使不可預期的狀況出現)。
6. ReadWriteRock 讀寫鎖,使用場景可分爲讀/讀、讀/寫、寫/寫,除了讀和讀之間是共享的,其它都是互斥的,接着會討論下怎樣實現互斥鎖和同步鎖的, 想了解對方對AQS,CAS的掌握程度,技術學習的深度。
7. Semaphore拿到執行權的線程之間是否互斥,Semaphore、CountDownLatch、CyclicBarrier、Exchanger 爲java併發編程的4個輔助類,面試中常問的 CountDownLatch CyclicBarrier之間的區別,面試者確定是常常碰到的, 因此問起來意義不大,Semaphore問的相對少一些,有些知識點若是沒有使用過仍是會忽略,Semaphore可有多把鎖,可容許多個線程同時擁有執行權,這些有執行權的線程如併發訪問同一對象,會產生線程安全問題。
8. 寫一個你認爲最好的單例模式, 這題面試者均可能遇到過,也算是工做中最常遇到的設計模式之一,想考察面試者對常常碰到的題目的理解深度,單例一共有幾種實現方式:餓漢、懶漢、靜態內部類、枚舉、雙檢鎖,要是寫了簡單的懶漢式可能就會問:要是多線程狀況下怎樣保證線程安全呢,面試者可能說雙檢鎖,那麼聊聊爲何要兩次校驗,接着會問光是雙檢鎖還會有什麼問題,這時候基礎好的面試者就會說了:對象在定義的時候加上volatile關鍵字,接下來會繼續引伸討論下原子性和可見性、java內存模型、類的加載過程。
其實沒有最好,枚舉方式、靜態內部類、雙檢鎖都是能夠的,就想聽下對不一樣的單例寫法認識程度,寫個雙檢鎖的方式吧:
public class Singleton {
private Singleton() {
}
private volatile static Singleton instance;
public static Singleton getInstance() {
if (null == instance) {
synchronized (Singleton.class) {
if (null == instance) {
instance = new Singleton();
}
}
}
return instance;
}
}
9. B樹和B+樹,這題既問mysql索引的實現原理,也問數據結構基礎,首先從二叉樹提及,由於會產生退化現象,提出了平衡二叉樹,再提出怎樣讓每一層放的節點多一些來減小遍歷高度,引伸出m叉樹,m叉搜索樹一樣會有退化現象,引出m叉平衡樹,也就是B樹,這時候每一個節點既放了key也放了value,怎樣使每一個節點放盡量多的key值,以減小遍歷高度呢(訪問磁盤次數),能夠將每一個節點只放key值,將value值放在葉子結點,在葉子結點的value值增長指向相鄰節點指針,這就是優化後的B+樹。而後談談數據庫索引失效的狀況,爲何給離散度低的字段(如性別)創建索引是不可取的,查詢數據反而更慢,若是將離散度高的字段和性別創建聯合索引會怎樣,有什麼須要注意的?
10. 生產者消費者模式,synchronized鎖住一個LinkedList,一個生產者,只要隊列不滿,生產後往裏放,一個消費者只要隊列不空,向外取,二者經過wait()和notify()進行協調,寫好了會問怎樣提升效率,最後會聊一聊消息隊列設計精要思想及其使用。
11. 寫一個死鎖,以爲這個問題真的很不錯,常常說的死鎖四個條件,背都能背上,那寫一個看看,思想爲:定義兩個ArrayList,將他們都加上鎖A,B,線程1,2,1拿住了鎖A ,請求鎖B,2拿住了鎖B請求鎖A,在等待對方釋放鎖的過程當中誰也不讓出已得到的鎖。
public class DeadLock {
public static void main(String[] args) {
final List<Integer> list1 = Arrays.asList(1, 2, 3);
final List<Integer> list2 = Arrays.asList(4, 5, 6);
new Thread(new Runnable() {
@Override
public void run() {
synchronized (list1) {
for (Integer i : list1) {
System.out.println(i);
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (list2) {
for (Integer i : list2) {
System.out.println(i);
}
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
synchronized (list2) {
for (Integer i : list2) {
System.out.println(i);
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (list1) {
for (Integer i : list1) {
System.out.println(i);
}
}
}
}
}).start();
}
}
12. cpu 100%怎樣定位,這題是一個應用性題目,網上搜一下便可,比較常見,說實話,把這題放進來有點後悔。
13. String a = "ab"; String b = "a" + "b"; a ,b 是相等的(各位要寫代碼驗證一下,我看到有人寫了錯誤答案)。常規的問法是new一個對象賦給變量,問:這行表達式建立了幾個對象,但這樣的題目太常見。
14. int a = 1; 是原子性操做。
15. for循環直接刪除ArrayList中的特定元素是錯的,不一樣的for循環會發生不一樣的錯誤,泛型for會拋出 ConcurrentModificationException,普通的for想要刪除集合中重複且連續的元素,只能刪除第一個。
錯誤緣由:打開JDK的ArrayList源碼,看下ArrayList中的remove方法(注意ArrayList中的remove有兩個同名方法,只是入參不一樣,這裏看的是入參爲Object的remove方法)是怎麼實現的,通常狀況下程序的執行路徑會走到else路徑下最終調用faseRemove方法,會執行System.arraycopy方法,致使刪除元素時涉及到數組元素的移動。針對普通for循環的錯誤寫法,在遍歷第一個字符串b時由於符合刪除條件,因此將該元素從數組中刪除,而且將後一個元素移動(也就是第二個字符串b)至當前位置,致使下一次循環遍歷時後一個字符串b並無遍歷到,因此沒法刪除。針對這種狀況能夠倒序刪除的方式來避免
解決方案:用 Iterator。
List<String> list = new ArrayList(Arrays.asList("a", "b", "b" , "c", "d"));
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()) {
String element = iterator.next();
if(element.equals("b")) {
iterator.remove();
}
將本問題擴展一下,下面的代碼可能會出現什麼問題?
ArrayList<String> array = new ArrayList<String>();
array.add(1,"hello world");
16. 第一步 :線程池判斷核心線程池裏的線程是否都在執行任務。若是不是,則建立一個新的工做線程來執行任務。若是核心線程池裏的線程都在執行任務,則執行第二步。
第二步 :線程池判斷工做隊列是否已經滿。若是工做隊列沒有滿,則將新提交的任務存儲在這個工做隊列裏進行等待。若是工做隊列滿了,則執行第三步。
第三步 :線程池判斷線程池的線程是否都處於工做狀態。若是沒有,則建立一個新的工做線程來執行任務。若是已經滿了,則交給飽和策略來處理這個任務。
17. 抽象隊列同步器AQS(AbstractQueuedSychronizer),若是說java.util.concurrent的基礎是CAS的話,那麼AQS就是整個Java併發包的核心了,ReentrantLock、CountDownLatch、Semaphore等都用到了它。AQS實際上以雙向隊列的形式鏈接全部的Entry,比方說ReentrantLock,全部等待的線程都被放在一個Entry中並連成雙向隊列,前面一個線程使用ReentrantLock好了,則雙向隊列實際上的第一個Entry開始運行。AQS定義了對雙向隊列全部的操做,而只開放了tryLock和tryRelease方法給開發者使用,開發者能夠根據本身的實現重寫tryLock和tryRelease方法,以實現本身的併發功能。
比較並替換CAS(Compare and Swap),假設有三個操做數:內存值V、舊的預期值A、要修改的值B,當且僅當預期值A和內存值V相同時,纔會將內存值修改成B並返回true,不然什麼都不作並返回false,整個比較並替換的操做是一個原子操做。CAS必定要volatile變量配合,這樣才能保證每次拿到的變量是主內存中最新的相應值,不然舊的預期值A對某條線程來講,永遠是一個不會變的值A,只要某次CAS操做失敗,下面永遠都不可能成功。
CAS雖然比較高效的解決了原子操做問題,但仍存在三大問題。
18. synchronized (this)原理:涉及兩條指令:monitorenter,monitorexit;再說同步方法,從同步方法反編譯的結果來看,方法的同步並無經過指令monitorenter和monitorexit來實現,相對於普通方法,其常量池中多了ACC_SYNCHRONIZED標示符。
JVM就是根據該標示符來實現方法的同步的:當方法被調用時,調用指令將會檢查方法的 ACC_SYNCHRONIZED 訪問標誌是否被設置,若是設置了,執行線程將先獲取monitor,獲取成功以後才能執行方法體,方法執行完後再釋放monitor。在方法執行期間,其餘任何線程都沒法再得到同一個monitor對象。
這個問題會接着追問:java對象頭信息,偏向鎖,輕量鎖,重量級鎖及其餘們相互間轉化。
19. 理解volatile關鍵字的做用的前提是要理解Java內存模型,volatile關鍵字的做用主要有兩點:
從實踐角度而言,volatile的一個重要做用就是和CAS結合,保證了原子性,詳細的能夠參見java.util.concurrent.atomic包下的類,好比AtomicInteger。
20. AOP 和 IOC是Spring精華部分,AOP能夠看作是對OOP的補充,對代碼進行橫向的擴展,經過代理模式實現,代理模式有靜態代理,動態代理,Spring利用的是動態代理,在程序運行過程當中將加強代碼織入原代碼中。IOC是控制反轉,將對象的控制權交給Spring框架,用戶須要使用對象無需建立,直接使用便可。AOP和IOC最難得的是它們的思想。
21. 什麼是循環依賴,怎樣檢測出循環依賴,Spring循環依賴有幾種方式,使用基於setter屬性的循環依賴爲何不會出現問題,接下來會問:Bean的生命週期。
22. 上一張圖,從這張圖去理解
<img src="https://pic3.zhimg.com/v2-976a59b61d42ddbf93ceb26aa3ba44e6_b.jpg" data-caption="" data-size="normal" data-rawwidth="860" data-rawheight="406" data-default-watermark-src="https://pic2.zhimg.com/v2-78a7d1f6188d0f2e45c45cd9d7ca5e01_b.jpg" class="origin_image zh-lightbox-thumb" width="860" data-original="https://pic3.zhimg.com/v2-976a59b61d42ddbf93ceb26aa3ba44e6_r.jpg">
23. 先上結論:重複性較強的字段,不適合添加索引。mysql給離散度低的字段,好比性別設置索引,再以性別做爲條件進行查詢反而會更慢。
一個表可能會涉及兩個數據結構(文件),一個是表自己,存放表中的數據,另外一個是索引。索引是什麼?它就是把一個或幾個字段(組合索引)按規律排列起來,再附上該字段所在行數據的物理地址(位於表中)。好比咱們有個字段是年齡,若是要選取某個年齡段的全部行,那麼通常狀況下可能須要進行一次全表掃描。但若是以這個年齡段建個索引,那麼索引中會按年齡值根據特定數據結構建一個排列,這樣在索引中就能迅速定位,不須要進行全表掃描。爲何性別不適合建索引呢?由於訪問索引須要付出額外的IO開銷,從索引中拿到的只是地址,要想真正訪問到數據仍是要對錶進行一次IO。假如你要從表的100萬行數據中取幾個數據,那麼利用索引迅速定位,訪問索引的這IO開銷就很是值了。但若是是從100萬行數據中取50萬行數據,就好比性別字段,那你相對須要訪問50萬次索引,再訪問50萬次表,加起來的開銷並不會比直接對錶進行一次完整掃描小。
固然若是把性別字段設爲表的彙集索引,那麼就確定能加快大約一半該字段的查詢速度了。彙集索引指的是表自己數據按哪一個字段的值來進行排序。所以,彙集索引只能有一個,並且使用匯集索引不會付出額外IO開銷。固然你得能捨得把彙集索引這麼寶貴資源用到性別字段上。
能夠根據業務場景須要,將性別和其它字段創建聯合索引,好比時間戳,可是創建索引記得把時間戳字段放在性別前面。
後加
小編特意精心準備了一些關於面試的資料分享給你們,歡迎工做一到五年的Java工程師朋友們加入Java高級架構:706315665 羣內提供免費的Java架構學習資料(裏面有高可用、高併發、高性能及分佈式、Jvm性能調優、Spring源碼,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多個知識點的架構資料)合理利用本身每一分每一秒的時間來學習提高本身,不要再用"沒有時間「來掩飾本身思想上的懶惰!趁年輕,使勁拼,給將來的本身一個交代!
小編面試多年來總結出來的一些面試資料了以及避免被裁人的一些最新的架構技術。