這篇文章我花了兩個小時編輯,是目前我能找到的幾乎所有的問題。所以你們如果能全部掌握,基本就能收割 offer 了。時間有限的話,針對自己的情況優先選最有可能被問到的問題來準備。
文中的200道題大部分都包含了答案,希望對要參加面試的讀者有一定的幫助,這是小編爲了準備面試阿里二面所準備的面試題,出來收集了200道高級Java面試題之外,小編同時整理的Java核心筆記,Java架構面試專題整合200道(pdf文檔)
注意:因爲我並沒有系統的學習多線程編程,所以併發相關的問題我只是大概瞭解一下,目前並不深入討論(面試時直接說明即可)
- 數據結構
- 算法題
- Java基礎
- Java高級
- 三大框架
- 數據庫
- 操作系統
- 計算機網絡
- 分佈式,集羣等高級主題
- 技術開放題
- 題目參考
- 解答參考
數據結構
- 講一下 HashMap 中 put 方法過程?
- 對 Key 求 Hash 值,然後再計算 下標。
- 如果沒有碰撞,直接放入桶中,
- 如果碰撞了,以鏈表的方式鏈接到後面,
- 如果鏈表長度超過閥值(TREEIFY_THRESHOLD == 8),就把鏈表轉成紅黑樹。
- 如果節點已經存在就替換舊值
- 如果桶滿了(容量 * 加載因子),就需要 resize。
- HashMap 中 hash 函數怎麼是是實現的? 還有哪些 hash 的實現方式?
- 高 16bit 不變,低 16bit 和高 16bit 做了一個異或
- (n - 1) & hash --> 得到下標
- 還有哪些 Hash 實現方式:可以參考之前的博客 Effective Java 學習筆記 -- hashCode()
- HashMap 怎樣解決衝突,講一下擴容過程,假如一個值在原數組中,現在移動了新數組,位置肯定改變了,那是什麼定位到在這個值新數組中的位置,
- 將新節點加到鏈表後,
- 容量擴充爲原來的兩倍,然後對每個節點重新計算哈希值。
- 這個值只可能在兩個地方,一個是原下標的位置,另一種是在下標爲 <原下標+原容量> 的位置。
- 拋開 HashMap,hash 衝突有那些解決辦法?
- 開放定址,鏈地址法
- 針對 HashMap 中某個 Entry 鏈太長,查找的時間複雜度可能達到 O(n),怎麼優化?
- 將鏈表轉爲紅黑樹, JDK1.8 已經實現了。
- 數組和 ArrayList 的區別;
- 數組可以包含基本類型和對象類型,ArrayList 只能包含對象類型
- 數組大小固定,ArrayList 大小可以動態變化
- ArrayList 提供了更多的特性(addAll、removeAll)。
- Arraylist 如何實現排序
- Collections.sort(List<T> list);
- sort(List<T> list, Comparator<? super T> c);
- HashMap
- 數組 + 鏈表方式存儲
- 默認容量: 16(2^n 爲宜,若定義的初始容量不是 2^n,容量會定義爲大於該初始容量的最小 2^n)
- 例如:初始容量爲 13,則真正的容量是 16.
- put:
- 索引計算 : ((key.hashCode() ^ (key.hashCode() >>> 16)) & (table.length - 1))
- 在鏈表中查找,並記錄鏈表長度,若鏈表長度達到了 TREEIFY_THRESHOLD(8),則將該鏈轉成紅黑樹。
- 若在鏈表中找到了,則替換舊值,若未找到則繼續
- 當總元素個數超過容量*加載因子時,擴容爲原來 2 倍並重新散列
- (元素的下標要麼不變,要麼變爲【原下標+原容量】)。
- 將新元素加到鏈表尾部
- 線程不安全
- HashTable
- 數組 + 鏈表方式存儲
- 默認容量: 11(質數 爲宜)
- put:
- 索引計算 : (key.hashCode() & 0x7FFFFFFF)% table.length
- 若在鏈表中找到了,則替換舊值,若未找到則繼續
- 當總元素個數超過容量*加載因子時,擴容爲原來 2 倍並重新散列。
- 將新元素加到鏈表頭部
- 對修改 Hashtable 內部共享數據的方法添加了 synchronized,保證線程安全。
- HashMap ,HashTable 區別
- 默認容量不同。
- 索引計算方式不同。
- HashMap 特有的將過長鏈表轉換爲紅黑樹。
- 新元素的位置不同。
- 線程安全性
- HashMap、ConcurrentHashMap 區別。
- 索引計算消除了最高位的影響
- 默認容量: 16(若定義了初始容量(c),容量會定義爲大於(c + (c >>> 1) +1) 的最小 2^n)
- 例如:初始容量爲 13,則真正的容量是 32.
- 線程安全,併發性能較好
- 併發性能好的原因是 ConcurrentHashMap 並不是定義 synchronized 方法,而是在鏈表頭上同步,不同的鏈表之間是互不影響的。
- ConcurrentHashMap 原理
- 最大特點是引入了 CAS(藉助 Unsafe 來實現【native code】)
- CAS有3個操作數,內存值V,舊的預期值A,要修改的新值B。當且僅當預期值A和內存值V相同時,將內存值V修改爲B,否則什麼都不做。
- Unsafe 藉助 CPU 指令 cmpxchg 來實現
- 使用實例:
- 對 sizeCtl 的控制都是用 CAS 來實現的
- sizeCtl :默認爲0,用來控制 table 的初始化和擴容操作。
- -1 代表table正在初始化
- N 表示有 -N-1 個線程正在進行擴容操作
- 如果table未初始化,表示table需要初始化的大小。
- 如果table初始化完成,表示table的容量,默認是table大小的0.75倍,居然用這個公式算0.75(n - (n >>> 2))。
- CAS 會出現的問題:ABA
- 對變量增加一個版本號,每次修改,版本號加 1,比較的時候比較版本號。
- TreeMap 和 TreeSet 區別和實現原理
- TreeSet 底層是 TreeMap,TreeMap 是基於紅黑樹來實現的。
- 如果想實現一個線程安全的隊列,可以怎麼實現?
- 知道 LRU 嗎,20分鐘基於 HashMap 實現一個 LRU 算法,面試官給個地址,進去寫代碼,面試官遠程看
- 如何設計實現一個LRU Cache?
- 二叉樹的遍歷方式,前序、中序、後序和層序
- 可以再寫一篇了。。
- 常見的排序算法時間複雜度(排序算法實現也要重點掌握)
- 常見排序算法實現(Java)
- B+樹的瞭解
- 多分支結構有效降低了樹的高度
- B 樹的各種操作能使 B 樹保持較低的高度,從而達到有效避免磁盤過於頻繁的查找存取操作,從而有效提高查找效率
算法題
- 怎麼查詢一個單向鏈表的倒數第五個節點
- 判斷鏈表是否成環
- 兩條相交的單向鏈表,如何求他們的第一個公共節點
- 在無序數組中找最大的K個數?
- 給定n個數,尋找第k小的數,同時給出時間複雜度
- 找一個數組中的第三大數
- 找出數組中第一個出現2次的數,
- 求 1-N 中數字 1 的個數。
- 判斷一個數是不是醜數;
- 求第 K 個醜數;
- 10w行數據,每行一個單詞,統計出現次數出現最多的前100個。
- 一個文本文件,給你一個單詞,判斷單詞是否出現。
- 一進去要求敲代碼二叉排序樹的插入、刪除及查找
- 某海量用戶網站,用戶擁有積分,積分可能會在使用過程中隨時更新。現在要爲該網站設計一種算法,在每次用戶登錄時顯示其當前積分排名。用戶最大規模爲2 億;積分爲非負整數,且小於 100 萬;
- 判斷一棵二叉樹是否是 BST。
- 一副撲克 54 張牌,現在分成 3 份,每份 18 張,問大小王出現在同一份中的概率是多少;
- 50個白球50個紅球,兩個盒子,怎麼放讓人隨機在一個盒子裏抽到紅球概率最高。。。這個就是一個盒子放一個紅球,另一個盒子放99個球。
- logN 查找一個有序數組移動後類似 4 5 6 7 1 2 3裏面的一個數
- 0 ~ n 連續 n + 1 數,現在有一個長度爲 n 的數組存放了上面 n + 1 個數的其中 n 個,找出哪一個數沒有被放進數組
- 將M個平均長度爲N的有序隊列組合成一個有序隊列
- 10億條短信,找出前一萬條重複率高的
- 對一萬條數據排序,你認爲最好的方式是什麼
- 假如有100萬個玩家,需要對這100W個玩家的積分中前100名的積分,按照順序顯示在網站中,要求是實時更新的。積分可能由做的任務和獲得的金錢決定。問如何對着100萬個玩家前100名的積分進行實時更新?
Java 基礎
- Java 的優勢
- 語法簡單
- 跨平臺
- 當開發規模膨脹到一定程度,Java在規範、協作和性能調優上還是佔有很大優勢,在大型應用,尤其是企業應用上,Java的地位仍然難以撼動
- boolean 佔幾個字節
- 如果 boolean 變量在棧上,那麼它佔用一個棧單元(32-bits)
- 如果在堆上,那麼就跟 JVM 的實現有關了
- 在 Oracle 的 JVM 實現,boolean[] 中每個元素佔用一個字節(8-bits)
- Java 訪問修飾符權限的區別;
- public 所有類都可訪問
- protected 只允許包內、子類訪問。
- 默認 只允許包內訪問
- private 只允許類內訪問
- String 是否可以繼承, “+” 怎樣實現?
- String 是 final 類,不可繼承。
- + 是通過 StringBuilder(或 StringBuffer) 類,和 append 方法實現
- String,StringBuffer,StringBuilder,區別,項目中那裏用到了 StringBuffer 或者 StringBuilder
- String 不可變
- StringBuffer,可變,線程安全
- Stringbuilder,可變,線程不安全
- String爲啥不可變,在內存中的具體形態?
- String 使用 final char value[] 來存放字符序列。
- Comparable 接口和 Comparator 接口實現比較
- Comparable 是直接在"被比較"的類內部來實現的
- Comparator則是在被比較的類外部實現的
- Arrays 靜態類如何實現排序的?
- 雙軸快排
- 首先檢查數組長度,如果比閥值(286)小,直接使用雙軸快排
- 否則先檢查數組中數據的連續性,標記連續升序,反轉連續降序,如果連續性好,使用 TimSort 算法(可以很好的利用數列中的原始順序)
- 否則使用雙軸快排 + 成對插入排序
- Java 中異常怎麼處理,什麼時候拋出,什麼時候捕獲;
- 一般原則是提早拋出,延遲捕獲
- 出現異常時,若當前無法處理則拋,否則捕獲異常,嘗試恢復。
- Java 鎖機制
- 重入鎖、對象鎖、類鎖的關係
- 哪些方法實現線程安全?
- Java 中的同步機制,synchronized 關鍵字,鎖(重入鎖)機制,其他解決同步的方 volatile 關鍵字 ThreadLocal 類的實現原理要懂。
- Synchronized 和 lock 區別
- 鎖的優化策略
- 讀寫分離
- 分段加鎖
- 減少鎖持有的時間
- 多個線程儘量以相同的順序去獲取資源
- Java線程阻塞調用 wait 函數和 sleep 區別和聯繫,還有函數 yield,notify 等的作用。
- sleep 時線程的方法(讓出 CPU),wait 是對象的方法。
- 談談的 Java 反射的理解,怎麼通過反射訪問某各類的私有屬性
- 通過反射,我們可以獲取類的運行時內部結構。
- 反射 API 中有個方法 getDeclaredFields()
- 動態代理的原理
- 動態代理基於反射實現,調用者通過代理對象來訪問方法的時候,代理對象可以做相應的處理,然後通過反射調用被代理對象的方法。
- 項目中都是用的框架,用過 Servlet 嗎? Servlet 是單例嗎?多線程下 Servlet 怎麼保證數據安全的?Servlet 的生命週期?
- Thread 狀態有哪些
三大框架
- Spring 主要思想是什麼,回答 IOC 和AOP,怎麼自己實現 AOP ?
- IOC 的好處:阿里一面總結 12 題
- 使用基於反射的動態代理
- SpringAOP 用的哪一種代理
- JDK 動態代理,這種是一般意義上的動態代理;用一個代理類來間接調用目標類的方法。目標類如果實現了接口那就用這種方式代理。
- cglib 動態代理。通過框架轉換字節碼生成目標類的子類,並覆蓋其中的方法實現增強,因爲採用的是繼承,所以不能對 final 類進行代理。目標類沒有實現任何接口,就使用這種方法
- spring bean 初始化過程
- 讀取 XML 資源,並解析,最終註冊到 Bean Factory 中
- spring bean 對象的生命週期
- 當一個 bean 被實例化時,它需要執行一些初始化(init-method)使它轉換成可用狀態。同樣,當 bean 不再需要,並且從容器中移除時,需要做一些清除工作(destroy-method)
- 講講 Spring 中 ApplicationContext 初始化過程。
- SpringMVC 處理請求的流程
- 收到用戶請求
- dispatcher Servlet 將請求轉發到相應的 Controller
- 通過 View Resolver 進行視圖解析
- 返回給用戶
數據庫
- SQL 優化方案
- 根據我目前的知識水平,大概分爲兩類:
- 多表連接時不直接連接表,而是先用 where 篩選出符合條件的記錄然後進行連接。一般情況下,篩選一次會除去相當多的無效記錄,這會極大的提高效率。
- 判斷當前的 SQL 是否合理的使用了索引。如果設置的索引沒有使用的話,會導致全表掃描。效率上會差很多。沒有利用索引的情況一般有以下幾種:
- 以“%”開頭的LIKE語句,模糊匹配
- OR 語句前後沒有同時使用索引
- 數據類型出現隱式轉化(如varchar不加單引號的話可能會自動轉換爲int型)
- where 子句中對字段進行表達式操作
- 在where子句中對字段進行函數操作
- 索引有哪些?分別有什麼特點?
- 從底層數據結構來劃分的話,主要有兩種:一種是基於 B+ 樹的索引,一種是基於哈希表的索引。基於哈希表的索引在等值查詢上有絕對的優勢,但其他方面就不是很好了。B+ 樹是一種多分支的樹結構,相比二叉樹來說高度降低了很多,能夠有效的減少磁盤 IO,所以我們平時使用的都是基於 B+ 樹的索引
- 索引爲什麼用 B 樹不用二叉樹,有什麼好處?
- 基於 B 樹的索引實現,降低了樹的高度,減少了磁盤 IO 的次數。
- 數據庫索引優點和缺點
- 優點:有效加速查詢;
- 缺點:操作數據時需要對索引進行更新,效率上稍微差一點;索引需要佔用一定的空間。
- 數據庫事務的四個隔離級別,MySql 在哪一個級別
MySQL 默認隔離級別爲 Repeatable read
操作系統
- 進程和線程的區別
- 進程是擁有資源的基本單位,線程是 CPU 調度的基本單位
- 進程擁有獨立的地址空間,同一個進程的線程共享該進程的地址空間
- 進程上下文切換相對線程上下文切換會消耗更多的資源
- 一個進程必須至少擁有一個線程
- 一個線程死掉就等於整個進程死掉,所以多進程的程序相對於多線程的程序來說會更健壯
- 通信方式不同,線程通過進程內的資源進行通信,進程的通信有多種方式,包括管道、共享內存、消息等等。
- 進程間通信
- 管道(Pipe)及有名管道(named pipe):管道可用於具有親緣關係進程間的通信,有名管道克服了管道沒有名字的限制,因此,除具有管道所具有的功能外,它還允許無親緣關係進程間的通信;
- 信號(Signal):信號是比較複雜的通信方式,用於通知接受進程有某種事件發生,除了用於進程間通信外,進程還可以發送信號給進程本身;
- 報文(Message)隊列(消息隊列):消息隊列是消息的鏈接表。有足夠權限的進程可以向隊列中添加消息,被賦予讀權限的進程則可以讀走隊列中的消息。消息隊列克服了信號承載信息量少,管道只能承載無格式字節流以及緩衝區大小受限等缺點。
- 共享內存:使得多個進程可以訪問同一塊內存空間,是最快的可用IPC形式。是針對其他通信機制運行效率較低而設計的。往往與其它通信機制,如信號量結合使用,來達到進程間的同步及互斥。
- 信號量(semaphore):主要作爲進程間以及同一進程不同線程之間的同步手段。
- 套接口(Socket):更爲一般的進程間通信機制,可用於不同機器之間的進程間通信。
- 在共享內存中如何使用 mutex
- select 和 epoll
- 操作系統由哪幾部分組成,進程結構
- 多進程和多線程的區別
- 什麼時候使用多線程,什麼時候使用多進程
- Java 多線程與操作系統線程的關係
- 一般線程和守護線程的區別
- 多線程與多線程的實現方式
- 死鎖的條件,死鎖避免。
- 互斥條件
- 佔有和等待條件
- 不剝奪條件
- 循環等待
- linux中如何查看進程等命令
- 不同進程打開了同一個文件,那麼這兩個進程得到的文件描述符(fd)相同嗎
- 不一定,因爲打開文件有三個表,inode 表,系統文件描述符表,進程文件描述符表。不同進程的文件描述符的範圍是一樣的,有可能剛好相同,也有可能不相同
- 兩個線程如何同時監聽一個端口。
- SO_REUSEPORT 參數。
計算機網絡
- HTTP 狀態碼有哪些,一一解釋含義
- 1xx 消息
- 100 服務器僅接收到部分請求,但是一旦服務器並沒有拒絕該請求,客戶端應該繼續發送其餘的請求。
- 2xx 成功
- 200 OK 請求成功(其後是對GET和POST請求的應答文檔。)
- 3xx 重定向
- 304 Not Modified 未修改的文檔。客戶端有緩衝的文檔併發出了一個條件性的請求(一般是提供If-Modified-Since頭表示客戶只想比指定日期更新的文檔)。服務器告訴客戶,原來緩衝的文檔還可以繼續使用。
- 4xx: 客戶端錯誤
- 400 Bad Request 服務器未能理解請求。
- 404 Not Found 服務器無法找到被請求的頁面。
- 5xx: 服務器錯誤
- 500 Internal Server Error 請求未完成。服務器遇到不可預知的情況。
- HTTP 請求頭有哪些,介紹平時見過的,怎麼利用這些信息來進行前後端調試
- Host, 請求的域名
- User-Agent,用戶的瀏覽器版本信息
- Accept,響應的內容類型
- Accept-Language, 接受的語言
- Accept-Encoding, 可接受的編碼方式
- Cookie,本地的 Cookie 信息
- if-Modified-Since, 本地有緩存,如果在那之後沒有做修改,則可以直接使用本地緩存。
- TCP 和 UDP 的區別
- HTTP1.0 和 1.1 的區別
- 最主要的區別是 1.1 支持持久連接。Connection 請求頭的值爲 Keep-Alive 時,客戶端通知服務器返回本次請求結果後保持連接;Connection 請求頭的值爲 close 時,客戶端通知服務器返回本次請求結果後關閉連接。
- 1.1 支持斷點續傳。RANGE:bytes=XXX 表示要求服務器從文件 XXX 字節處開始傳送
- 還有一些其他的改進,有興趣可以自行查閱相關資料
- HTTP 和 HTTPS 的主要區別
- 安全。HTTP 直接與 TCP 通信,而 HTTPS 是先與 SSL(加密) 通信,然後再由 SSL 和 TCP 通信
- 滑動窗口算法
- 又稱回退 N 步(go-back-N),發送方的窗口滑動是由接收方是否已成功收到數據包來決定的。即接收方的窗口向前滑動後發送方的窗口才會向前滑動。//TODO
- 域名解析詳細過程
- IP 地址分爲幾類,每類都代表什麼,私網是哪些
- A:前 1 byte 爲網絡標識,剩下的是主機標識
- B:前 2 bytes 爲網絡標識
- C:前 3 bytes 爲網絡標識
- D:爲多播地址,最高位爲 1110
- E:特殊 IP。例如 0.0.0.0,127.0.0.1,255.255.255.255 等等
- IP 頭組成;
- 計算機網絡中的同步和異步
- 發現百度上不去,怎麼辦
- 查看 DNS 解析是否正確。若有錯誤,刪除本地 DNS 緩存
- 若 DNS 沒有問題,使用 traceroute 檢測路徑,若路徑不通則說明網路阻塞,暫時就別上網了
- traceroute 沒有問題,ping 也能通一般就是服務器端出問題了。
分佈式/集羣等高級主題
- 負載均衡算法
- 隨機:負載均衡方法隨機的把負載分配到各個可用的服務器上。
- 輪詢:按順序將新的連接請求分配給下一個服務器
- 加權輪詢:每臺服務器接受到的連接數按權重分配,一般是用在應用服務器的處理能力大小不同的情況下。
- 最少連接:把新連接分配給當前連接最少的服務器
- BLABAL...
- 分佈式數據庫
- 分佈式數據庫提供了原來集中式數據庫不具備的高可用性和拓展能力
技術開放題
- 如何設計一個高併發的系統
- 數據庫的優化,包括合理的事務隔離級別、SQL語句優化、索引的優化
- 使用緩存,儘量減少數據庫 IO
- 分佈式數據庫、分佈式緩存
- 服務器的負載均衡
- 現在一個網頁響應速度明顯變慢了,假如我把這個任務交給你,你怎麼處理這個問題
- 負載均衡的大題,數千萬的負載部署到機器上,要求對問題進行抽象,建模,提出解決方案。
- 美團面試官來到一個城市面試應聘者,面試有三天,每天面試官上午可以面試三場,下午可以面試四場,怎麼設計面試系統,面試者可以選擇面試日期,面試時間和面試官。
- 有一些爬蟲IP不斷的訪問美團網站,現在美團設定一個IP5分鐘之內訪問美團網站超過100次,就判定爲爬蟲IP,怎麼設計這個程序?如果100改成10000,怎麼設計?
- 假設在某一時刻由幾萬個併發請求同時產生,請設計一個方案來處理這種情況。
- 問我簡歷上學校 oj 平臺這個項目怎麼實現1000人併發?併發的性能瓶頸在哪?
- 因爲還沒完成,現處於開發階段,只跟面試官說了下自己的構想,nginx+tomcat集羣,性能瓶頸可能出現在網絡io和java gc上,然後說了下jvm gc的優化,如何實現session共享。最後我問了下面試官這樣設計可以嗎,他說這樣設計不行可能有問題,沒有告訴我問題出現在哪裏。
讀者福利、完整面試題【含答案】Java核心筆記,Java架構面試專題整合800道(pdf文檔)si xin免費領取方式