1. 程式人生 > >Web後臺開發之CVTE的面試經歷

Web後臺開發之CVTE的面試經歷

前幾天(180922)收到CVTE面試簡訊,要我今天(180925)去長沙美爵酒店7樓面試。今天下午四點的面試我三點多就到了,前臺接待員挺好的,一見到有人去,趕緊上來做引導。大概四點二十的時候到我了。下面我說一下面試官問的問題吧(自我介紹就不談了)。

一 為什麼要用Netty框架?

詳情請參考https://blog.csdn.net/weixin_39453325/article/details/82995356這篇部落格

二 Netty框架的實現,大概問的是Netty的一個框架流程?

詳情請參考https://blog.csdn.net/weixin_39453325/article/details/82995356

這篇部落格

三 Tcp的拆包與粘包問題?

詳情請參考https://blog.csdn.net/weixin_39453325/article/details/84181805這篇部落格

四 Struts2與SpringMVC的不同點?

1. Struts2 和 SpringMVC 的框架載入機制不同。

Struts2 入口是 Filter ,而 SpringMVC 入口是 Servlet (DispatcherServlet)。Filter在容器啟動之後即初始化,服務停止後銷燬,但晚於 Servlet 。Servlet 是在呼叫時初始化的,先於 Filter 呼叫,服務停止後銷燬。

2. Struts2 和 SpringMVC 的框架攔截機制不同。

2.1 Struts2

  •  Struts2 框架是類級別的攔截,每次有請求就會建立一個 Action ,和 Spring 整合時 Struts2 的 ActionBean 注入作用域是原型模式 prototype(不然會出現執行緒併發問題),然後呼叫 setter ,getter 把 request 引數注入到物件的屬性中去(屬性驅動和模型驅動)。
  •  Struts2 中,一個 Action 對應一個 request ,response 上下文,在接收引數時,可以通過屬性接收,說明屬性引數是可以讓多個方法共享的,採用值棧儲存請求以及相應資料,OGNL 存取資料。
  •  Struts2 中 Action 的一個方法可以對應一個 url ,而其類屬性卻被所有方法共享,這也就無法用註解或其他方法標識其屬性方法了。
  •  Intercepter 的實現機制,Struts2 有自己的攔截機制 Interceptor。

2.2 SpringMVC

  •  SpringMVC 是方法級別的攔截,一個方法對應一個 Request 上下文,所以方法基本上是獨立的,獨享 request ,response 資料。而每個方法同時又和一個 url 對應,引數的傳遞是根據引數上的註解直接注入到方法中的,是方法獨享的,處理結果通過 ModelAndView 物件儲存(Model 儲存資料,View 儲存返回的頁面),最後通過 request 返回到頁面。
  •  在 Sping 整合時,SpringMVC 的 Controller Bean 預設時單例模式(Singleton),所以在預設情況下對所有的請求,只會建立一個 Controller ,因為沒有共享的屬性,所以是執行緒安全的,如果要改變預設的作用域,需要新增 @Scope 註解修改。
  • Intercepter 的實現機制,SpringMVC 使用獨立的 AOP 方式,導致配置檔案比 Struts2 的配置檔案多。

3. 框架整合方面。

SpringMVC 和 Spring 是無縫的(無需資料格式的轉換,直接訪問來自資料來源資料格式)。專案的管理和安全上比 Struts2 高。

4. Ajax 互動。

SpringMVC 處理 Ajax 的請求十分方便,只需一個註解 @ResponseBody,然後直接返回響應文字;

Struts2 攔截器集成了 Ajax ,在 Action 中處理時一般需要安裝外掛或者自己寫程式碼整合進去,使用起來相對不方便。

五 為什麼要用Spring?

我回答了用來整合Struts2和Hibernate,還講了IOC與AOP。

Spring 是一個開源框架,為了解決企業應用開發的複雜性而建立的,是一個輕量級的 IOC (控制反轉) 和 AOP (面向切面)的容器框架。所有物件的建立和依賴關係的維護以及生命週期可以由 Spring 容器統一管理(有效降低耦合度);只需要通過配置即可完成對事務的管理,而無需手動程式設計(宣告式事務的支援);提供對 Junit 的支援,可以通過註解方便的測試程式(易於單元測試)。

  • IOC (控制反轉):控制權的轉移,應用程式本身不在負責依賴物件的建立與維護,而是由外部容器負責建立和維護。誰控制誰:IOC 容器控制了物件;控制了什麼:控制了外部資源獲取(不只是物件的建立,還有檔案之類的);為何是反轉:因為容器幫我們查詢注入依賴的物件,物件只是被動的接受依賴的物件,所以是反轉;哪些方面反轉:依賴物件的獲取被反轉了。
  • DI (依賴注入):是 IOC 的一種實現方式,其目的是建立物件並且組裝物件之間的關係。
  • AOP (面向切面):在程式開發中主要用來解決一些系統層面上的問題,比如事件管理,安全檢查,快取,物件池管理,日誌管理等,利用一種稱為“橫切”的技術,把軟體系統分為兩個部分“核心關注點”和“橫切關注點”,AOP的作用載與分離系統中的各種關注點,將核心關注點與橫切關注點分離開。其底層實現是使用了動態代理模式,大家可以參考這篇部落格https://blog.csdn.net/weixin_39453325/article/details/84309742

六 資料庫有哪些引擎?

詳情請參考https://blog.csdn.net/weixin_39453325/article/details/83142151這篇部落格

七 資料庫的優化有哪一些,索引是怎麼使用的?

詳情請參考https://blog.csdn.net/weixin_39453325/article/details/83472961這篇部落格

八 都用過哪些集合框架?

  • Collection 介面:為 List 和 Set 聲明瞭集合的基本操作方法,包括獲得迭代器、集合是否包含指定元素、增加或刪除集合元素、集合運算、獲得子集等。

 

  • Iterator 介面:Java 採用迭代方式實現對集合中的元素進行遍歷和刪除操作。迭代指從集合中獲得元素的後繼元素。迭代功能由 Iterable 可迭代介面和 Iterator、ListIterator 迭代器介面實現。

  • Map 介面:Map 介面宣告從關鍵字到值的對映。實現 Map 介面的類主要有:使用散列表的 HashMap 雜湊對映類和使用平衡二叉樹的 TreeMap 樹對映類,TreeMap 類實現按關鍵字排序的 SortedMap 有序對映介面。

九 什麼情況下用ArrayList,什麼情況下用LinkedList?

ArrayList 陣列列表類使用一維陣列實現 List,具有隨機存取特徵(存取元素的時間複雜度是O(1)),插入、刪除元素時需要移動其他元素,當元素很多時,插入和刪除操作的平均時間效率較低。

LinkedList 連結串列類使用迴圈雙鏈表實現 List ,插入、刪除元素時不需要移動其他元素,但不具有隨機存取的特性。

  • 當操作是在資料後面新增資料而不是在前面或中間,並且需要隨機訪問其中的元素時,使用 ArrayList 比較好。
  • 當操作是在資料的前面或中間新增刪除資料,並且按照順序訪問元素時,應該使用 LinkedList 。

十 HashMap的底層實現。我回答了get與put方法以及擴容,為什麼HashMap的大小是2^n。

詳情請參考 https://blog.csdn.net/weixin_39453325/article/details/82953747 這篇部落格。

十一 如何實現執行緒安全的HashMap?提示了我ConcurrentHashMap 。

HashMap 是執行緒不安全的

1. 假設正好存在兩個 put 操作的 key 發生碰撞(hash 值相同),那麼根據 HahMap 的實現,這兩個 key 會新增到陣列的同一個位置,這樣會發生其中一個 key 的 value 值被覆蓋的情況。

2. 如果多個執行緒同時檢測到到元素的個數超過(陣列大小 * 負載因子),這樣會發生多個執行緒同時對 Node 陣列進行擴容,都在重新計算元素位置以及複製資料,但是最終只有一個執行緒擴容後的陣列會賦給 table,也就是說其他執行緒都會丟失,並且各自執行緒的 put 資料也會丟失。

那麼如何實現執行緒安全的 HashMap 呢?

1 HashTable:使用 synchronized 來保證執行緒安全,當一個執行緒訪問 HashTable 的同步方法時,其他的執行緒也要訪問的話會被阻塞。

public synchronized V get(Object key) {
       // 省略實現
    }
public synchronized V put(K key, V value) {
    // 省略實現
    }

2 Collections.synchronizedMap:從下面的原始碼中可以看出呼叫 synchronizedMap() 方法後會返回一個 SynchronizedMap 類的物件,而在 SynchronizedMap 類中使用了 synchronized 同步關鍵字來保證對 Map 的操作都是執行緒安全的(一個時間內只能有一個執行緒得到執行,其他執行緒必須等待當前的執行緒執行完之後才能執行)。

  SynchronizedMap(Map<K,V> m) {
            this.m = Objects.requireNonNull(m);
            mutex = this;
        }

  SynchronizedMap(Map<K,V> m, Object mutex) {
            this.m = m;
            this.mutex = mutex;
        } 
public V get(Object key) {
            synchronized (mutex) {return m.get(key);}
        }

 public V put(K key, V value) {
            synchronized (mutex) {return m.put(key, value);}
        }

3 ConcurrentHashMap: ConcurrentHashMap 與 HashMap 相比,最關鍵的一個概念是:segment,segment 其實就是一個 HashMap 。segment 也包含一個 HashEntry 陣列,陣列中的每一個 HashEntry 既是一個鍵值對,也是一個連結串列的頭節點。

每個 segment 的讀寫都是高度自治的,segment 之間互不影響,這稱之為“鎖分段技術”,在併發情況下的 ConcurrentHashMap 

情景一:不同的 segment 併發寫入

情景二:同一 segment 併發寫入

 情景三:同一 segment 一寫一讀

get 操作:

  • 為輸入的 key 做 hash 運算,得到 hash 值
  • 通過 hash 值,定位到對應的 segment 物件
  • 再次通過 hash 值,定位到 segment 中陣列的具體位置

put 操作: 

  • 為輸入的 key 做 hash 運算,得到 hash 值
  • 通過 hash 值,定位到對應的 segment 物件
  • 獲得可重入鎖
  • 再次通過 hash 值,定位到 segment 當中陣列的具體位置
  • 插入或覆蓋 hashEntry 物件
  • 釋放鎖

十二 Java中都有哪些方式來保證執行緒安全?

1 同步互斥(悲觀併發)

synchronized關鍵字: 是一個重量級的鎖,因為執行緒的切換需要作業系統的呼叫。在編譯前後會在同步塊的前後分別形成 monitorentry 和 monitorexit 這兩個位元組碼指令,它們兩都需要一個 reference 型別的引數來指明要鎖定和解鎖的物件。在執行 monitorenter 指令時,首先會嘗試獲取物件的鎖,如果該物件沒有被鎖定,或者當前執行緒已經擁有了那個物件的鎖,則把鎖的計數器加一。如獲取物件失敗,當前執行緒就要阻塞等待,直到物件鎖被另一個執行緒釋放。

ReentrantLock:支援重入鎖,還支援等待中斷,支援公平鎖(指按照申請的時間順序進行分配,synchronized 鎖是非公平的),還支援繫結多個條件,可以同時繫結多個 condition 物件。

2 非阻塞同步(基於衝突檢測的樂觀併發)

互斥同步又叫阻塞同步,是一種悲觀的併發策略,總是認為只要不做正確的同步措施,就肯定會出現問題,即無論是否出現共享資料的競爭,都會給資源加鎖,使用者態核心態轉換,維護鎖計數器,檢查被阻塞的執行緒是否需要喚醒操作。

非阻塞同步是一種樂觀的併發處理策略,是一種基於衝突檢測的樂觀併發處理,如果共享資料沒有出現其他競爭,則操作成功,但當競爭出現時,會不斷重試,直到成功。CAS 是實現非阻塞同步的計算機指令,它有三個運算元,記憶體位置,舊的預期值,新值,在執行 CAS 操作時,當且僅當記憶體地址的值符合舊的預期值的時候,才會用新值來更新記憶體地址的值,否則不執行更新。

3 無同步方案

執行緒本地儲存:將共享資料的可見範圍限制在一個執行緒中,這樣就無需同步也能保證執行緒之間不會出現資料爭用問題,經常使用的是 ThreadLocal 類。當多個執行緒訪問同一個變數的時候,ThreadLocal 會為每一個執行緒建立一個該變數的副本,每個執行緒可以訪問自己的副本變數。

十三 java中的CAS?

詳情請參考 https://mp.csdn.net/postedit/82964401 這篇部落格。

十四 程式設計題:給定一個數組,返回陣列中關鍵字重複的次數,複雜度O(n)。

我當時自己寫的,沒想到方法,寫了一個O(n^2)的,後面面試官提示HashMap我恍然大悟,這不就是我在LeetCode上的第一道演算法題中的一個子問題嗎。有點智障了...

	public static int total(int a[]) {
		int max=0;
		for(int i=0;i<a.length;i++) {
			int key=a[i];
			int j=0;
			int num=0;
			while(j<a.length) {
				if(key==a[j])
					num++;
				j++;
			}
			if(num>max) {
				max=num;
			}
		}
		return max;
	}

正確的是:

     private static int total(int[] a) {
		HashMap<Integer,Integer> hm=new HashMap<Integer,Integer>();
		int max=1;
		for(int i=0;i<a.length;i++) {
			if(!hm.containsKey(a[i])){
				hm.put(a[i], 1);
			}
			else {
				int num=hm.get(a[i]);
				hm.put(a[i],num+1);
				if(max<num+1) {
					max=num+1;
				}
			}
		}
		return max;
	}