1. 程式人生 > >一位老鳥的JAVA面試和基礎總結

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

前言

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

關於面試準備

  先推薦一個寫的不錯的部落格,專門關於面試的,比較詳盡仔細:關於面試。我在這裡簡單總結幾點:

  1、簡歷要用心準備好,個人資訊,特別是聯絡方式一定要清晰明確,自身掌握的技能要完成清晰,專案經歷最好按照時間順序,說明本人在專案中的職責,完成的工作,有什麼樣的提升或收穫;

  2、一般面試流程是電面=》HR現場面=》技術面=》結果,並不是每一個面試結果就能立馬有結果,所以當面試官說回去等訊息的時候,並不代表沒有機會,有時候需要討論篩選才能最終確定人選。

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

  4、準備好紮實的基礎知識,以及對經歷過的專案要有足夠的認識,每一個專案都是一次學習、提升的機會,一般JAVA集合類是考察的重點;

  5、一般好一點的面試官會順著知識點逐漸深入或者逐漸擴充套件,所以對於知識點的掌握最好全面深入,不要走馬觀花式的學習;

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

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

  1、HashMap原始碼,實現原理,JDK8以後對HashMap做了怎樣的優化。

  答: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

 

   2、HashMap的擴容是怎樣擴容的,為什麼都是2的N次冪的大小。

  答:可以參考上文  JAVA8系列之重新認識HashMap  有詳細的講解

 

  3、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引入了分段鎖。

 

  4、極高併發下HashTable和ConcurrentHashMap哪個效能更好,為什麼,如何實現的。

  答:當然是ConcurrentHashMap,因為ConcurrentHashMap引入了分段鎖,而HashTable則使用的是方法級別的鎖;因此在新版本中一般不建議使用HashTable,不需要執行緒安全的場合可以使用HashMap,而需要執行緒安全的場合可以使用ConcurrentHashMap;

 

  5、HashMap在高併發下如果沒有處理執行緒安全會有怎樣的隱患,具體表現是什麼。

  答:可能造成死迴圈,具體表現連結串列的迴圈指向;

 

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

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

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

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

  default:修飾的成員在同類、同包中可以訪問,但其他包中不管是不是子類都不能被訪問

 

  7、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():用於通知所有持有物件鎖的執行緒獲取操作許可權

 

  8、介面和抽象類的區別 

  答:一個類可以實現多個介面,但只能繼承一個抽象類;抽象類可以包含具體的方法,介面所有的方法都是抽象的(JDK8開始新增功能介面中有default方法);抽象類可以宣告和使用欄位,介面則不能,但可以建立靜態的final常量;抽象類的方法可以是protected、public、private或者預設的package,介面的方法都是public;抽象類可以定義建構函式,介面不能;介面被宣告為public,省略後,包外的類不能訪問介面;

 

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

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

  JDK動態代理只能對實現了介面的類生成代理,而不能針對類;cglib是針對類實現代理,主要是對指定的類生成一個子類,覆蓋其中的方法,因為是繼承,所以該類或方法最好不要宣告稱final,final可以阻止繼承和多型;

  

  10、java序列化的方式

  答:實現Serializable介面、實現Externalizable介面(一般只希望序列化一部分資料,其他資料都使用transient修飾的話有點麻煩,這時候可以使用externalizable介面,指定序列化的屬性)

 

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

  答:首先,java中是沒有指標的,只存在值傳遞;而我們經常看到對於物件的傳遞似乎有點像引用傳遞,可以改變物件中的某個屬性的值,請不要被這個假象矇蔽了雙眼,實際上這個傳入函式的值是物件引用的拷貝,即傳遞的是引用的地址值,所以還是按值傳遞;

  傳值呼叫時,改變的是形參的值,並沒有改變實參的值,實參的值可以傳遞給形參,但是這個傳遞是單向的,形參不能傳遞會實參;

  傳引用呼叫時,如果引數是物件,無論是物件做了何種操作,都不會改變實參物件的引用,但是如果改變了物件的內容,就會改變實參物件的內容;

 

  12、@transactional註解在什麼情況下會失效,為什麼。

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

資料結構和演算法

  1、B+樹

  參考:B+樹介紹

  2、八大排序演算法

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

  3、一致性Hash演算法,一致性Hash演算法的應用

  答:一致性hash演算法是一個負載均衡演算法,可以用在分散式快取、資料庫的分庫分表等場景,還可以應用在負載均衡器中作為負載均衡演算法。在多臺伺服器時,對於某個請求資源通過hash演算法,對映到某一臺伺服器,當增加或者減少一臺伺服器時,可能會改變這些資源對應的hash值,這樣可能導致一部分快取或者資料的丟失。一致性hash就是儘可能在將同一個資源請求到同一臺伺服器中;

JVM

  1、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方法服務

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

多執行緒

  1、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 + "毫秒】";  
}  
}

   2、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();
            }
        }
    }
}

  

  3、執行緒池的引數有哪些,線上程池建立一個執行緒的過程

  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 }

 

  4、volatile關鍵字的作用,原理

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

 

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

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

    synchronized修飾的方法或者物件,只能以同步的方式執行,會引起效能問題;無法中斷一個正在等候獲得鎖的執行緒,也無法通過投票獲得鎖;一個優先順序高的執行緒等待一個優先順序低的執行緒釋放鎖會導致優先順序倒置,引起效能風險;

  

  6、Lock介面有哪些實現類,使用場景是什麼

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

  使用場景:一般應用於多度少寫,因為讀的執行緒之間沒有競爭,所以比起synchronzied,效能要好很多;

   

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

  悲觀鎖:總是假設最壞的情況,每次去拿資料的時候都認為別人會修改,所以每次拿資料的時候都會上鎖,這樣別人拿資料的時候就會阻塞知道它拿到鎖;比如關係型資料庫的行鎖、表鎖、讀鎖、寫鎖;比如java裡面的同步原語synchronized關鍵字的實現也是悲觀鎖;

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

  CAS:CAS是樂觀鎖技術,當多個執行緒嘗試使用CAS同時更新同一個變數時,只有其中一個執行緒能更新變數的值,而其他執行緒都失敗,失敗的執行緒不會被掛起,而是被告知這次競爭失敗,並可以再次嘗試;

  CAS的缺陷:ABA問題、迴圈時間長開銷大,只能保證一個共享變數的原子操作;

  

  8、ABC三個執行緒如何保證順序執行

  答:用Thread.join() 方法,或者執行緒池newSingleThreadExecutor(原理是會將所有執行緒放入一個佇列,而佇列則保證了FIFO),也可以通過ReentrantLock,state整數用阿里判斷輪到誰來執行

 

  9、執行緒的狀態都有哪些(五大狀態)

  新建狀態(new):當用new操作符建立一個執行緒時,如new Thread(),執行緒還沒有開始執行,此時處於仙劍狀態;

  就緒狀態(runnable):一個新建立的執行緒並不自動開始執行,要執行執行緒,必須要呼叫執行緒的start()方法,當執行緒物件呼叫start()方法即啟動了執行緒,start()方法建立執行緒執行的系統資源,並排程執行緒執行run()方法。當start()方法返回後,執行緒就處於就緒狀態;

  執行狀態(running):當執行緒獲得cpu時間後,他才進入執行狀態,真正開始實行run()方法

  阻塞狀態(blocked):當執行緒執行過程中,可能由於各種原因進入阻塞狀態;

    a.執行緒通過呼叫sleep方法進入睡眠狀態

    b.執行緒呼叫一個在I/O上被阻塞的操作,即該操作在輸入輸出操作完成之前不會返回到它的呼叫者

    c.執行緒試圖得到一個鎖,而該鎖正被其他執行緒持有

    d.執行緒正等待某個觸發條件

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

  

 

  10、sleep和wait的區別

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

 

  11、notify()和notifyAll()的區別

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

 

   12、ThreadLocal的瞭解,實現原理。

   答:ThreadLocal,執行緒本地變數。定義了一個ThreadLocal,每個執行緒往這個ThreadLocal中讀寫都是執行緒隔離的,互相之間不會影響,他提供了一種將可變資料通過每個執行緒有自己的獨立副本從而實現執行緒封閉的機制;實現的思路,Thread類有一個型別為ThreadLocal.ThreadLocalMap的例項變數threadLocals,也就是說每個執行緒都有一個自己的ThreadLocalMap。ThreadLocalMap有自己的獨立實現,可以簡單的將它的key視作ThreadLocal,value為程式碼中放入的值(實際上key並不是ThreadLocal本省,而是它的一個弱引用)。每個執行緒在往ThreadLocal裡set值的時候,都會往自己的ThreadLocalMap裡存,讀也是已某個ThreadLocal作為引用,在自己的map裡找對應的key,從而實現了執行緒的隔離。如果想詳細瞭解,可以參考:ThreadLocal原始碼解讀

資料庫相關

  1、常見的資料庫優化手段

  答:庫表優化,表設計合理化,符合三大正規化;新增適當的索引(普通索引、主鍵索引、唯一索引、全文索引);分庫分表;讀寫分離等;sql語句優化,定位執行效率低,慢sql的語句,通過explain分析低效率的原因;

 

  2、索引的優缺點,什麼欄位上建立索引

  答:優點方面:第一,通過建立唯一索引可以保證資料的唯一性;第二,可以大大加快資料的檢索速度,是主要目的;第三;在使用分組和排序子句進行資料檢索時,可以顯著減少查詢中分組和排序的時間;第四,可以在查詢中使用優化隱藏器,提高系統的效能;

  缺點方面:第一,建立索引和維護索引要耗費時間,並且隨著資料量的增加而增加;第二,每一個索引需要佔用額外的物理空間,需要的磁碟開銷更大;第三,當對錶中的資料進行增加、刪除、修改操作時,索引也要動態維護,降低了資料的維護速度;

  一般來說,在經常需要搜尋的列上,強制該列的唯一性和組織表中資料的排列結構的列,在經常用在連結的列上,在經常需要排序的列上,在經常使用在where字句的列上可以新增索引,以提升查詢速度;同樣,對於一些甚少使用或者參考的列,只有很少數值的列(如性別),定義為text,image,bit的列,修改效能遠遠大於檢索效能的列不適合新增索引;

 

  3、資料庫連線池

  答:資料庫連線池(Connection pooling)是程式啟動時建立足夠的資料庫連線,並將這些連線組成一個連線池,由程式動態的對池中的連線進行申請、使用、釋放;

  (1)程式初始化時建立連線池

  (2)使用時向連線池申請可用連線

  (3)使用完畢,將連線返還給連線池

  (4)程式退出時,斷開所有的連線,並釋放資源

計算機網路

  1、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則是不可靠通道;

 

  2、三次握手,四次揮手,為什麼要四次揮手。

  答:三次握手的目的是建立可靠的通訊通道,簡單來說就是資料的傳送與接收,主要目的是雙方確認自己與對方的傳送和接收機能正常;    

    第一次握手: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

  

  3、長連線和短連線。

  短連線:連線=》傳輸資料=》關閉連線

  HTTP是無狀態的,瀏覽器和伺服器之間每進行一次http操作,就建立一次連線,但任務結束就中斷連線;也可以理解為短連線是指socket連線後,傳送接收完資料馬上斷開連線;

  長連線:連線=》傳輸資料=》保持連線=》傳輸資料=》。。。=》關閉連線

  長連線指建立socket連線後不管是否使用都保持連線,但安全性較差;

 設計模式

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

  1、單例模式的幾種寫法

  懶漢模式

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;
}

  

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

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

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

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

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

  (5)策略模式,第一個地方,載入資原始檔的地方,使用了不同的方法,比如:classPathResource,FileSystemResource,ServletContextResource,UrlResource但他們都有共同的介面Resource;第二個地方就是AOP的實現中,採用了不同的方式,JDK動態代理和CGLIB代理;

分散式相關

  1、分散式事務的控制

  可以參考分散式系統事務一致性解決方案

 

  2、分散式鎖

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

 

  3、分散式session如何設計

  答:一個比較成熟的方案是通過redis進行session共享。詳細的原理可以參考一種分散式session實現方案

 

  4、關於dubbo

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

 

  5、可以瞭解zk相關知識

 

快取相關

  1、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主備模式;

 

  2、redis是單執行緒的麼(是的)

  3、redis的持久化策略

  答:rdb:快照形式是直接把記憶體中的資料儲存到一個dump檔案中,定時儲存

    aof:把所有的對redis的伺服器進行修改的命令都存到一個檔案裡,命令的集合

框架相關

  1、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等。

  

  2、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容器排程了

  

  3、Spring的IOC有什麼優勢

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

  

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

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