JAVA基礎總結【面試】


前言

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

關於面試准備

  先推薦一個寫的不錯的博客,專門關於面試的,比較詳盡仔細: 關於面試。我在這里簡單總結幾點:
  1、簡歷要用心准備好,個人信息,特別是聯系方式一定要清晰明確,自身掌握的技能要完成清晰,項目經歷最好按照時間順序,說明本人在項目中的職責,完成的工作,有什么樣的提升或收獲;
  2、一般面試流程是電面=》HR現場面=》技術面=》結果,並不是每一個面試結果就能立馬有結果,所以當面試官說回去等消息的時候,並不代表沒有機會,有時候需要討論篩選才能最終確定人選。
  3、關於自我介紹,最好簡明扼要,能體現自身的特點,表達流暢、自信,提前最好准備;
  4、准備好扎實的基礎知識,以及對經歷過的項目要有足夠的認識,每一個項目都是一次學習、提升的機會,一般JAVA集合類是考察的重點;
  5、一般好一點的面試官會順着知識點逐漸深入或者逐漸擴展,所以對於知識點的掌握最好全面深入,不要走馬觀花式的學習;
  6、當遇到一些設計類的問題時,一般面試官考察的是你的思路,對問題的應變能力,對於事物觀察的點;

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

   1、HashMap源碼,實現原理,JDK8以后對HashMap做了怎樣的優化。
  答:HashMap是基於哈希表的Map接口的非同步實現,提供所有可選的映射操作,並允許使用null值和null鍵,不保證映射的順序;HashMap是一個“鏈表散列”的數據結構,即數組和鏈表的結合體;它的底層就是一個數組結構,數組中的每一項又是一個鏈表,每當新建一個HashMap時,就會初始化一個數組;
  而在JDK8中引入了紅黑樹的部分,當存入到數組中的鏈表長度大於(默認)8時,即轉為紅黑樹;利用紅黑樹快速增刪改查的特點提高HashMap的性能,其中會用到紅黑樹的插入、刪除、查找等算法。本文不再對紅黑樹展開討論,想了解更多紅黑樹數據結構的工作原理可以參考 http://blog.csdn.net/v_july_v/article/details/6105630
 
   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思想的核心,資源不由使用資源的雙方管理,由不適用資源的第三方管理。

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

 

  4、Mybatis的設計思想,以及動態代理的真正實現

  Mybatis中的mapper沒有實現類,只有對應的xml文件,是如何實現的;

  Spring整合Mybatis時sqlsession為何不需要自動釋放或關閉;


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM