一位老鳥的JAVA面試和基礎總結

前言

  畢業至今已有4年的時間,近兩年期間陸續面試了很多的求職的前(JAVA)、後(WEB)端開發人員,包括實習生、應屆畢業生、一兩年工做經驗的、也有三四年工做經驗的,也算見過了比較多的開發人員,想在這裏作個總結,本次主要講一講面試和後端(java)相關的東西;html

關於面試準備

  先推薦一個寫的不錯的博客,專門關於面試的,比較詳盡仔細:關於面試。我在這裏簡單總結幾點:前端

  一、簡歷要用心準備好,我的信息,特別是聯繫方式必定要清晰明確,自身掌握的技能要完成清晰,項目經歷最好按照時間順序,說明本人在項目中的職責,完成的工做,有什麼樣的提高或收穫;java

  二、通常面試流程是電面=》HR現場面=》技術面=》結果,並非每個面試結果就能立馬有結果,因此當面試官說回去等消息的時候,並不表明沒有機會,有時候須要討論篩選才能最終肯定人選。面試

  三、關於自我介紹,最好簡明扼要,能體現自身的特色,表達流暢、自信,提早最好準備;redis

  四、準備好紮實的基礎知識,以及對經歷過的項目要有足夠的認識,每個項目都是一次學習、提高的機會,通常JAVA集合類是考察的重點;算法

  五、通常好一點的面試官會順着知識點逐漸深刻或者逐漸擴展,因此對於知識點的掌握最好全面深刻,不要蜻蜓點水式的學習;sql

  六、當遇到一些設計類的問題時,通常面試官考察的是你的思路,對問題的應變能力,對於事物觀察的點;數據庫

JAVA基礎(答案僅供參考,若有不對之處請批評指正)

  一、HashMap源碼,實現原理,JDK8之後對HashMap作了怎樣的優化。segmentfault

  答:HashMap是基於哈希表的Map接口的非同步實現,提供全部可選的映射操做,並容許使用null值和null鍵,不保證映射的順序;HashMap是一個「鏈表散列」的數據結構,即數組和鏈表的結合體;它的底層就是一個數組結構,數組中的每一項又是一個鏈表,每當新建一個HashMap時,就會初始化一個數組;後端

可參考博客:完全搞懂JAVA集合HashMap,HashTable,ConcurrentHashMap之關聯

  而在JDK8中引入了紅黑樹的部分,當存入到數組中的鏈表長度大於(默認)8時,即轉爲紅黑樹;利用紅黑樹快速增刪改查的特色提升HashMap的性能,其中會用到紅黑樹的插入、刪除、查找等算法。本文再也不對紅黑樹展開討論,想了解更多紅黑樹數據結構的工做原理能夠參考http://blog.csdn.net/v_july_v/article/details/6105630

可參考博客:JAVA8系列之從新認識HashMap

 

   二、HashMap的擴容是怎樣擴容的,爲何都是2的N次冪的大小。

  答:能夠參考上文  JAVA8系列之從新認識HashMap  有詳細的講解

 

  三、HashMap,HashTable,ConcurrentHashMap的區別

  答:   

  a、HashMap是非線程安全的,HashTable是線程安全的。

  b、HashMap的鍵和值都容許有null值存在,而HashTable則不行。

  c、由於線程安全的問題,HashMap效率比HashTable的要高。

  HashMap:它根據鍵的hashCode值存儲數據,大多數狀況下能夠直接定位到它的值,於是具備很快的訪問速度,但遍歷順序倒是不肯定的。 HashMap最多隻容許一條記錄的鍵爲null,容許多條記錄的值爲null。HashMap非線程安全,即任一時刻能夠有多個線程同時寫HashMap,可能會致使數據的不一致。若是須要知足線程安全,能夠用 Collections的synchronizedMap方法使HashMap具備線程安全的能力,或者使用ConcurrentHashMap。

   Hashtable:Hashtable是遺留類,不少映射的經常使用功能與HashMap相似,不一樣的是它承自Dictionary類,而且是線程安全的,任一時間只有一個線程能寫Hashtable,併發性不如ConcurrentHashMap,由於ConcurrentHashMap引入了分段鎖。

 

  四、極高併發下HashTable和ConcurrentHashMap哪一個性能更好,爲何,如何實現的。

  答:固然是ConcurrentHashMap,由於ConcurrentHashMap引入了分段鎖,而HashTable則使用的是方法級別的鎖;所以在新版本中通常不建議使用HashTable,不須要線程安全的場合可使用HashMap,而須要線程安全的場合可使用ConcurrentHashMap;

 

  五、HashMap在高併發下若是沒有處理線程安全會有怎樣的隱患,具體表現是什麼。

  答:可能形成死循環,具體表現鏈表的循環指向;

 

  六、JAVA中四種修飾符的限制範圍。

  private:修飾的成員只能在同類中別訪問,而在同包、子類和其餘包中都不能被訪問

  public:修飾的成員在同類、同包、子類(繼承自本類)、其餘包均可以訪問

  protected:修飾的成員在同類、同包、子類中能夠訪問,其餘包中不能被訪問

  default:修飾的成員在同類、同包中能夠訪問,但其餘包中無論是否是子類都不能被訪問

 

  七、Object中的方法

  構造函數

  hashCode():用戶獲取對象的hash值,用於檢索

  queals():用於確認兩個對象是否相等;補充,哈希值相同的對象不必定equals(),但equals()的兩個對象,hash值必定相等

  toString():返回一個String對象,用來標識本身

  getClass():返回一個class對象,打印的格式通常爲  class package.name.xxx,常常用於java的反射機制

  clone():用來另存一個當前存在的對象

  finalize():垃圾回收的時候回用到,匿名對象回收以前會調用到

  wait():用於讓當前線程失去操做權限,當前線程進入等待序列

  wait(long)、wait(long,int):用戶設定下一次獲取鎖的距離當前釋放鎖的間隔時間

  notify():用於隨機通知一個持有對象鎖的線程獲取操做的權限

  notifyAll():用於通知全部持有對象鎖的線程獲取操做權限

 

  八、接口和抽象類的區別 

  答:一個類能夠實現多個接口,但只能繼承一個抽象類;抽象類能夠包含具體的方法,接口全部的方法都是抽象的(JDK8開始新增功能接口中有default方法);抽象類能夠聲明和使用字段,接口則不能,但能夠建立靜態的final常量;抽象類的方法能夠是protected、public、private或者默認的package,接口的方法都是public;抽象類能夠定義構造函數,接口不能;接口被聲明爲public,省略後,包外的類不能訪問接口;

 

  九、動態代理的兩種方式,以及區別

  答:jdk動態代理和cglib動態代理;

  JDK動態代理只能對實現了接口的類生成代理,而不能針對類;cglib是針對類實現代理,主要是對指定的類生成一個子類,覆蓋其中的方法,由於是繼承,因此該類或方法最好不要聲明稱final,final能夠阻止繼承和多態;

  

  十、java序列化的方式

  答:實現Serializable接口、實現Externalizable接口(通常只但願序列化一部分數據,其餘數據都使用transient修飾的話有點麻煩,這時候可使用externalizable接口,指定序列化的屬性)

 

  十一、傳值和傳引用的區別,java是怎麼樣的,有沒有傳值傳引用

  答:首先,java中是沒有指針的,只存在值傳遞;而咱們常常看到對於對象的傳遞彷佛有點像引用傳遞,能夠改變對象中的某個屬性的值,請不要被這個假象矇蔽了雙眼,實際上這個傳入函數的值是對象引用的拷貝,即傳遞的是引用的地址值,因此仍是按值傳遞;

  傳值調用時,改變的是形參的值,並無改變實參的值,實參的值能夠傳遞給形參,可是這個傳遞是單向的,形參不能傳遞會實參;

  傳引用調用時,若是參數是對象,不管是對象作了何種操做,都不會改變實參對象的引用,可是若是改變了對象的內容,就會改變實參對象的內容;

 

  十二、@transactional註解在什麼狀況下會失效,爲何。

  答:一個目標對象的方法調用改目標對象的另一個方法時,即便被調用的方法已使用了@Transactional註解標記,事務也不會有效執行;Spring的官方說明在代理下(默認或者配置爲proxy-targer-class="true"),只有當前代理類的外部方法調用註解方法時代理纔會被攔截。

數據結構和算法

  一、B+樹

  參考:B+樹介紹

  二、八大排序算法

  參考:八大排序算法JAVA實現

  三、一致性Hash算法,一致性Hash算法的應用

  答:一致性hash算法是一個負載均衡算法,能夠用在分佈式緩存、數據庫的分庫分表等場景,還能夠應用在負載均衡器中做爲負載均衡算法。在多臺服務器時,對於某個請求資源經過hash算法,映射到某一臺服務器,當增長或者減小一臺服務器時,可能會改變這些資源對應的hash值,這樣可能致使一部分緩存或者數據的丟失。一致性hash就是儘量在將同一個資源請求到同一臺服務器中;

JVM

  一、JVM的內存結構

  答:主要分爲三大塊 堆內存、方法區、棧;棧又分爲JVM棧、本地方法棧

    堆(heap space),堆內存是JVM中最大的一塊,有年輕代和老年代組成,而年輕代又分爲三分部分,Eden區,From Survivor,To Survivor,默認狀況下按照8:1:1來分配

    方法區(Method area),存儲類信息、常量、靜態變量等數據,是線程共享的區域

    程序計數器(Program counter Register),是一塊較小的內存空間,是當前線程所執行的字節碼的行號指示器

    JVM棧(JVM stacks),也是線程私有的,生命週期與線程相同,每一個方法被執行時都會建立一個棧幀,用於存儲局部變量表、操做棧、動態連接、方法出口等信息

    本地方法棧(Native Mthod Stacks),爲虛擬機使用的native方法服務

  二、關於垃圾回收和常見的GC算法,請參考:GC專家系列-理解java垃圾回收

多線程

  一、JAVA實現多線程的幾種方式

  a、繼承Thread類實現

public class MyThread extends Thread {  
  public void run() {  
   System.out.println("MyThread.run()");  
  }  
}  

MyThread myThread1 = new MyThread();  
MyThread myThread2 = new MyThread();  
myThread1.start();  
myThread2.start();

  b、實現Runnable接口

  若是本身的類已經extends另外一個類,就沒法直接extends Thread,此時,必須實現一個Runnable接口,以下:

public class MyThread extends OtherClass implements Runnable {  
  public void run() {  
   System.out.println("MyThread.run()");  
  }  
}  

MyThread myThread = new MyThread();  
Thread thread = new Thread(myThread);  
thread.start();

  c、使用ExecutorService、Callable、Future實現有返回結果的多線程

import java.util.concurrent.*;  
import java.util.Date;  
import java.util.List;  
import java.util.ArrayList;  
  
/** 
* 有返回值的線程 
*/  
@SuppressWarnings("unchecked")  
public class Test {  
public static void main(String[] args) throws ExecutionException,  
    InterruptedException {  
   System.out.println("----程序開始運行----");  
   Date date1 = new Date();  
  
   int taskSize = 5;  
   // 建立一個線程池  
   ExecutorService pool = Executors.newFixedThreadPool(taskSize);  
   // 建立多個有返回值的任務  
   List<Future> list = new ArrayList<Future>();  
   for (int i = 0; i < taskSize; i++) {  
    Callable c = new MyCallable(i + " ");  
    // 執行任務並獲取Future對象  
    Future f = pool.submit(c);  
    // System.out.println(">>>" + f.get().toString());  
    list.add(f);  
   }  
   // 關閉線程池  
   pool.shutdown();  
  
   // 獲取全部併發任務的運行結果  
   for (Future f : list) {  
    // 從Future對象上獲取任務的返回值,並輸出到控制檯  
    System.out.println(">>>" + f.get().toString());  
   }  
  
   Date date2 = new Date();  
   System.out.println("----程序結束運行----,程序運行時間【"  
     + (date2.getTime() - date1.getTime()) + "毫秒】");  
}  
}  
  
class MyCallable implements Callable<Object> {  
private String taskNum;  
  
MyCallable(String taskNum) {  
   this.taskNum = taskNum;  
}  
  
public Object call() throws Exception {  
   System.out.println(">>>" + taskNum + "任務啓動");  
   Date dateTmp1 = new Date();  
   Thread.sleep(1000);  
   Date dateTmp2 = new Date();  
   long time = dateTmp2.getTime() - dateTmp1.getTime();  
   System.out.println(">>>" + taskNum + "任務終止");  
   return taskNum + "任務返回運行結果,當前任務時間【" + time + "毫秒】";  
}  
}

   二、Callable和Future

  答:Callable接口相似於Runnable,可是Runnable不會返回結果,而且沒法拋出返回結果的異常,而Callable更強大,被線程執行之後,能夠返回值,這個返回值就是經過Future拿到,也就是說,Future能夠拿到異步執行任務的返回值,能夠看如下例子: 

import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

public class Test {
    
    public static void main(String[] args) {
        Callable<Integer> callable = new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                return new Random().nextInt(100);
            }        
        };
        FutureTask<Integer> futureTask = new FutureTask<Integer>(callable);
        new Thread(futureTask).start();
        try {
            Thread.sleep(1000);
            System.err.println(futureTask.get());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

ExecutorService繼承自Executor,目的是爲咱們管理Thread對象,從而簡化併發變成,Executor使咱們無需顯示的去管理線程的聲明週期,是JDK5以後啓動任務的首選方式。

執行多個帶返回值的任務,並取得多個返回值,代碼以下:

import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CallableAndFuture {
    
    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newCachedThreadPool();
        CompletionService<Integer> cs = new ExecutorCompletionService<Integer>(threadPool);
        for( int i = 0; i < 5; i++ ){
            final int taskId = i;
            cs.submit(new Callable<Integer>() {
                @Override
                public Integer call() throws Exception {
                    return taskId;
                }
            });
        }
        
        for( int i = 0; i < 5; i++ ){
            try {
                System.err.println(cs.take().get());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

  

  三、線程池的參數有哪些,在線程池建立一個線程的過程

  corePoolSize:核心線程數,可以同時執行的任務數量

  maximumPoolSize:除去緩衝隊列中等待的任務,最大能容納的任務數(其實就是包括了核心線程池的數量)

  keepAliveTime:超出workQueue的等待任務的存活時間,就是指maximumPoolSize裏面的等待任務的存活等待時間

  unit:時間單位

  workQueue:阻塞等待線程的隊列,通常使用new LinkedBlockingQueue()這個,若是不指定容量,會一直往裏添加,沒有限制,workQueue永遠不會滿,通常選擇沒有容量上限的隊列

  threadFactory:建立線程的工廠,使用系統默認的類

  handler:當任務數超過maximumPoolSize時,對任務的處理策略,默認策略是拒絕添加

  執行流程:當線程數小於corePoolSize時,每添加一個任務,則當即開啓線程執行;當corePoolSize滿的時候,後面添加的任務將放入緩衝隊列workQueue等待;當workQueue滿的時候,看是否超過maximumPoolSize線程數,若是超過,則拒絕執行,若是沒有超過,則建立線程理解執行;  

1 import java.util.concurrent.Executors;
 2 import java.util.concurrent.LinkedBlockingQueue;
 3 import java.util.concurrent.ThreadPoolExecutor;
 4 import java.util.concurrent.TimeUnit;
 5 
 6 /**
 7  * 對線程池進行管理和封裝
 8  * @author guoqing
 9  *
10  */
11 public class ThreadPoolManager {
12     
13     private static ThreadPoolManager mInstance = new ThreadPoolManager();
14     private ThreadPoolExecutor executor;
15     
16     private int corePoolSize;    //核心線程池數量,表示可以同時執行的任務數量
17     private int maximumPoolSize;    //最大線程池數量,實際上是包含了核心線程池數量在內的
18     private long keepAliveTime = 1;        //存活時間,表示最大線程池中等待任務的存活時間
19     private TimeUnit unit = TimeUnit.HOURS;        //存活時間的時間單位
20     
21     public static ThreadPoolManager getInstance() {
22         return mInstance;
23     }
24     
25     private ThreadPoolManager() {
26         //核心線程數量的計算規則:當前設備的可用處理器核心數*2+1,可以讓cpu獲得最大效率的發揮
27         corePoolSize = Runtime.getRuntime().availableProcessors()*2+1;
28         maximumPoolSize = corePoolSize;    //雖然用不到,可是不能爲0,不然會報錯
29         //線程池機制:領工資的機制
30         executor = new ThreadPoolExecutor(corePoolSize, 
31                 maximumPoolSize, 
32                 keepAliveTime, 
33                 unit, 
34                 new LinkedBlockingQueue<Runnable>(),    //緩衝隊列,超出核心線程池的任務會被放入緩衝隊列中等待
35                 Executors.defaultThreadFactory(),        //建立線程的工廠類
36                 new ThreadPoolExecutor.AbortPolicy()    //當最大線程池也超出的時候,則拒絕執行
37                 );    
38     }
39     
40     /**
41      * 往線程池中添加任務
42      * @param r
43      */
44     public void executor(Runnable r) {
45         if(r!=null) {
46             executor.execute(r);
47         }
48     }
49     
50     /**
51      * 從線程池中移除任務
52      * @param r
53      */
54     public void remove(Runnable r) {
55         if(r!=null) {
56             executor.remove(r);
57         }
58     }
59 }

 

  四、volatile關鍵字的做用,原理

  答:保證內存可見性和禁止指令重排。實現原理可參考:JAVA併發變成--valatile關鍵字剖析

 

  五、synchronized關鍵字的用法,優缺點

  答:java關鍵字,當它用來修飾一個方法或者代碼塊的時候,可以保證在同一時刻最多隻有一個線程執行該代碼段的代碼;

    synchronized修飾的方法或者對象,只能以同步的方式執行,會引發性能問題;沒法中斷一個正在等候得到鎖的線程,也沒法經過投票得到鎖;一個優先級高的線程等待一個優先級低的線程釋放鎖會致使優先級倒置,引發性能風險;

  

  六、Lock接口有哪些實現類,使用場景是什麼

  答:Lock接口有三個實現類,一個是ReentrantLock,另兩個是ReentrantReadWriteLock類中的兩個靜態內部類ReadLock和WriteLock。

  使用場景:通常應用於多度少寫,由於讀的線程之間沒有競爭,因此比起synchronzied,性能要好不少;

   

  七、悲觀鎖、樂觀鎖的優缺點,CAS有什麼缺陷,該如何解決

  悲觀鎖:老是假設最壞的狀況,每次去拿數據的時候都認爲別人會修改,因此每次拿數據的時候都會上鎖,這樣別人拿數據的時候就會阻塞知道它拿到鎖;好比關係型數據庫的行鎖、表鎖、讀鎖、寫鎖;好比java裏面的同步原語synchronized關鍵字的實現也是悲觀鎖;

  樂觀鎖:每次去拿數據的時候都認爲別人不會修改,因此不會上鎖,可是在更新的時候會判斷一下再次期間別人有沒有更新這個數據。樂觀鎖適用於多讀的應用類型,能夠提升吞吐量。java中java.util.conncurrent.atomic包下面的原子變量類就是使用了樂觀鎖的一種實現方式CAS實現的;

  CAS:CAS是樂觀鎖技術,當多個線程嘗試使用CAS同時更新同一個變量時,只有其中一個線程能更新變量的值,而其餘線程都失敗,失敗的線程不會被掛起,而是被告知此次競爭失敗,並能夠再次嘗試;

  CAS的缺陷:ABA問題、循環時間長開銷大,只能保證一個共享變量的原子操做;

  

  八、ABC三個線程如何保證順序執行

  答:用Thread.join() 方法,或者線程池newSingleThreadExecutor(原理是會將全部線程放入一個隊列,而隊列則保證了FIFO),也能夠經過ReentrantLock,state整數用阿里判斷輪到誰來執行

 

  九、線程的狀態都有哪些(五大狀態)

  新建狀態(new):當用new操做符建立一個線程時,如new Thread(),線程尚未開始運行,此時處於仙劍狀態;

  就緒狀態(runnable):一個新建立的線程並不自動開始運行,要執行線程,必需要調用線程的start()方法,當線程對象調用start()方法即啓動了線程,start()方法建立線程運行的系統資源,並調度線程運行run()方法。當start()方法返回後,線程就處於就緒狀態;

  運行狀態(running):當線程得到cpu時間後,他才進入運行狀態,真正開始實行run()方法

  阻塞狀態(blocked):當線程運行過程當中,可能因爲各類緣由進入阻塞狀態;

    a.線程經過調用sleep方法進入睡眠狀態

    b.線程調用一個在I/O上被阻塞的操做,即該操做在輸入輸出操做完成以前不會返回到它的調用者

    c.線程試圖獲得一個鎖,而該鎖正被其餘線程持有

    d.線程正等待某個觸發條件

  死亡狀態(dead):run方法天然退出而天然死亡,或者一個未捕獲的異常終止了run方法而使線程猝死

  

 

  十、sleep和wait的區別

  答:首先,sleep()方法屬於Thread類的,而wait()方法是屬於Object類的;sleep()方法致使了程序暫停執行指定的時間,讓出cpu給其餘線程,可是他的監控狀態依然保持,當指定的時間到了又自動回恢復運行狀態,調用了sleep()方法的過程當中,線程不會釋放對象鎖;而當調用了wait()方法的時候,線程回放棄對象鎖,進入等待此對象的等待鎖定池,只有針對此對象調用notify()方法後本線程才進入對象鎖定池準備。

 

  十一、notify()和notifyAll()的區別

  答:notify()方法表示,當前線程已經放棄對資源的佔有,通知等待的線程來獲取對資源的佔有權,可是隻有一個線程可以從wait狀態中恢復;notifyAll()方法表示,當前的線程已經放棄對資源的佔有,通知全部的等待線程從wait()方法後的語句開始執行,但最終只有一個線程能競爭得到鎖並執行;notify()是對notifyAll()的一個優化,

 

   十二、ThreadLocal的瞭解,實現原理。

   答:ThreadLocal,線程本地變量。定義了一個ThreadLocal,每一個線程往這個ThreadLocal中讀寫都是線程隔離的,互相之間不會影響,他提供了一種將可變數據經過每一個線程有本身的獨立副本從而實現線程封閉的機制;實現的思路,Thread類有一個類型爲ThreadLocal.ThreadLocalMap的實例變量threadLocals,也就是說每一個線程都有一個本身的ThreadLocalMap。ThreadLocalMap有本身的獨立實現,能夠簡單的將它的key視做ThreadLocal,value爲代碼中放入的值(實際上key並非ThreadLocal本省,而是它的一個弱引用)。每一個線程在往ThreadLocal裏set值的時候,都會往本身的ThreadLocalMap裏存,讀也是已某個ThreadLocal做爲引用,在本身的map裏找對應的key,從而實現了線程的隔離。若是想詳細瞭解,能夠參考:ThreadLocal源碼解讀

數據庫相關

  一、常見的數據庫優化手段

  答:庫表優化,表設計合理化,符合三大範式;添加適當的索引(普通索引、主鍵索引、惟一索引、全文索引);分庫分表;讀寫分離等;sql語句優化,定位執行效率低,慢sql的語句,經過explain分析低效率的緣由;

 

  二、索引的優缺點,什麼字段上創建索引

  答:優勢方面:第一,經過建立惟一索引能夠保證數據的惟一性;第二,能夠大大加快數據的檢索速度,是主要目的;第三;在使用分組和排序子句進行數據檢索時,能夠顯著減小查詢中分組和排序的時間;第四,能夠在查詢中使用優化隱藏器,提升系統的性能;

  缺點方面:第一,建立索引和維護索引要耗費時間,而且隨着數據量的增長而增長;第二,每個索引須要佔用額外的物理空間,須要的磁盤開銷更大;第三,當對錶中的數據進行增長、刪除、修改操做時,索引也要動態維護,下降了數據的維護速度;

  通常來講,在常常須要搜索的列上,強制該列的惟一性和組織表中數據的排列結構的列,在常常用在連接的列上,在常常須要排序的列上,在常用在where字句的列上能夠添加索引,以提高查詢速度;一樣,對於一些甚少使用或者參考的列,只有不多數值的列(如性別),定義爲text,image,bit的列,修改性能遠遠大於檢索性能的列不適合添加索引;

 

  三、數據庫鏈接池

  答:數據庫鏈接池(Connection pooling)是程序啓動時創建足夠的數據庫鏈接,並將這些鏈接組成一個鏈接池,由程序動態的對池中的鏈接進行申請、使用、釋放;

  (1)程序初始化時建立鏈接池

  (2)使用時向鏈接池申請可用鏈接

  (3)使用完畢,將鏈接返還給鏈接池

  (4)程序退出時,斷開全部的鏈接,並釋放資源

計算機網絡

  一、TCP和UDP的區別

  答:TCP(傳輸控制協議),UDP(用戶數據報協議)

  (1)TCP面向鏈接(如打電話先撥號創建鏈接);UDP是無鏈接的,即發送數據以前不須要創建鏈接;

  (2)TCP提供可靠的服務。也就是說,經過TCP鏈接傳送的數據,無差錯,不丟失,不重複,且按序達到;UDP盡最大努力交付,即不保證可靠交付;

  (3)TCP面向字節流,其實是TCP把數據當作一連串無結構的字節流;UDP是面向報文,UDP沒有擁塞控制,所以網絡出現擁塞不會使源主機的發送速率下降(對實時應用頗有用,如IP電話,實時視頻會議等)

  (4)每一條TCP鏈接只能是點到點的,UDP支持一對一,一對多,多對一和多對多的交互通訊;

  (5)TCP首部開銷20字節,UDP首部開銷8字節;

  (6)TCP的邏輯通訊信道是全雙工的可靠信道,DUP則是不可靠信道;

 

  二、三次握手,四次揮手,爲何要四次揮手。

  答:三次握手的目的是創建可靠的通訊信道,簡單來講就是數據的發送與接收,主要目的是雙方確認本身與對方的發送和接收機能正常;    

    第一次握手:Client什麼都不能確認,Server確認了對方發送正常;

    第二次握手:Clent確認了,本身發送、接收正常,對方發送、接收正常;Server確認了本身接收正常,對方發送正常;

    第三次握手:Clent確認了,本身發送、接收正常,對方發送、接收正常;Server確認了本身發送、接收正常,對方發送、接收正常;

  因此,通過三次握手以後,就能確認雙方收發功能都正常;

  四次揮手:

    A:「喂,我不說了 (FIN)。」A->FIN_WAIT1

    B:「我知道了(ACK)。等下,上一句還沒說完。Balabala…..(傳輸數據)」B->CLOSE_WAIT | A->FIN_WAIT2

    B:」好了,說完了,我也不說了(FIN)。」B->LAST_ACK

    A:」我知道了(ACK)。」A->TIME_WAIT | B->CLOSED

    A等待2MSL,保證B收到了消息,不然重說一次」我知道了」,A->CLOSED

  

  三、長鏈接和短鏈接。

  短鏈接:鏈接=》傳輸數據=》關閉鏈接

  HTTP是無狀態的,瀏覽器和服務器之間每進行一次http操做,就創建一次鏈接,但任務結束就中斷鏈接;也能夠理解爲短鏈接是指socket鏈接後,發送接收完數據立刻斷開鏈接;

  長鏈接:鏈接=》傳輸數據=》保持鏈接=》傳輸數據=》。。。=》關閉鏈接

  長鏈接指創建socket鏈接後無論是否使用都保持鏈接,但安全性較差;

 設計模式

  此處推薦閱讀:java23種設計模式 深刻理解

  一、單例模式的幾種寫法

  懶漢模式

public class Singleton {

    private static Singleton instance = null;
    private Singleton(){}

    public static synchronized Singleton getInstance(){
        //若是尚未被實例化過,就實例化一個,而後返回
        if(instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}

  餓漢模式

public class Singleton {
    //類加載的時候instance就已經指向了一個實例
    private static Singleton instance = new Singleton();
    private Singleton(){}

    public static Singleton getInstance(){
        return instance;
    }
}

  雙重檢驗鎖

public class Singleton {
    
    private static Singleton instance = null;
    private Singleton(){}

    public static Singleton getInstance(){
        if(instance == null){
            synchronized (Singleton.class){
                if(instance == null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

  靜態內部類:由於JAVA靜態內部類的特性,加載的時候不會加載內部靜態類,使用的時候纔會加載,而使用的時候類加載又是線程安全的,這就完美達到了效果;

public class Singleton {

    private static class SingletonHolder{
        private static Singleton instance = new Singleton();
    }

    private Singleton(){}

    public static Singleton getInstance(){
        return SingletonHolder.instance;
    }
}

  枚舉:

public enum Singleton {
    INSTANCE;
}

  

  二、Spring使用了哪些設計模式

  (1)工廠模式,在各類BeanFactory以及ApplicationContext建立中都用到了;

  (2)模板模式,也是在各類BeanFactory以及ApplicationContext建立中都用到了;

  (3)代理模式,在AOP實現中用到了JDK的動態代理;

  (4)單例模式,好比建立bean的時候;

  (5)策略模式,第一個地方,加載資源文件的地方,使用了不一樣的方法,好比:classPathResource,FileSystemResource,ServletContextResource,UrlResource但他們都有共同的接口Resource;第二個地方就是AOP的實現中,採用了不一樣的方式,JDK動態代理和CGLIB代理;

分佈式相關

  一、分佈式事務的控制

  能夠參考分佈式系統事務一致性解決方案

 

  二、分佈式鎖

  答:通常使用zk瞬時有序節點實現的分佈式鎖,或者利用redis的setnx()封裝分佈式鎖;提供思路,具體的能夠自行詳細理解;

 

  三、分佈式session如何設計

  答:一個比較成熟的方案是經過redis進行session共享。詳細的原理能夠參考一種分佈式session實現方案

 

  四、關於dubbo

  能夠參考博文:Dubbo學習總結(2)——Dubbo架構詳解

 

  五、能夠了解zk相關知識

 

緩存相關

  一、redis和memcached的區別

  (1)redis和memcache都是將數據放入內存中,都是內存數據庫。可是memcache能夠緩存圖片、視頻等數據;

  (2)redis不只僅支持簡單的k/v數據,還提供list、set、hash等數據結構的存儲;

  (3)虛擬內存--redis當物理內存用完時,能夠將一些好久沒有用到的value交換到磁盤;

  (4)過時策略--memcache在set時就指定,例如set key1008,即永不過時,redis經過expire設定;

  (5)分佈式--設定memcache集羣,利用magent作一主多從;redis能夠作一主多從或一主一從;

  (6)存儲數據安全--memcache掛掉後,數據沒了,redis能夠按期保存到磁盤進行持久化;

  (7)災難恢復--memcache掛掉後,數據不可恢復。redis數據丟失後能夠經過aof恢復;

  (8)redis支持數據備份,即master-slave主備模式;

 

  二、redis是單線程的麼(是的)

  三、redis的持久化策略

  答:rdb:快照形式是直接把內存中的數據保存到一個dump文件中,定時保存

    aof:把全部的對redis的服務器進行修改的命令都存到一個文件裏,命令的集合

框架相關

  一、SpringMvc工做原理

  (1)用戶發送請求至前端控制器DispatcherServlet

  (2)DispatcherServlet收到請求調用HandlerMapping處理映射器

  (3)處理器映射器找到具體的處理器(能夠根據xml配置、註解進行查找),生成處理器對象及處理器攔截器(若有則生成)一併返回給DispatcherServlet

  (4)DispatcherServlet調用HandlerAdapter處理器映射器

  (5)HandlerAdapter通過適配調用具體的處理器(Controller,也叫後端控制器)

  (6)Controller執行完成返回ModelAndView

  (7)HandlerAdapter將Controller執行結果ModelAndView返回給DispatcherServlet

  (8)DispatcherServlet將ModelAndView傳給ViewResolver視圖解析器

  (9)ViewResolver解析後返回具體的view

  (10)DispatcherServlet根據view進行試圖渲染(即將模型數據填充至視圖中)

  (11)DispatcherServlet響應用戶 

 

  如下組件一般使用框架提供實現:

  DispatcherServlet:做爲前端控制器,整個流程控制的中心,控制其它組件執行,統一調度,下降組件之間的耦合性,提升每一個組件的擴展性。

  HandlerMapping:經過擴展處理器映射器實現不一樣的映射方式,例如:配置文件方式,實現接口方式,註解方式等。 

  HandlAdapter:經過擴展處理器適配器,支持更多類型的處理器。

  ViewResolver:經過擴展視圖解析器,支持更多類型的視圖解析,例如:jsp、freemarker、pdf、excel等。

  

  二、Quartz概念及原理

  org.quartz.Job:它是一個抽象接口,表示一個工做,也是咱們要執行的具體的內容,只定義了一個接口方法:void execute(JobExecutionContext context)

  org.quartz.JobDetail:JobDetail表示一個具體的可執行的調度程序,Job是這個可執行調度程序所要執行的內容,它包含了這個調度任務的方案和策略

  org.quartz.Trigger:Trigger是一個抽象接口,表示一個調度參數的配置,經過配置他,來告訴調度器何時去調用JobDetail

  org.quartz.Scheduler:一個調度容器,能夠註冊多個Trigger和JobDetail。當Trigger和JobDetail組合,就能夠被Scheduler容器調度了

  

  三、Spring的IOC有什麼優點

  答:要了解IOC首先要明白依賴倒置原則(Dependency Inversion Principle),就是把本來的高層建築依賴底層建築倒置過來,變成底層建築依賴高層建築。高層建築決定須要什麼,底層去實現這樣的需求,可是高層並不用管底層的是怎麼實現的;而控制反轉(Inversion of Control)就是依賴倒置原則的一種代碼的設計思路;

  

  IOC思想的核心,資源不禁使用資源的雙方管理,由不適用資源的第三方管理。

  優點:資源集中管理,實現資源的可配置和易管理;下降了使用資源雙方的依賴程度,也就是下降了耦合度;

相關文章
相關標籤/搜索