面試總結(知乎+京東)
阿新 • • 發佈:2019-01-04
知乎面試問題
一面
- 演算法題
兩個有序陣列,輸出第k小的數字
思想時:分別折半查詢,每個陣列記錄自己的left,right索引,進行查詢。 - hashmap
- 原始碼結構
- hashmap在擴容時空間建立+新舊節點的對應關係+如果擴容過程中查詢該怎麼查詢**(分析如下)**
- 究竟哪些節點需要申請新的儲存空間?哪些不需要呢?
- 需要新申請空間的節點:newTab,即為新table陣列,申請空間個數為32、64、128等。
- 不需要新申請空間的節點:所有連結串列或者紅黑樹上的節點;
- 原始碼:resize()方法
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
- 新舊索引有什麼關係?
- 因為我們使用的增長策略是2的整數次冪方式,table的容量在更改時,同一元素在table中的索引要麼不變,要麼移動到相對原位置而言距離2的整數次冪的一個位置。
- 舊連結串列中的節點如何對應到新的table索引中?
思想:對這個舊連結串列進行遍歷,從而先生成兩個連結串列;然後一個放在原來索引位置j處,一個放在[j+oldCap]處; - 那麼生成兩個連結串列的策略是怎樣的呢?
if ((e.hash & oldCap) == 0) 條件成立的節點是放在原來索引位置j處的連結串列;
否則,放在j+oldCap索引處的連結串列;
- 查詢引數包括:hash(key)和key。那麼接下來如何進行查詢?
- 先獲得first節點的索引,其索引策略是:
first = tab[(n - 1) & hash] - 接下來如果first節點為紅黑樹節點,則按照紅黑樹查詢方式查詢;
- 如果first節點為連結串列節點,則按照連結串列查詢方式進行查詢;
- 那麼問題來了,如果此時hashMap在擴容,那麼tab究竟是新tab陣列還是舊tab陣列?
通過resize()方法的原始碼可以發現,有這樣一步操作:
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
table = newTab;
並且這步操作的後面,才會發生生成新的兩個連結串列的操作。所以如果hashMap擴容過程中發生查詢操作,則在table沒有被重新賦值前就在舊table中查詢;否則在新table中查詢;但在新table的查詢中,可能新的2個連結串列還沒有生成導致存在的元素查詢結果也是null;不過本來hashMap也是執行緒不安全的,返回併發導致的錯誤結果也能理解。
- 先獲得first節點的索引,其索引策略是:
- 究竟哪些節點需要申請新的儲存空間?哪些不需要呢?
- jvm記憶體模型+gc演算法
- mysql索引實現
- B樹和B+樹的對比
- LRU cache的實現方式?自己說了連結串列方式,不知道還有什麼方式
- LinkedHashMap的實現方式
- 一個包含3個指標節點的雙向連結串列。節點指標為before,next,after。分別指向:
雙向連結串列的前一個節點;
hash值衝突的邏輯連結串列;
雙向連結串列的後一個節點;
參考:https://blog.csdn.net/caoxiaohong1005/article/details/79909083 - 時間複雜度
- get時,最好O(1),最差O(N);
- put時,最好O(1),最差O(N);
- 空間複雜度
O(N)
- 一個包含3個指標節點的雙向連結串列。節點指標為before,next,after。分別指向:
- 單鏈表+hashmap
- 底層使用有頭指標的單鏈表,同時用hashmap對每個key維護一個前驅節點;
- 時間複雜度
- get時,首先從hashmap中找到對應的節點,然後移到頭節點,並更改hashmap的對映關係;
- set時,如果cache未滿,則直接插入到頭節點前面,並更改hashmap的對映關係;
- set時,如果cache滿了,則將重用連結串列尾節點,然後挪到表頭,並更改hashmap的對映關係;
- 空間複雜度
O(N)
- LinkedHashMap的實現方式
- 網路程式設計socket,自己說不會也就沒有再問
- TCP/IP 的三次握手和四次揮手過程
二面
-
講解實習專案架構和自己實現的模組功能
-
springAOP的實現原理
- java動態代理可以對類實現嗎?不知道,只知道是對interface的實現
- java動態原理
-
使用到的class和interface包括:Proxy類和invocationHandler介面。
-
需要自己寫的class和interface
1)被代理物件的interface
2)被代理物件的interfaceImpl
3)動態代理的InvocationHandler實現類 -
JVM生成動態代理類
傳入引數:定義代理類的類載入器,代理類要實現的介面列表,invocationHandler例項import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * @Author: cxh * @CreateTime: 18/9/23 00:48 * @ProjectName: JavaBaseTest */ public class AopTest { public static void main(String[] args) { //定義被代理物件 Subject subjectImpl = new SubjectImpl(); //定義InvocationHandler介面例項 InvocationHandler invocationHandler = new InvocationHandlerImpl(subjectImpl); //生成動態代理例項(定義代理類的類載入器,代理類要實現的介面列表,invocationHandler例項) Subject subject = (Subject) Proxy.newProxyInstance(invocationHandler.getClass().getClassLoader(), subjectImpl.getClass().getInterfaces(), invocationHandler); System.out.println("subject.getClass().getName():" + subject.getClass().getName()); subject.before(); } } /** *被代理物件的介面 */ interface Subject { void before(); void after(); } /** * 被代理物件 */ class SubjectImpl implements Subject { @Override public void before() { System.out.println("this is method of before()."); } @Override public void after() { System.out.println("this is method of after()."); } } /** * 動態代理,必須實現InvocationHandler介面 */ class InvocationHandlerImpl implements InvocationHandler { Subject subject; /** * 建構函式引數必須包括:被代理物件,這樣呼叫動態代理的invoke()方法時,才會呼叫被代理物件的方法. */ InvocationHandlerImpl(Subject subject) { this.subject = subject; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("before invoke()"); System.out.println("the method is :" + method); method.invoke(subject, args); System.out.println("after invoke()"); return null; } } ----------- 輸出結果: subject.getClass().getName():$Proxy0 before invoke() the method is :public abstract void Subject.before() this is method of before(). after invoke()
-
-
springIOC的實現原理 :三級快取+提前曝光+java的物件引用原理
- Spring迴圈依賴的理論依據其實是Java基於引用傳遞,當我們獲取到物件的引用時,物件的field或者或屬性是可以延後設定的。Spring單例物件的初始化其實可以分為三步:
- createBeanInstance, 例項化,實際上就是呼叫對應的構造方法構造物件,此時只是呼叫了構造方法,spring xml中指定的property並沒有進行populate。
- populateBean,填充屬性,這步對spring xml中指定的property進行populate
- initializeBean,呼叫spring xml中指定的init方法,或者AfterPropertiesSet方法
會發生迴圈依賴的步驟集中在第一步和第二步。
- spring在解決迴圈依賴中使用了三級快取
- 單例物件的cache:
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256); - 單例物件工廠的cache:
private final Map <String, ObjectFactory?> singletonFactories = new HashMap<String, ObjectFactory<?>>(16); - 提前曝光的單例物件的cache:
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
- 單例物件的cache:
- singleton初始化過程圖片:
- 顯然:兩個迴圈依賴都是通過構造器函式進行依賴的,則spring無法解決此種依賴。因為提前暴露出來的原始bean需要執行完例項構造器函式。
- Spring迴圈依賴的理論依據其實是Java基於引用傳遞,當我們獲取到物件的引用時,物件的field或者或屬性是可以延後設定的。Spring單例物件的初始化其實可以分為三步:
-
interface 和 abstractclass 的區分和適用場景
- 區別
- 變數型別:抽象類中的成員變數可以是各種型別的,但是介面中的只能是public static final型別的。
- 單根繼承:一個類只能繼承一個抽象類,但可以實現多個介面;
- 實現關鍵字:介面需要實現implements;而抽象類需要繼承extends;
- 強調內容:interface強調特定功能的實現;abstract class強調所屬關係;
- 的
- 使用場景
- 介面:
- 如果想實現多繼承,則使用介面;
- 用於定義mix-in型別,表示類具有某特定效能,如Serializable介面,沒有任何方法,只是用於表示實現該介面的類可以序列化,當然序列話方法要自己寫程式碼去實現。
- 抽象類:
- 如果基本功能不斷改變,則用抽象類。因為不斷改變功能時,用介面的話,就要改變所有實現了該介面的類。
- 某些場合下,只靠純粹的介面不能滿足類與類之間的協調,還必需類中表示狀態的變數來區別不同的關係。abstract的中介作用可以很好地滿足這一點.
- 介面:
- 區別
京東面試總結
一面
- 演算法題
- 實習專案
- 資料庫為什麼要使用執行緒池
- 原因:各種池其實原因都一樣
- 實現方式:
預先建立一定數量的執行緒,當有請求達到時,執行緒池分配一個執行緒提供服務,請求結束後,該執行緒又去服務其他請求。 - 優勢
1)避免了執行緒和記憶體物件的頻繁建立和釋放
2)降低了服務端的併發度
3)減少了上下文切換和資源的競爭,提高資源利用效率
- 實現方式:
- MySQL的執行緒池改進過程
1)One-Connection-Per-Thread
每一個數據庫連線,Mysql-Server都會建立一個獨立的執行緒服務,請求結束後,銷燬執行緒。
2)基於1)的thread-cache,將執行緒快取起來,以供下次使用,避免頻繁建立和釋放的問題,但是無法解決高連線數的問題。1)的方式隨著連線數暴增,導致需要建立同樣多的服務執行緒,高併發執行緒意味著高的記憶體消耗,更多的上下文切換(cpu cache命中率降低)以及更多的資源競爭,導致服務出現抖動。
3)Thread-Pool實現方式
1.執行緒處理的最小單位是statement(語句),一個執行緒可以處理多個連線的請求。
2.在保證充分利用硬體資源情況下(合理設定執行緒池大小),可以避免瞬間連線數暴增導致的伺服器抖動。 - 執行緒池常配置的屬性
1)user和password
2)initialPoolSize、minPoolSize、maxPoolSize
3)acquireIncrement :聲明當連線池中連線耗盡時再一次新生成多少個連線,預設為3個
4)maxIdleTime :超過多長時間連線自動銷燬,預設為0,即永遠不會自動銷燬
5)maxStatements :JDBC的標準引數,用以控制資料來源內載入的PreparedStatements數量
ps:但由於預快取的statements屬於單個connection而不是整個連線池。所以設定這個引數需要考慮到多方面的因素。
如果maxStatements與maxStatementsPerConnection均為0,則快取被關閉
6)idleConnectionTestPeriod :每xx秒檢查所有連線池中的空閒連線 - 執行緒池和連線池
- 執行緒池
- 執行緒池實現在server端,通過建立一定數量的執行緒服務DB請求
- 執行緒池服務的最小單位是語句,即一個執行緒可以對應多個活躍的連線
- 可以將server端的服務執行緒數控制在一定的範圍,減少了系統資源的競爭和執行緒上下文切換帶來的消耗,同時也避免出現高連線數導致的高併發問題
- 連線池
1)連線池通常實現在Client端,是指應用(客戶端)建立預先建立一定的連線,利用這些連線服務於客戶端所有的DB請求
2)如果某一個時刻,空閒的連線數小於DB的請求數,則需要將請求排隊,等待空閒連線處理。通過連線池可以複用連線,避免連線的頻繁建立和釋放,從而減少請求的平均響應時間,並且在請求繁忙時,通過請求排隊,可以緩衝應用對DB的衝擊
PS:連線池和執行緒池相輔相成,通過連線池可以減少連線的建立和釋放,提高請求的平均響應時間,並能很好地控制一個應用的DB連線數,但無法控制整個應用叢集的連線數規模,從而導致高連線數,通過執行緒池則可以很好地應對高連線數,保證server端能提供穩定的服務。
- 原因:各種池其實原因都一樣
- 畢設+神經網路
- java實現檔案的匯入匯出用到的類有哪些?
只記得InputStream,OutputStream. - java如何實現將一個Date按照某種日期的格式顯示?
SimpleDateFormat可以實現
二面
-
講實習專案
-
Spring的功能有哪些
自己從spring組成,和各個模組及常用模組的功能都講了一遍 -
Spring事務的隔離級別
- Spring事務的隔離級別和MySQL的事務隔離級別的關係
- Spring使用的事務隔離級別必須是MySQL能支援的,故如果Spring設定的隔離級別MySQL不支援則此Spring的設定無效。
- 如果Spring和MySQL設定了不同的隔離級別,那麼以Spring會在事務開始時,根據你程式中設定的隔離級別,調整資料庫隔離級別與你的設定一致。
- Spring事務的隔離級別和MySQL的事務隔離級別的關係
-
Mybatis的二級快取
- 一級快取
- 生命週期:基於sqlSession
- 二級快取
- 生命週期:基於application
- 作用範圍:按照每個namepace一個快取來存貯和維護,同一個namespace放到一個快取物件中。
- cache使用的注意事項
- 只能在【只有單表操作】的表上使用快取
不只是要保證這個表在整個系統中只有單表操作,而且和該表有關的全部操作必須全部在一個namespace下。 - 在可以保證查詢遠遠大於insert,update,delete操作的情況下使用快取
- 只能在【只有單表操作】的表上使用快取
- 避免使用二級快取
多表操作不管多表操作寫到哪個namespace下,都會存在某個表不在這個namespace下的情況,這會導致查詢使用快取的時候結果就是錯的。
- 一級快取
-
MySQL的索引建立原則
自己先說了索引型別,不同引擎支援的索引型別,以及索引的使用規則 -
java中的鎖怎麼實現的
自己講了volatile,synchronized的實現原理,以及從jdk1.6開始synchronized對鎖的處理:偏向鎖,輕量級鎖,重量級鎖。以及鎖只能升級不能降級。 -
java反射都用過什麼?