1. 程式人生 > 程式設計 >隨便分享點不那麼常規的面試題(一)

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

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,此題有待進一步研究 ↩︎