1. 程式人生 > >JAVA 中級面試題 (附答案)

JAVA 中級面試題 (附答案)

問題大多取自點選開啟連結 在網上找了一些答案,也添加了一些幾乎是必問的題

一、     基礎知識:

1)   HashMap,LinkedHashMap,TreeMap的區別

1.        HashMap,LinkedHashMap,TreeMap都屬於Map。

2.        Map的主要作用是用於儲存鍵(key)值(value)對,根據鍵得到值,因此不允許鍵重複,但允許值重複。

3.        HashMap是一個最常用的Map,它根據鍵的HashCode值儲存資料,根據鍵可以直接獲取它的值,具有最快的訪問速度。HashMap最多隻允許一條記錄的鍵為Null;允許多條記錄的值為Null;HashMap不支援執行緒的同步,即任一時刻可以有多個執行緒同時寫HashMap;可能會導致資料的不一致。如果需要同步,可以用Collections的synchronizedMap方法使HashMap具有同步的能力。

4.        LinkedHashMap也是一個HashMap,但是內部維持了一個雙向連結串列,可以保持順序;

5.        TreeMap不僅可以保持順序,而且可以用於排序;

2)   ArrayList和LinkedList的區別

1.        ArrayList是實現了基於動態陣列的資料結構,LinkedList是基於連結串列的資料結構。

2.        對於隨機訪問get和set,ArrayList優於LinkedList,因為LinkedList要移動指標。

3.        對於新增和刪除操作add和remove,LinkedList比較佔優勢,因為ArrayList要移動資料。

3)   概述一下SpringMVC的工作原理

1.        客戶端發出一個HTTP請求給web伺服器,web伺服器對HTTP請求進行解析,如果匹配DispatcherServlet的請求對映路徑(在web.xml中指定,或者使用註解),web容器將請求轉交給dispatcherServelet。

2.        DispacherServelet接受到這個請求之後根據請求的資訊(包括URL、Http方法、請求報文頭和請求引數Cookie等)以及HandleMapping的配置找到處理請求的處理器Haddler。

3.        DispatcherServlet根據HandlerMapping找到對應的Handler,將處理權交給Handler(Handler將具體的處理進行封裝),再由具體的HandlerAdapter對Handler進行具體的呼叫。

4.        Handler對資料處理完成以後將返回一個ModelAndView()物件給DispatcherServlet。

5.        Handler返回的ModelAndView()只是一個邏輯檢視並不是一個正式的檢視,DispatcherSevlet通過ViewResolver將邏輯檢視轉化為真正的檢視View。

6.        Dispatcher通過model解析出ModelAndView()中的引數進行解析最終展現出完整的view並返回給客戶端。

4)   集合類:List和Set比較

List是有序(儲存元素的順序和取出元素的順序一致)的,Set是無序的;List集合中的元素都有索引,可以儲存重複資料;Set不能儲存重複資料。簡單來說當存入的的物件有重複時,用List,沒有重複元素時,用Set。

5)   HashMap的底層實現;

首先有一個每個元素都是連結串列(可能表述不準確)的陣列,當新增一個元素(key-value)時,就首先計算元素key的hash值,以此確定插入陣列中的位置,但是可能存在同一hash值的元素已經被放在陣列同一位置了,這時就新增到同一hash值的元素的後面,他們在陣列的同一位置,但是形成了連結串列,同一各連結串列上的Hash值是相同的,所以說陣列存放的是連結串列。而當連結串列長度太長時,連結串列就轉換為紅黑樹,這樣大大提高了查詢的效率。

6)   如何實現HashMap順序儲存

LinkedHashMap裡面有一個模擬的“雙向迴圈連結串列”,用來儲存entry的插入順序,我也可以採用這種方法來在插入的時候儲存key和value的有序。這裡暫定名為OrderedHashMap,主要程式碼是從LinkedHashMap抄過來的,它也維護著兩個模擬“雙向迴圈連結串列”:keyHeader和valueHeader,保持key或value由小到大的順序。當有個元素put進來後,除把它存在雜湊桶中外,還要在keyHeader按key的大小插入,也要在valueHeader上按value的順序插入。想要輸出的話,可以從這兩個“頭指標”向前或向後來迭代得到key或value有序的entry。(可以實現key和value,正序逆序的輸出,只對數值型比較,如果不是數值型的話,如HashMap般正常處理)

7)   HashMap、HashTable和ConcurrentHashMap的區別

HashMap和HashTable的區別一種比較簡單的回答是:

1.                  HashMap是非執行緒安全的,HashTable是執行緒安全的,內部的方法基本都經過synchronized修飾。

2.                  因為同步、雜湊效能等原因,效能肯定是HashMap更佳。

3.                  HashMap允許有null值的存在,而在HashTable中put進的鍵值只要有一個null,直接丟擲NullPointerException

HashMap和ConCurrentHashMap的對比:

先對ConcurrentHashMap進行一些介紹吧,它是執行緒安全的HashMap的實現。

HashTable裡使用的是synchronized關鍵字,這其實是對物件加鎖,鎖住的都是物件整體,當Hashtable的大小增加到一定的時候,效能會急劇下降,因為迭代時需要被鎖定很長的時間。

1.        ConcurrentHashMap對整個桶陣列進行了分割分段(Segment),然後在每一個分段上都用lock鎖進行保護,相對於HashTable的syn關鍵字鎖的粒度更精細了一些,併發效能更好,而HashMap沒有鎖機制,不是執行緒安全的。

2.        HashMap的鍵值對允許有null,但是ConCurrentHashMap都不允許。

8)   String,StringBuffer和StringBuilder的區別

String 字串常量

StringBuffer 字串變數(執行緒安全)

StringBuilder 字串變數(非執行緒安全)

9)   wait和sleep的區別

對於sleep()方法,我們首先要知道該方法是屬於Thread類中的。而wait()方法,則是屬於Object類中的。

sleep()方法導致了程式暫停執行指定的時間,讓出cpu該其他執行緒,但是他的監控狀態依然保持者,當指定的時間到了又會自動恢復執行狀態。

在呼叫sleep()方法的過程中,執行緒不會釋放物件鎖。

而當呼叫wait()方法的時候,執行緒會放棄物件鎖,進入等待此物件的等待鎖定池,只有針對此物件呼叫notify()方法後本執行緒才進入物件鎖定池準備獲取物件鎖進入執行狀態。

10)JVM的記憶體結構,JVM的演算法

JVM記憶體結構主要有三大塊:堆記憶體、方法區和棧。堆記憶體是JVM中最大的一塊由年輕代和老年代組成,而年輕代記憶體又被分成三部分,Eden空間、From Survivor空間、To Survivor空間,預設情況下年輕代按照8:1:1的比例來分配;

方法區儲存類資訊、常量、靜態變數等資料,是執行緒共享的區域,為與Java堆區分,方法區還有一個別名Non-Heap(非堆);棧又分為java虛擬機器棧和本地方法棧主要用於方法的執行。

11)用過哪些設計模式,手寫一個(除單例);

單例模式

實現方式:

a)將被實現的類的構造方法設計成private的。

b)新增此類引用的靜態成員變數,併為其例項化。

c)在被實現的類中提供公共的CreateInstance函式,返回例項化的此類,就是b中的靜態成員變數。

策略模式

實現方式:

a)提供公共介面或抽象類,定義需要使用的策略方法。(策略抽象類)

b)多個實現的策略抽象類的實現類。(策略實現類)

c)環境類,對多個實現類的封裝,提供介面型別的成員量,可以在客戶端中切換。

d)客戶端 呼叫環境類 進行不同策略的切換。

注:Jdk中的TreeSet TreeMap的排序功能就是使用了策略模式。

觀察者模式

觀察者模式是物件的行為模式,又叫釋出-訂閱(Publish/Subscribe)模式、模型-檢視(Model/View)模式、源-監聽器(Source/Listener)模式或從屬者(Dependents)模式。

實現方式:

a)角色抽象類(提供對觀察者的新增,刪除和通知功能)。

b)角色具體類,實現a,維護一個c的集合(對角色抽象類的實現)。

c)觀察者抽象類(被角色通知後實現的方法)。

d)觀察者實現類,實現c(多個)。

注:JDK提供了對觀察者模式的支援,使用Observable類和Observer介面

單例模式程式碼

  1. public class Singleton {  
  2.     private Singleton(){}  
  3.     private static class SingletonBuild{  
  4.         private static Singleton value = new Singleton();  
  5.     }  
  6.     public Singleton getInstance(){  return  SingletonBuild.value ;}  
  7. }  

工廠模式程式碼

  1. interface food{}  
  2. class A implements food{}  
  3. class B implements food{}  
  4. class C implements food{}  
  5. public class StaticFactory {  
  6.     private StaticFactory(){}  
  7.     public static food getA(){  return new A(); }  
  8.     public static food getB(){  return new B(); }  
  9.     public static food getC(){  return new C(); }  
  10. }  
  11. class Client{  
  12.     //客戶端程式碼只需要將相應的引數傳入即可得到物件  
  13.     //使用者不需要了解工廠類內部的邏輯。  
  14.     public void get(String name){  
  15.         food x = null ;  
  16.         if ( name.equals("A")) {  
  17.             x = StaticFactory.getA();  
  18.         }else if ( name.equals("B")){  
  19.             x = StaticFactory.getB();  
  20.         }else {  
  21.             x = StaticFactory.getC();  
  22.         }  
  23.     }  
  24. }  

12)SpringMVC的核心是什麼,請求的流程是怎麼處理的;

SpringMVC的核心是什麼:

1、  IoC:控制反轉

概念:控制權由物件本身轉向容器;由容器根據配置檔案去建立例項並建立各個例項之間的依賴關係

核心:bean工廠;在Spring中,bean工廠建立的各個例項稱作bean

2、  AOP(Aspect-Oriented Programming): 面向切面程式設計

概念:簡單來說AOP就是,每個人各司其職,靈活組合,達到一種可配置的、可插拔的程式結構。

從Spring的角度看,AOP最大的用途就在於提供了事務管理的能力。事務管理就是一個關注點,你的正事就是去訪問資料庫,而你不想管事務(太煩),所以,Spring在你訪問資料庫之前,自動幫你開啟事務,當你訪問資料庫結束之後,自動幫你提交/回滾事務

請求的流程是怎麼處理的:

SpringMVC框架是一個基於請求驅動的Web框架,並且使用了‘前端控制器’模型來進行設計,再根據‘請求對映規則’分發給相應的頁面控制器進行處理。

1.        首先使用者傳送請求—— >DispatcherServlet , 分發器收到請求後自己不進行處理,而是委託給其他的解析器進行處理,作為統一訪問點,進行全域性的流程控制;

2.        DispatcherServlet ——>HandlerMapping , HandlerMapping 將會把請求對映為 HandlerExecutionChain 物件(包含一個 Handler 處理器(Controller)物件、多個HandlerInterceptor 攔截器)物件,通過這種策略模式,很容易新增新的對映策略;

3.        DispatcherServlet ——>HandlerAdapter , HandlerAdapter 將會把處理器包裝為介面卡,從而支援多種型別的處理器,即介面卡設計模式的應用,從而很容易支援很多型別的處理器;

4.        HandlerAdapter —— > 處理器功能處理方法的呼叫,HandlerAdapter 將會根據適配的結果呼叫真正的處理器的功能處理方法,完成功能處理(在呼叫處理器前會先執行spring的前置攔截器preHandle);並返回一個 ModelAndView 物件(包含模型資料、邏輯檢視名),返回檢視後會執行spring的後置攔截器postHandle;

5.        ModelAndView 的邏輯檢視名—— >ViewResolver , ViewResolver 將把邏輯檢視名解析為具體的 View,通過這種策略模式,很容易更換其他檢視技術;

6.        View —— > 渲染 ,View 會根據傳進來的 Model模型資料進行渲染,此處的 Model 實際是一個 Map 資料結構,因此很容易支援其他檢視技術(這步處理完後執行spring的完成後攔截器);

7.        返回控制權給 DispatcherServlet ,由 DispatcherServlet 返回響應給使用者,到此一個流程結束。

13)spring裡面的aop的原理是什麼

Spring實現AOP:JDK動態代理和CGLIB代理 JDK動態代理:其代理物件必須是某個介面的實現,它是通過在執行期間建立一個介面的實現類來完成對目標物件的代理;其核心的兩個類是InvocationHandler和Proxy。 CGLIB代理:實現原理類似於JDK動態代理,只是它在執行期間生成的代理物件是針對目標類擴充套件的子類。CGLIB是高效的程式碼生成包,底層是依靠ASM(開源的java位元組碼編輯類庫)操作位元組碼實現的,效能比JDK強;需要引入包asm.jar和cglib.jar。     使用AspectJ注入式切面和@AspectJ註解驅動的切面實際上底層也是通過動態代理實現的。

14)mybatis如何處理結果集

把結果集拿到以後,從配置檔案裡獲取對應的bean,類反射

15)java的多型表現在哪裡;

多型:就是指相同的事物的不同狀態,比如:水。水可以有三種狀態:

氣體、液體和固體。那麼在JAVA中的多型也可以理解成這個意思,就是:

將父物件設定成為和一個或多個它的子物件相等的技術,

比如Parent=Child;

多型性使得能夠利用同一類(父類)引用不同類的物件,

以及根據所引用物件的不同,以不同的方式執行相同的操作。

多型實現包括兩種方式:過載和重寫

例如:Animal a = new Tiger(); 這是一個老話題了,呵呵……

父類引用指向子類物件,Animal類中包含一個eat()方法,而Tiger類繼承自

Animal類,如果子類重寫了父類的eat()方法,則呼叫的時候,就可以按照子類

的形式呼叫,本質上是父類的方法,但是子類重寫之後,就成了另一種方式,

這就是多型。

16)說說http,https協議;

HTTP 和 HTTPS 的相同點

大多數情況下,HTTP 和 HTTPS 是相同的,因為都是採用同一個基礎的協議,作為 HTTP 或 HTTPS 客戶端——瀏覽器,設立一個連線到 Web 伺服器指定的埠。當伺服器接收到請求,它會返回一個狀態碼以及訊息,這個迴應可能是請求資訊、或者指示某個錯誤傳送的錯誤資訊。系統使用統一資源定位器 URI 模式,因此資源可以被唯一指定。而 HTTPS 和 HTTP 唯一不同的只是一個協議頭(https)的說明,其他都是一樣的。

HTTP 和 HTTPS 的不同之處

1.HTTP 的 URL 以 http:// 開頭,而 HTTPS 的 URL 以 https:// 開頭

2.HTTP 是不安全的,而 HTTPS 是安全的

3.HTTP 標準埠是 80 ,而 HTTPS 的標準埠是 443

4.在 OSI 網路模型中,HTTP 工作於應用層,而HTTPS 工作在傳輸層

5.HTTP 無需加密,而 HTTPS 對傳輸的資料進行加密

6.HTTP 無需證書,而 HTTPS 需要認證證書

17)tcp/ip協議簇是什麼意思

TCP/IP是一個網路通訊協議群,它包含了很多通訊協議。這些協議制定了網路裝置、計算機連入網路以及資料是如何在它們之間進行傳輸的標準。TCP協議又叫傳輸控制協議,作用於傳輸層。IP協議又叫網路間互聯協議,工作在網路層。它們都是TCP/IP協議簇中非常重要的協議。

18)osi五層網路協議;

OSI七層模型

OSI中的層功能 TCP/IP協議族

應用層檔案傳輸,電子郵件,檔案服務,虛擬終端 TFTP,HTTP,SNMP,FTP,SMTP,DNS,Telnet

表示層資料格式化,程式碼轉換,資料加密沒有協議

會話層解除或建立與別的接點的聯絡沒有協議

傳輸層提供端對端的介面 TCP,UDP

網路層為資料包選擇路由 IP,ICMP,RIP,OSPF,BGP,IGMP

資料鏈路層傳輸有地址的幀以及錯誤檢測功能 SLIP,CSLIP,PPP,ARP,RARP,MTU

物理層以二進位制資料形式在物理媒體上傳輸資料 ISO2110,IEEE802,IEEE802.2

19)cookie和session的區別,分散式環境怎麼儲存使用者狀態;

Cookie

通俗講,Cookie是訪問某些網站以後在本地儲存的一些網站相關的資訊,下次再訪問的時候減少一些步驟。另外一個更準確的說法是:Cookies是伺服器在本地機器上儲存的小段文字並隨每一個請求傳送至同一個伺服器,是一種在客戶端保持狀態的方案。

Session

Session是存在伺服器的一種用來存放使用者資料的類HashTable結構。

區別:

最明顯的不同是一個在客戶端一個在服務端。因為Cookie存在客戶端所以使用者可以看見,所以也可以編輯偽造,不是十分安全。

Session過多的時候會消耗伺服器資源,所以大型網站會有專門的Session伺服器,而Cookie存在客戶端所以沒什麼問題。

域的支援範圍不一樣,比方說a.com的Cookie在a.com下都能用,而www.a.com的Session在api.a.com下都不能用,解決這個問題的辦法是JSONP或者跨域資源共享。

分散式環境怎麼儲存使用者狀態:

第一種:粘性session  (Nginx)

原理:粘性Session是指將使用者鎖定到某一個伺服器上,比如上面說的例子,使用者第一次請求時,負載均衡器將使用者的請求轉發到了A伺服器上,如果負載均衡器設定了粘性Session的話,那麼使用者以後的每次請求都會轉發到A伺服器上,相當於把使用者和A伺服器粘到了一塊,這就是粘性Session機制。

優點:簡單,不需要對session做任何處理。

缺點:缺乏容錯性,如果當前訪問的伺服器發生故障,使用者被轉移到第二個伺服器上時,他的session資訊都將失效。

適用場景:發生故障對客戶產生的影響較小;伺服器發生故障是低概率事件。

實現方式:以Nginx為例,在upstream模組配置ip_hash屬性即可實現粘性Session。

upstream mycluster{

#這裡新增的是上面啟動好的兩臺Tomcat伺服器

ip_hash;#粘性Session

server192.168.22.229:8080 weight=1;

server192.168.22.230:8080 weight=1;

}

第二種:session持久化到資料庫

原理:就不用多說了吧,拿出一個數據庫,專門用來儲存session資訊。保證session的持久化。

優點:伺服器出現問題,session不會丟失

缺點:如果網站的訪問量很大,把session儲存到資料庫中,會對資料庫造成很大壓力,還需要增加額外的開銷維護資料庫。

20)git,svn區別

相同:

能記錄檔案的所有更改記錄。這樣是為了大量更改後,但是最後覺得還是原來的版本程式碼好,可以有記錄回到過去,而不用採用 Copy 舊程式碼另存為某檔案,然後某個時間從大量檔案中找你需要的歷史記錄,版本控制幫我們做到了歷史記錄的儲存,可以方便地查詢及回滾到過去的某一版本。

不同:

git和其他版本控制系統(如 CVS)有不少的差別,git本身關心檔案的整體性是否有改變,但多數的 CV S或 Subversion 系統則在乎檔案內容的差異。因此git更像一個檔案系統,直接在本機上獲取資料,不必連線到主機端獲取資料。

git 是用於Linux核心開發的版本控制工具。與CVS、Subversion(SVN) 一類的集中式版本控制工具不同,它採用了分散式版本庫的作法,不需要伺服器端軟體,就可以運作版本控制,使得原始碼的釋出和交流極其方便。git的速度很快,這對於諸如Linux核心這樣的大專案來說自然很重要。git最為出色的是它的合併追蹤(merge tracing)能力。

SVN 是集中式或者有中心式版本控制系統,版本庫是集中放在中央伺服器的,而幹活的時候,用的都是自己的電腦,所以首先要從中央伺服器哪裡得到最新的版本,然後幹活,幹完後,需要把自己做完的活推送到中央伺服器。集中式版本控制系統是必須聯網才能工作,如果在區域網還可以,頻寬夠大,速度夠快,如果在網際網路下,如果網速慢的話,就納悶了。

Git 是分散式版本控制系統,那麼它就沒有中央伺服器的,每個人的電腦就是一個完整的版本庫,這樣,工作的時候就不需要聯網了,因為版本都是在自己的電腦上。既然每個人的電腦都有一個完整的版本庫,那多個人如何協作呢?比如說自己在電腦上改了檔案A,其他人也在電腦上改了檔案A,這時,你們兩之間只需把各自的修改推送給對方,就可以互相看到對方的修改了。

21)請寫一段棧溢位、堆溢位的程式碼

  1. public class Test {      
  2.     //堆    
  3.     public void testHeap(){      
  4.         for(;;){      
  5.               ArrayList list = new ArrayList (2000);      
  6.           }      
  7.     }      
  8.      //棧    
  9.     int num=1;      
  10.     public void testStack(){      
  11.         num++;      
  12.         this.testStack();      
  13.      }      
  14.     public static void main(String[] args){      
  15.         Test  t  = new Test ();      
  16.         t.testHeap();      
  17.         t.testStack();         
  18.     }      
  19. }     

22)動態代理:JDK動態代理和CGLIB代理的區別

當一個物件(客戶端)不能或者不想直接引用另一個物件(目標物件),這時可以應用代理模式在這兩者之間構建一個橋樑--代理物件。按照代理物件的建立時期不同,可以分為兩種:

靜態代理:程式設計師事先寫好代理物件類,在程式釋出前就已經存在了;

動態代理:應用程式釋出後,通過動態建立代理物件。

1.JDK動態代理

此時代理物件和目標物件實現了相同的介面,目標物件作為代理物件的一個屬性,具體介面實現中,可以在呼叫目標物件相應方法前後加上其他業務處理邏輯。

代理模式在實際使用時需要指定具體的目標物件,如果為每個類都新增一個代理類的話,會導致類很多,同時如果不知道具體類的話,怎樣實現代理模式呢?這就引出動態代理。

JDK動態代理只能針對實現了介面的類生成代理。

2.CGLIB代理

CGLIB(CODE GENERLIZE LIBRARY)代理是針對類實現代理,主要是對指定的類生成一個子類,覆蓋其中的所有方法,所以該類或方法不能宣告稱final的。

如果目標物件沒有實現介面,則預設會採用CGLIB代理;

如果目標物件實現了介面,可以強制使用CGLIB實現代理(新增CGLIB庫,並在spring配置中加入<aop:aspectj-autoproxy proxy-target-class="true"/>)。

AOP包括切面(aspect)、通知(advice)、連線點(joinpoint),實現方式就是通過對目標物件的代理在連線點前後加入通知,完成統一的切面操作。

22)  轉發與重定向的區別

轉發是伺服器行為,重定向是客戶端行為。

二、    IO

1)   bio,nio,aio的區別

同步阻塞IO(JAVA BIO):

    同步並阻塞,伺服器實現模式為一個連線一個執行緒,即客戶端有連線請求時伺服器端就需要啟動一個執行緒進行處理,如果這個連線不做任何事情會造成不必要的執行緒開銷,當然可以通過執行緒池機制改善。

同步非阻塞IO(Java NIO) :

同步非阻塞,伺服器實現模式為一個請求一個執行緒,即客戶端傳送的連線請求都會註冊到多路複用器上,多路複用器輪詢到連線有I/O請求時才啟動一個執行緒進行處理。使用者程序也需要時不時的詢問IO操作是否就緒,這就要求使用者程序不停的去詢問。

非同步阻塞IO(Java NIO): 

   此種方式下是指應用發起一個IO操作以後,不等待核心IO操作的完成,等核心完成IO操作以後會通知應用程式,這其實就是同步和非同步最關鍵的區別,同步必須等待或者主動的去詢問IO是否完成,那麼為什麼說是阻塞的呢?因為此時是通過select系統呼叫來完成的,而select函式本身的實現方式是阻塞的,而採用select函式有個好處就是它可以同時監聽多個檔案控制代碼(如果從UNP的角度看,select屬於同步操作。因為select之後,程序還需要讀寫資料),從而提高系統的併發性! 

(Java AIO(NIO.2))非同步非阻塞IO: 

   在此種模式下,使用者程序只需要發起一個IO操作然後立即返回,等IO操作真正的完成以後,應用程式會得到IO操作完成的通知,此時使用者程序只需要對資料進行處理就好了,不需要進行實際的IO讀寫操作,因為真正的IO讀取或者寫入操作已經由核心完成了。   

2)   核心元件 Buffer,Channel,Selector等,用處是什麼

Buffer

本質: 一塊可寫入資料,也可以從中讀取資料的記憶體, 這塊記憶體被包裝成NIO buffer物件,並提供相關訪問以便進行訪問

Channel

與流類似,又有些不同:

1. 既可以從通道中讀取資料,又可以寫資料到通道。但流的讀寫通常是單向的。

2. 通道可以非同步地讀寫。

3. 通道中的資料總是要先讀到一個Buffer,或者總是要從一個Buffer中寫入

Seletor

Selector(選擇器)是Java NIO中能夠檢測一到多個NIO通道,並能夠知曉通道是否為諸如讀寫事件做好準備的元件。這樣,一個單獨的執行緒可以管理多個channel,從而管理多個網路連線。

3)   NIO對比IO的有點在哪裡

Java NIO和IO之間第一個最大的區別是,IO是面向流的,NIO是面向緩衝區的。

1.        io是面向流的,也就是讀取資料的時候是從流上逐個讀取,所以資料不能進行整體以為,沒有緩衝區;nio是面向緩衝區的,資料是儲存在緩衝區中,讀取資料是在緩衝區中進行,所以進行資料的偏移操作更加方便

2.        io是阻塞的,當一個執行緒操作io時如果當前沒有資料可讀,那麼執行緒阻塞,nio由於是對通道操作io,所以是非阻塞,當一個通道無資料可讀,可切換通道處理其他io

3.        nio有selecter選擇器,就是執行緒通過選擇器可以選擇多個通道,而io只能處理一個

4)   哪些開源專案中用到了NIO

dubbo裡的netty就大量用了nio

5)   nio框架:dubbo的實現原理

dubbo作為rpc框架,實現的效果就是呼叫遠端的方法就像在本地呼叫一樣。如何做到呢?就是本地有對遠端方法的描述,包括方法名、引數、返回值,在dubbo中是遠端和本地使用同樣的介面;然後呢,要有對網路通訊的封裝,要對呼叫方來說通訊細節是完全不可見的,網路通訊要做的就是將呼叫方法的屬性通過一定的協議(簡單來說就是訊息格式)傳遞到服務端;服務端按照協議解析出呼叫的資訊;執行相應的方法;在將方法的返回值通過協議傳遞給客戶端;客戶端再解析;在呼叫方式上又可以分為同步呼叫和非同步呼叫;簡單來說基本就這個過程。

三、    演算法

1)   氣泡排序

原理:以從小到大排序為例,每一輪排序就找出未排序序列中最大值放在最後。

設陣列的長度為N: 
(1)比較前後相鄰的二個數據,如果前面資料大於後面的資料,就將這二個數據交換。

(2)這樣對陣列的第0個數據到N-1個數據進行一次遍歷後,最大的一個數據就“沉”到陣列第N-1個位置。

(3)N=N-1,如果N不為0就重複前面二步,否則排序完成。

以上就是氣泡排序的基本思想,按照這個定義很快就能寫出程式碼:

  1. /** 
  2.  * 氣泡排序的第一種實現, 沒有任何優化 
  3.  * @param a 
  4.  * @param n 
  5.  */  
  6. public static void bubbleSort1(int [] a, int n){  
  7.     int i, j;  
  8.     for(i=0; i<n; i++){//表示n次排序過程。  
  9.         for(j=1; j<n-i; j++){  
  10.             if(a[j-1] > a[j]){//前面的數字大於後面的數字就交換  
  11.                 //交換a[j-1]和a[j]  
  12.                 int temp;  
  13.                 temp = a[j-1];  
  14.                 a[j-1] = a[j];  
  15.                 a[j]=temp;  
  16.             }  
  17.         }  
  18.     }  
  19. }// end  

2)   快速排序

快速排序是氣泡排序的升級,他們都屬於交換類排序,都是採用不斷的比較和移動來實現排序的。快速排序是一種非常高效的排序演算法,它的實現,增大了記錄的比較和移動的距離,將關鍵字較大的記錄從前面直接移動到後面,關鍵字較小的記錄從後面直接移動到前面,從而減少了總的比較次數和移動次數。同時採用“分而治之”的思想,把大的拆分為小的,小的拆分為更小的,其原理如下:對於給定的一組記錄,選擇一個基準元素,通常選擇第一個元素或者最後一個元素,通過一趟掃描,將待排序列分成兩部分,一部分比基準元素小,一部分大於等於基準元素,此時基準元素在其排好序後的正確位置,然後再用同樣的方法遞迴地排序劃分的兩部分,直到序列中的所有記錄均有序為止。

Java實現:

  1. publicclass QuickSort {  
  2.     publicstaticvoidsort(int a[], int low, int hight) {  
  3.         int i, j, index;  
  4.         if (low > hight) {  
  5.            return;  
  6.         }  
  7.         i =low;  
  8.         j =hight;  
  9.        index = a[i]; // 用子表的第一個記錄做基準  
  10.         while (i < j) { // 從表的兩端交替向中間掃描  
  11.            while (i < j && a[j] >= index)  
  12.                j--;  
  13.            if (i < j)  
  14.                a[i++] = a[j];// 用比基準小的記錄替換低位記錄  
  15.            while (i < j && a[i] < index)  
  16.                i++;  
  17.            if (i < j) // 用比基準大的記錄替換高位記錄  
  18.                a[j--] = a[i];  
  19.         }  
  20.        a[i] = index;// 將基準數值替換回 a[i]  
  21.        sort(a, low, i - 1); // 對低子表進行遞迴排序  
  22.        sort(a, i + 1, hight); // 對高子表進行遞迴排序  
  23.     }  
  24.     publicstaticvoidquickSort(int a[]) {  
  25.        sort(a, 0, a.length - 1);  
  26.     }  
  27.     publicstaticvoidmain(String[] args) {  
  28.         int a[] = { 4938659776132749 };  
  29.        quickSort(a);  
  30.        System.out.println(Arrays.toString(a));  
  31.     }  
  32. }  

3)   二分查詢

二分查詢也稱折半查詢(BinarySearch),它是一種效率較高的查詢方法。但是,折半查詢要求線性表必須採用順序儲存結構,而且表中元素按關鍵字有序排列。

首先,假設表中元素是按升序排列,將表中間位置記錄的關鍵字與查詢關鍵字比較,如果兩者相等,則查詢成功;否則利用中間位置記錄將表分成前、後兩個子表,如果中間位置記錄的關鍵字大於查詢關鍵字,則進一步查詢前一子表,否則進一步查詢後一子表。重複以上過程,直到找到滿足條件的記錄,使查詢成功,或直到子表不存在為止,此時查詢不成功。

Java實現:

四、    多執行緒相關

1)   阻塞佇列的實現 參考ArrayBlockingQueue的底層實現

ArrayBlockingQueue:基於陣列實現的一個阻塞佇列,在建立ArrayBlockingQueue物件時必須制定容量大小。並且可以指定公平性與非公平性,預設情況下為非公平的,即不保證等待時間最長的佇列最優先能夠訪問佇列。

LinkedBlockingQueue:基於連結串列實現的一個阻塞佇列,在建立LinkedBlockingQueue物件時如果不指定容量大小,則預設大小為Integer.MAX_VALUE。

ArrayBlockingQueue基於陣列的阻塞佇列實現,在ArrayBlockingQueue內部,維護了一個定長陣列,以便快取佇列中的資料物件,這是一個常用的阻塞佇列,除了一個定長陣列外,ArrayBlockingQueue內部還儲存著兩個整形變數,分別標識著佇列的頭部和尾部在陣列中的位置。  ArrayBlockingQueue在生產者放入資料和消費者獲取資料,都是共用同一個鎖物件,由此也意味著兩者無法真正並行執行,這點尤其不同於LinkedBlockingQueue;按照實現原理來分析,ArrayBlockingQueue完全可以採用分離鎖,從而實現生產者和消費者操作的完全並行執行。Doug Lea之所以沒這樣去做,也許是因為ArrayBlockingQueue的資料寫入和獲取操作已經足夠輕巧,以至於引入獨立的鎖機制,除了給程式碼帶來額外的複雜性外,其在效能上完全佔不到任何便宜。 ArrayBlockingQueue和LinkedBlockingQueue間還有一個明顯的不同之處在於,前者在插入或刪除元素時不會產生或銷燬任何額外的物件例項,而後者則會生成一個額外的Node物件。這在長時間內需要高效併發地處理大批量資料的系統中,其對於GC的影響還是存在一定的區別。而在建立ArrayBlockingQueue時,我們還可以控制物件的內部鎖是否採用公平鎖,預設採用非公平鎖。

2)   為什麼要用執行緒池

執行緒的執行過程:

建立(t1)  執行(t2) 銷燬(t3)

執行緒執行的總時間 T= t1+t2+t3;

假如,有些業務邏輯需要頻繁的使用執行緒執行某些簡單的任務,那麼很多時間都會浪費t1和t3上。

為了避免這種問題,JAVA提供了執行緒池

線上程池中的執行緒可以複用,當執行緒執行完任務之後,不被銷燬。

因為建立執行緒開銷比較大,當你的程式需要頻繁地建立銷燬一些相同的執行緒時,就可以先建立一定數量的執行緒,讓他們睡眠,當需要執行緒的時候,就從裡面拿一個出來跑,跑完了再放回去,這樣就增加了效率

3)   volatile關鍵字的用法

使多執行緒中的變數可見

4)   程序通訊的方式

訊息佇列,共享記憶體,訊號量,socket通訊等

五、    資料庫相關

1)   msyql優化經驗

1.        為查詢快取優化查詢

2.        為搜尋欄位建索引

3.        為每張表設定一個ID

4.        使用 ENUM 而不是 VARCHAR

5.        表的垂直拆分

解決表字段過多的問題

拆分原則:

把不常用的欄位放在一個表中

把大欄位獨立放在一個表中

把經常使用的欄位放在一起

6.        表的水平拆分

解決表資料量的問題,拆分後表結構是一樣的。

存在問題:跨分割槽表查詢、統計及後臺報表操作

2)   mysql的語句優化

1.        儘量避免在列上運算,這樣會導致索引失效

2.        使用 JOIN 時,應該用小結果集驅動大結果集,同時把複雜的 JOIN 查詢拆分成多個query,因為 JOIN 多個表,可能導致更多的鎖定和堵塞

3.        使用 LIKE 時,避免使用 %%

4.        select 指定查詢欄位,不要全查出來,節省記憶體

5.        使用批量插入語句節省互動

6.        limit的基數比較大時,使用 betweenbetween 限定比 limit 快,但是between也有缺陷,如果id中間有斷行或是中間部分id不讀取的情況,資料會少

7.        不要使用 rand 函式取多條隨機記錄

8.        避免使用 NULL

9.   不要使用count(id) , 而應該是count(*)

10.    不要做無謂的排序操作,而應儘可能在索引中完成排序

3)   說說事務的特性和隔離級別

隔離級別:

1.        髒讀

  髒讀是指在一個事務處理過程裡讀取了另一個未提交的事務中的資料。

  當一個事務正在多次修改某個資料,而在這個事務中這多次的修改都還未提交,這時一個併發的事務來訪問該資料,就會造成兩個事務得到的資料不一致。例如:使用者A向用戶B轉賬100元,對應SQL命令如下

   update account set money=money+100 where name=’B’;  (此時A通知B)

   update account set money=money - 100 where name=’A’;

當只執行第一條SQL時,A通知B檢視賬戶,B發現確實錢已到賬(此時即發生了髒讀),而之後無論第二條SQL是否執行,只要該事務不提交,則所有操作都將回滾,那麼當B以後再次檢視賬戶時就會發現錢其實並沒有轉。

2.        不可重複讀

不可重複讀是指在對於資料庫中的某個資料,一個事務範圍內多次查詢卻返回了不同的資料值,這是由於在查詢間隔,被另一個事務修改並提交了。

例如事務T1在讀取某一資料,而事務T2立馬修改了這個資料並且提交事務給資料庫,事務T1再次讀取該資料就得到了不同的結果,傳送了不可重複讀。

不可重複讀和髒讀的區別是,髒讀是某一事務讀取了另一個事務未提交的髒資料,而不可重複讀則是讀取了前一事務提交的資料。

在某些情況下,不可重複讀並不是問題,比如我們多次查詢某個資料當然以最後查詢得到的結果為主。但在另一些情況下就有可能發生問題,例如對於同一個資料A和B依次查詢就可能不同

3.        虛讀(幻讀)

幻讀是事務非獨立執行時發生的一種現象。例如事務T1對一個表中所有的行的某個資料項做了從“1”修改為“2”的操作,這時事務T2又對這個表中插入了一行資料項,而這個資料項的數值還是為“1”並且提交給資料庫。而操作事務T1的使用者如果再檢視剛剛修改的資料,會發現還有一行沒有修改,其實這行是從事務T2中新增的,就好像產生幻覺一樣,這就是發生了幻讀。

幻讀和不可重複讀都是讀取了另一條已經提交的事務(這點就髒讀不同),所不同的是不可重複讀查詢的都是同一個資料項,而幻讀針對的是一批資料整體(比如資料的個數)。

MySQL資料庫為我們提供的四種隔離級別:

1)        Serializable (序列化):可避免髒讀、不可重複讀、幻讀的發生。

2)        Repeatable read (可重複讀):可避免髒讀、不可重複讀的發生。

3)        Read committed (讀已提交):可避免髒讀的發生。

4)        Read uncommitted (讀未提交):最低級別,任何情況都無法保證。

4)   悲觀鎖和樂觀鎖的區別,怎麼實現

1.        悲觀鎖:即很悲觀,每次拿資料的時候都覺得資料會被人更改,所以拿資料的時候就把這條記錄鎖掉,這樣別人就沒法改這條資料了,一直到你的鎖釋放。

2.        樂觀鎖:即很樂觀,查詢資料的時候總覺得不會有人更改資料,等到更新的時候再判斷這個資料有沒有被人更改,有人更改了則本次更新失敗。

一個典型的依賴資料庫的悲觀鎖呼叫:

select * from account where name=”rica” forupdate

悲觀鎖,也是基於資料庫的鎖機制實現。

樂觀鎖,大多是基於資料版本(Version)記錄機制實現,需要為每一行資料增加一個版本標識(也就是每一行資料多一個欄位version),每次更新資料都要更新對應的版本號+1。

樂觀鎖的工作原理:讀取出資料時,將此版本號一同讀出,之後更新時,對此版本號加一。此時,將提交資料的版本資料與資料庫表對應記錄的當前版本資訊進行比對,如果提交的資料版本號大於資料庫表當前版本號,則予以更新,否則認為是過期資料。

樂觀鎖還可以基於timestamp、和ALL等;

all 就是讓該記錄所有的欄位都為版本控制資訊 更新的時候把該記錄所有更新前的資料作為WHERE條件。

timestamp 就是把查詢出來的時候的時間作為更新的時候的條件與資料庫記錄進行比對是否相等

select * from users whereuserName='fudong';

update users set userSex='女',updateDate=sysDate where userName='fudong' and updateDate=oldDate;

5)   樂觀鎖會產生什麼問題

ABA問題

樂觀鎖只在提交時會對原始資料進行對比

但是如果期間發生過由A—B—A 的問題,樂觀鎖會認為資料是正常的

解決這個問題的辦法是加入版本號,然後提交時做對比

六、     nosql相關(主要是redis

1)   redis和memcache的區別

1. Redis中,並不是所有的資料都一直儲存在記憶體中的,這是和Memcached相比一個最大的區別。

2. Redis不僅僅支援簡單的k/v型別的資料,同時還提供list,set,hash等資料結構的儲存。

3. Redis支援資料的備份,即master-slave模式的資料備份。

4. Redis支援資料的持久化,可以將記憶體中的資料保持在磁碟中,重啟的時候可以再次載入進行使用。

2)   redis是如何持久化的:rdb和aof

RDB 持久化可以在指定的時間間隔內生成資料集的時間點快照

優勢 

RDB 檔案要比AOF小

RDB的效能更好

劣勢

RDB的持久化不夠及時

RDB持久化時如果檔案過大可能會造成伺服器的阻塞,停止客戶端請求

AOF redis會將每一個收到的寫命令都通過write函式追加到檔案中(預設是appendonly.aof)。

優勢

AOF的永續性更加的耐久(可以每秒 或 每次操作儲存一次)

AOF 檔案有序地儲存了對資料庫執行的所有寫入操作, 這些寫入操作以 Redis協議的格式儲存,因此 AOF 檔案的內容非常容易被人讀懂,對檔案進行分析(parse)也很輕鬆。

AOF是增量操作

劣勢

對於相同的資料集來說,AOF 檔案的體積通常要大於 RDB 檔案的體積

根據所使用的 fsync 策略,AOF 的速度可能會慢於 RDB 。

當 Redis 啟動時,如果 RDB 持久化和 AOF 持久化都被打開了,那麼程式會優先使用 AOF 檔案來恢復資料集,因為 AOF 檔案所儲存的資料通常

七、     linux相關

1)    linux常用的命令有哪些

1.        cd命令

2.        ls命令

3.        grep命令

4.        find命令

5.        cp命令

6.        mv命令

7.        rm命令

8.        kill命令

9.        file命令

10.    tar命令

11.    cat命令

12.    chmod命令

2)    如何獲取java程序的pid

ps -ef | grep java

kill -9 XXXXX     XXXXX為上述查出的序號

3)    如何實時列印日誌

cat /var/log/*.log

如果日誌在更新,如何實時檢視 tail -f /var/log/messages

還可以使用 watch -d -n 1 cat /var/log/messages

-d表示高亮不同的地方,-n表示多少秒重新整理一次。