隨便分享點不那麼常規的面試題(一)

NO IMAGE

1. 為什麼HashMap允許key和value為空,而Hashtable和ConcurrentHashMap不允許key和value為空?

本題主要考察是否熟悉各種map的應用場景,如果只說出key/value為空無意義是不夠的,要能考慮到更根本的原因

直接原因

  • HashMap將null的hash值設置為0,所以允許key和value為空
  • Hashtable會檢測當value為空時拋出NPE,同時在計算hash值時直接調用了key的hashcode方法,如果key為空會拋出NPE
  • ConcurrentHashMap會檢測當key或value為空時拋出NPE

根本原因

  • HashMap是線程不安全的,所以在設計時可以只考慮單線程執行場景。使用get(key)方法前想判斷一個key-value鍵值對是否存在於Map中時,可以調用containsKey方法,該方法是根據是否存在索引節點來判斷的,所以value為空也無所謂,又因為是單線程的,所以containsKey方法和get方法不會出現線程安全問題
  • Hashtable和ConcurrentHashMap是線程安全的,在設計時,默認使用場景是多線程下。如果允許value為空,則用戶調用get(key)方法後返回null不能判斷是否存在對應的key-null鍵值對,需要額外使用containsKey來進行判斷,在多線程場景下需要用戶手動進行加鎖。為了減少用戶使用的負擔,以及避免由於誤使用帶來嚴重的後果,同時也考慮到key/value為空也沒有太大的意義,所以就禁止key/value為空

2. 講一下linux的awk命令

本題主要考察對linux的使用深度,屬於加分項,沒有用過awk至少要讓面試官知道你用過grep,如果都沒有用過的話,面試官會懷疑你是否有linux的使用經驗

awk是一個文本分析工具,將文件逐行讀入,然後默認按空格進行切片,對切開的部分進行單獨分析

格式是awk [options] '{command}' filename,如awk '{print $1}' out.txt,其中$1代表第1列,awk會默認按照分隔符來切分每一行

分隔符也可以通過在awk後使用-F來指定,如awk -F ':' '{ print $1 }' out.txt

3. 數據庫水平分表之後怎麼查詢

本題主要考察你是否有考慮過分表之後的事情,而不是僅僅背了幾道面試題,說出聚合操作後這道題基本就通過了

如果查詢單條記錄,可以根據查詢條件和分表規則找到對應的表,然後進行查詢並返回結果

如果查詢多條記錄,則分開查詢,將所有查詢結果做聚合,將聚合後的結果返回

4. mysql中select *** from *** limit offset, rows會有什麼問題,怎麼解決

本題主要考察數據庫查詢優化的知識,至少要能給出兩種解決方案,並說出其各自的優缺點

存在問題:當offset很大時,查詢效率會降低,因為mysql並不能直接定位到offset處進行查詢,而是查詢前offset+rows行,然後只取後rows條數據,將前面的數據捨棄,所以效率會很低

解決方案:

  1. limit offset, rows刪去,使用where 主鍵 > offset limit rows代替,但是使用前提是沒有其他where條件對數據進行過濾
  2. 在子查詢中查出索引,外層查詢根據索引來定位數據,前提也是不能有其他where條件對數據進行過濾
  3. 如果偏移量超過記錄數的一半,則反序查詢,可以保證偏移量不會超過記錄數的一半
  4. 限制limit的偏移量,不允許超過某個值
  5. 用額外的表記錄數據和頁數的關係,但是隻適用於每頁條數固定的情況

5. Java中final的實現原理是什麼

本題主要考察一些語言層面上的知識,如果知道泛型是語法糖的話,這個問題也能很容易回答出來

Java中final和private、泛型一樣,都是一種編程約束,編譯器會檢查final修飾的變量是否被重新賦值等,在編譯後與普通變量沒有區別

8. Java是編譯型語言還是解釋型語言,JVM是編譯器嗎,什麼是JIT?

本題主要考察對Java語言的熟悉程度,能全部說正確當然最好,至少也得要能說出JVM是解釋器,Java是解釋型語言

Java既是解釋型語言也是編譯型語言。Java代碼需要經過javac編譯器編譯成class文件,交給JVM進行解釋執行,為了加快執行效率,同時Java引入JIT編譯技術(Just-in-Time Compile,即時編譯技術),將部分熱點代碼編譯成機器碼優化執行

7. 逃逸分析瞭解嗎

本題主要考察對Java優化技術的瞭解程度,屬於加分項,回答好的話能給面試官留下深刻印象

Java引入JIT技術後,會將熱點代碼編譯成本地機器相關的機器碼,編譯時會進行相應的優化,逃逸分析就是JIT優化的基礎

一個對象在方法中被定義後,如果被外部方法所引用,比如通過參數傳遞給其他方法,就叫做(方法)逃逸。同樣地,如果一個對象能被其他線程訪問到,也叫做逃逸。逃逸分析就是來分析對象是否發生了逃逸,通過逃逸分析,Java編譯器可以分析出一個對象的使用範圍

當JIT使用逃逸分析時,可以執行相應的優化策略:

  • 同步省略/鎖消除:如果同步塊只能被一個線程所訪問,則加鎖是沒有必要的,在優化時,同步塊上的鎖會被省略
  • 對象分離/標量替換:如果一個對象不會發生逃逸,則將對象屬性的引用替換為標量,從而避免創建對象
  • 棧上分配:將不會發生逃逸的對象直接在棧上分配內存,而不是堆中,這樣可以省略垃圾回收的操作(實際上還是使用標量替換來完成的,沒有真正實現棧上分配)

逃逸分析這項技術至今仍不成熟,無法保證逃逸分析所提高的運行效率能超過它的性能損耗,但是逃逸分析依然是JIT編譯器優化技術中一個重要手段

8. TCP粘包問題了解過嗎,怎麼解決

本題主要考察對TCP協議的瞭解程度,能回答出來TCP這塊基本就不會再問了。一般TCP部分的問題深度由淺至深分別是:TCP/UDP區別 => 三次握手與四次揮手 => 流量控制和擁塞控制 => TCP優化技術與常見問題,必須要做好準備

粘包問題有兩種常見情況:

  1. TCP協議為了將包更有效地發送到對象,所以使用Nagle優化算法,將多個小數據包合併成大數據塊進行封包,因為TCP傳輸的數據是沒有邊界的,所以接收方會收到一個粘成一團的包
  2. 接收方沒有即時接收消息,導致接收方的TCP緩衝區存放了幾段數據,接收方的應用層就會收到不完整的數據

解決方案有兩種,一種是隻發送固定長度的包,另一種是在發送消息時帶上消息的長度,同時使用特殊標記來標記消息邊界

9. 為什麼主鍵索引最好是自增的

本題考察的是你的思考能力,如果這題答得不好會給面試官留下只會背題的印象,此題不難,即使不知道答案只要給出自己的想法即可,重點是聯繫到索引的數據結構上

因為innodb索引採用b+樹的結構存儲,避免插入非規律主鍵的數據時,導致頁分裂

10. 接口和抽象方法哪一個調用速度快,為什麼

本題考察對JVM知識的掌握程度,雖然本題應用意義不大,但是能很好地考察出被面試者對JVM底層原理的瞭解情況

抽象方法調用速度快,因為抽象類屬於實現類的父類,抽象方法的實現屬於方法覆寫,可以直接根據偏移量來定位方法,而接口則只能通過遍歷來找

11. 看下面這段代碼,執行結果是什麼,為什麼

        String a1 = new String("a") + new String("a");  // 1
a1.intern();                                    // 2
String a2 = "aa";                               // 3
System.out.println(a1 == a2);
// -------------------------------------
String b1 = new String("b") + new String("b");  // 4 
String b2 = "bb";                               // 5
b1.intern();                                    // 6
System.out.println(b1 == b2);

本題考察的是面試者對String的理解深度,此題的要點就是能答出intern方法的實現原理,能夠完全答對非常難,基本能打趴絕大多數面試者,能夠回答出來說明對JVM的常量池很熟悉,絕對是一個加分項,不過遺憾的是基本沒有面試官會問這麼深

在jdk1.7之前,返回false false[1],在jdk1.7及之後的jdk版本中,返回true false

為什麼在jdk1.7之前返回false false

  • 因為在jdk1.6中,字符串常量池在“永久代”中,調用String#intern方法時,如果常量池中已經存在該字符串對象,會直接返回常量池中的對象引用;否則會在常量池中創建一個對象,並返回常量池中對象的引用
  • 回到代碼中,代碼1在堆中創建了“aa”字符串對象,池中沒有“aa”字符串對象;代碼2發現常量池中沒有“aa”對象,則在池中創建“aa”對象;代碼3發現池中存在“aa”對象,直接返回常量池中對象的引用;此時,a1為堆中字符串對象引用,a2為常量池中字符串對象引用,顯然指向的不是同一個對象
  • 代碼4在堆中創建了“bb”字符串對象,池中沒有“bb”字符串對象;代碼5發現池中沒有“bb”對象,則在池中創建“bb”對象;代碼6發現常量池中存在“bb”對象,不執行操作;此時,b1為堆中字符串對象引用,b2為常量池中字符串對象引用,顯然指向的不是同一個對象

為什麼在jdk1.7及之後返回true false

  • 在jdk1.7中,常量池被存放在Java堆中,jdk1.8中,常量池被放在元空間中,與堆獨立(其實主要跟intern方法的實現改變有關,和常量池在哪沒有太大關係,所以別被誤導了,這一點只是做個知識擴展)
  • 當調用String#intern方法時,如果常量池中已經有該對象,則返回常量池中對象的引用;否則,將Java堆中的對象引用添加到常量池中,並返回該引用;如果Java堆中也不存在,則在常量池中創建該字符串對象,並返回其引用
  • 回到代碼中,代碼1在堆中創建了“aa”字符串對象,池中沒有“aa”字符串對象;代碼2發現常量池中不存在“aa”對象,則檢查Java堆,發現存在“aa”對象,則將堆中該對象的引用添加到常量池中;代碼3發現常量池中存在“aa”對象的引用,則返回該引用;此時,a1為堆中對象的引用,a2也為堆中該對象的引用,指向的是同一對象
  • 代碼4/5/6與jdk1.6中的原理完全一致,就不再贅述了

12. ConcurrentHashMap的擴容是如何實現的

本題考察的是對ConcurrentHashMap源碼熟悉程度,答題要點是分區間處理和單節點加鎖。如果是考察HashMap的擴容,則答題要點是先創建新數組,再拷貝原數據

  1. 根據處理器個數,計算出每個處理器要處理的區間個數
  2. 創建一個新數組
  3. 計算每個線程需要處理的區間段大小
  4. 遍歷每個索引位置,遍歷到時對索引節點單個進行加鎖
    1. 將索引位置下所有節點分為兩個鏈表,一個是擴容後位置不變的,一個是擴容後位置改變的
    2. 處理完成後將這兩個鏈表一次性放置到原數組對應位置上(使用原子賦值操作,不會影響線程安全)

13. Service Mesh/Serverless是什麼

本題主要考察面試者的技術視野,是否僅僅關注自己眼前的技術。這種就屬於概念性的問題,能說多少說多少,沒有標準答案,能體現出自己有了解過大致的概念即可。這兩個問題本身沒有太大關聯,但是因為都屬於這一類問題,不想再單獨列出來,就直接放在一起了

  • Service Mesh,指服務網格,屬於基礎設施層,功能在於處理服務間通信,是系統通信的中間層,負責實現請求的可靠傳遞。Service Mesh一般由一系列輕量級的網絡代理組成,和應用程序部署在一起,但是對應用程序透明(不可見)。可以解耦應用程序的重試、超時、監控、追蹤,以及服務發現,讓應用服務專注於業務邏輯
  • Serverless,指無服務架構,可以讓開發者不用考慮服務器的問題,將計算資源作為服務,而不是服務器。Serverless是一種構建和管理基於微服務架構的完整流程,允許開發者將應用以服務的級別來部署,而不是服務器級別。可以不用去關心服務器運行狀態,不用考慮運營維護問題

14. 靜態方法和單例模式有什麼區別,我們為什麼推薦使用單例模式

本題主要考察面試者是否對自己使用的工具有過思考,能夠知道“為什麼用”,一個工程師至少要清楚自己使用工具的場景,而不能是稀裡糊塗的“有就用”

  1. 單例模式契合面向對象的思想
  2. 單例類中的方法可以繼承覆寫
  3. 單例類可以被延遲實例化
  4. 單例類可以被用於多態,不需要關注全局的狀態

15. 偽共享瞭解嗎

本題考察的是面試者對線程安全(特指volatile部分)的掌握程度,如果認真看過JUC包下的源碼,就一定會瞭解到偽共享的概念。不過本題屬於加分項,即便不知道也沒有太大影響

偽共享指的是兩個volatile類型的數據在同一個緩存行中,由於volatile每次都會刷新緩存行,所以導致緩存行沒有完全發揮出作用,致使性能浪費

可以通過在對象或字段的前後加padding,也可以使用@sun.misc.Contended註解(其原理是在對象或字段前後加上128位的padding)

16. SpringBoot是怎麼啟動的

本題主要考察對Springboot的瞭解程度,答題要點是設置監聽器和加載上下文,如果這題沒答上來或沒答好面試官可能會問IoC和AOP原理相關的問題,所以至少有一個要非常熟悉

  • @SpringbootApplication複合了一些註解:
    • @SpringBootConfiguration:聲明該類為配置類
    • @EnableAutoConfiguration:啟用自動配置,原理是使用AutoConfigurationImportSelector類,將所有配置類加載到容器中,並掃描配置文件,將配置項通過反射實例化為配置類,彙總之後加載到容器中
    • @ComponentScan:自動掃描包下的bean
  • Springboot在啟動時,通過SpringApplication的run方法啟動容器。在run方法中,分為兩步:
    1. 先構建SpringApplication對象,其中會設置成員變量和監聽器,最終推斷main方法所在的入口類
    1. 然後調用SpringApplication具體實例對象的run方法,會遍歷並啟動監聽器,封裝args參數為對象,初始化並加載應用上下文

17. 適配器模式的應用場景是什麼,舉幾個具體應用的例子

本題主要考察對適配器這種設計模式的掌握程度,現在很多人為了突擊面試,設計模式只看過單例和工廠兩種,如果問到其他設計模式答不上來,會在面試官的心中大打折扣

應用場景:

  • 需要使用某個類,但是接口不符合需要
  • 需要將一些沒有公共接口/父類的類關聯起來,使其可以一起工作
  • 需要為一些類提供統一的輸出接口

應用實例:

  • spring-aop:將不同的通知類型適配到統一的適配器接口AdvisorAdapter中,可以通過具體的適配器類來獲取對應通知的攔截器
  • spring-mvc:使用HandlerAdapter來適配具體的Controller,實現了DispatcherServlet和Controller之間的統一接口對接

18. CountDownLatch的await()方法有什麼風險

本題主要考察面試者是否真正用過CountDownLatch,是否在使用時有考慮過風險。與此題類似的問題還有:ThreadLocal使用時需要注意什麼,SimpleDateFormat在多線程下使用有什麼風險等

如果執行countDown的線程發生異常,則await()方法會被阻塞。所以要設置超時時間,所以要使用await(timeout, unit)方法

19. 為什麼有些公司棄用zookeeper

本題主要考察對zookeeper的掌握程度,能說出一種工具的優點很容易,有時候編也能編個差不多,但是想說出來缺點就需要對它有相當的瞭解才行。如果是校招的話一般不會問這麼深,回答不上來也不用太緊張,但是一定要敢於說出來自己的見解,即使是錯的

客戶端:

  • 服務端集群的地址列表是寫死在客戶端的,不支持動態變更
  • 不支持同機房優先策略
  • 沒有針對服務端集群完全不可用的容災策略

服務端:

  • 寫能力不足,不支持水平擴展
  • 監測手段單一,只有TCP的活性監測

20. Java中基本數據類型是對象嗎

本題考察的是對Java語言的理解程度,這個問題幾乎沒見過有面試官問過,問題本身的價值也不是很大,如果答不上來也沒有太大關係,不過確實能反應出面試者對語言的認識程度

JVM會把所有的基本數據類型當作對象處理,JVM有9種設定好的Class對象來對應基本數據類型void關鍵字,這些對象都是JVM創建的

除了基本類型之外,枚舉也是一種類,註解屬於一種特殊的接口,數組也是Object的子類,但是對程序員是透明的


  1. 勘誤:使用openjdk6測試發現輸出依然是true false,此題有待進一步研究 ↩︎

相關文章

手寫源碼(一):自己實現Spring事務

淺談Kafka特性與架構

淺談Spring事務中的7種傳播特性

隨便分享點不那麼常規的面試題(二)