一份全面得Java面試題

2020-08-12 09:27:17
 

包含的模組

本文分爲十九個模組,分別是: Java 基礎、容器、多執行緒、反射、物件拷貝、Java Web 、異常、網路、設計模式、Spring/Spring MVC、Spring Boot/Spring Cloud、Hibernate、MyBatis、RabbitMQ、Kafka、Zookeeper、MySQL、Redis、JVM ,如下圖所示:

模块图

共包含 208 道面試題,本文的宗旨是爲讀者朋友們整理一份詳實而又權威的面試清單,下面 下麪一起進入主題吧。

Java 基礎

1. JDK 和 JRE 有什麼區別?

  • JDK:Java Development Kit 的簡稱,Java 開發工具包,提供了 Java 的開發環境和執行環境。
  • JRE:Java Runtime Environment 的簡稱,Java 執行環境,爲 Java 的執行提供了所需環境。

具體來說 JDK 其實包含了 JRE,同時還包含了編譯 Java 原始碼的編譯器 Javac,還包含了很多 Java 程式偵錯和分析的工具。簡單來說:如果你需要執行 Java 程式,只需安裝 JRE 就可以了,如果你需要編寫 Java 程式,需要安裝 JDK。

2. == 和 equals 的區別是什麼?

== 解讀

對於基本型別和參照型別 == 的作用效果是不同的,如下所示:

  • 基本型別:比較的是值是否相同;
  • 參照型別:比較的是參照是否相同;

程式碼範例:

String x = "string";
String y = "string";
String z = new String("string");
System.out.println(x==y); // true
System.out.println(x==z); // false
System.out.println(x.equals(y)); // true
System.out.println(x.equals(z)); // true

程式碼解讀:因爲 x 和 y 指向的是同一個參照,所以 == 也是 true,而 new String()方法則重寫開闢了記憶體空間,所以 == 結果爲 false,而 equals 比較的一直是值,所以結果都爲 true。

equals 解讀

equals 本質上就是 ==,只不過 String 和 Integer 等重寫了 equals 方法,把它變成了值比較。看下面 下麪的程式碼就明白了。

首先來看預設情況下 equals 比較一個有相同值的物件,程式碼如下:

class Cat {
    public Cat(String name) {
        this.name = name;
    }

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Cat c1 = new Cat("王磊");
Cat c2 = new Cat("王磊");
System.out.println(c1.equals(c2)); // false

輸出結果出乎我們的意料,竟然是 false?這是怎麼回事,看了 equals 原始碼就知道了,原始碼如下:

public boolean equals(Object obj) {
        return (this == obj);
}

原來 equals 本質上就是 ==。

那問題來了,兩個相同值的 String 物件,爲什麼返回的是 true?程式碼如下:

String s1 = new String("老王");
String s2 = new String("老王");
System.out.println(s1.equals(s2)); // true

同樣的,當我們進入 String 的 equals 方法,找到了答案,程式碼如下:

public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = value.length;
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}

原來是 String 重寫了 Object 的 equals 方法,把參照比較改成了值比較。

總結 :== 對於基本型別來說是值比較,對於參照型別來說是比較的是參照;而 equals 預設情況下是參照比較,只是很多類重新了 equals 方法,比如 String、Integer 等把它變成了值比較,所以一般情況下 equals 比較的是值是否相等。

3. 兩個物件的 hashCode() 相同,則 equals() 也一定爲 true,對嗎?

不對,兩個物件的 hashCode() 相同,equals() 不一定 true。

程式碼範例:

String str1 = "通話";
String str2 = "重地";
System. out. println(String. format("str1:%d | str2:%d",  str1. hashCode(),str2. hashCode()));
System. out. println(str1. equals(str2));

執行的結果:

str1:1179395 | str2:1179395

false

程式碼解讀:很顯然「通話」和「重地」的 hashCode() 相同,然而 equals() 則爲 false,因爲在雜湊表中,hashCode() 相等即兩個鍵值對的雜湊值相等,然而雜湊值相等,並不一定能得出鍵值對相等。

4. final 在 Java 中有什麼作用?

  • final 修飾的類叫最終類,該類不能被繼承。
  • final 修飾的方法不能被重寫。
  • final 修飾的變數叫常數,常數必須初始化,初始化之後值就不能被修改。

5. Java 中的 Math. round(-1. 5) 等於多少?

等於 -1,因爲在數軸上取值時,中間值(0.5)向右取整,所以正 0.5 是往上取整,負 0.5 是直接捨棄。

6. String 屬於基礎的數據型別嗎?

String 不屬於基礎型別,基礎型別有 8 種:byte、boolean、char、short、int、float、long、double,而 String 屬於物件。

7. Java 中操作字串都有哪些類?它們之間有什麼區別?

操作字串的類有:String、StringBuffer、StringBuilder。

String 和 StringBuffer、StringBuilder 的區別在於 String 宣告的是不可變的物件,每次操作都會生成新的 String 物件,然後將指針指向新的 String 物件,而 StringBuffer、StringBuilder 可以在原有物件的基礎上進行操作,所以在經常改變字串內容的情況下最好不要使用 String。

StringBuffer 和 StringBuilder 最大的區別在於,StringBuffer 是執行緒安全的,而 StringBuilder 是非執行緒安全的,但 StringBuilder 的效能卻高於 StringBuffer,所以在單執行緒環境下推薦使用 StringBuilder,多執行緒環境下推薦使用 StringBuffer。

8. String str="i"與 String str=new String("i")一樣嗎?

不一樣,因爲記憶體的分配方式不一樣。String str="i"的方式,Java 虛擬機器會將其分配到常數池中;而 String str=new String("i") 則會被分到堆記憶體中。

9. 如何將字串反轉?

使用 StringBuilder 或者 stringBuffer 的 reverse() 方法。

範例程式碼:

// StringBuffer reverse
StringBuffer stringBuffer = new StringBuffer();
stringBuffer. append("abcdefg");
System. out. println(stringBuffer. reverse()); // gfedcba
// StringBuilder reverse
StringBuilder stringBuilder = new StringBuilder();
stringBuilder. append("abcdefg");
System. out. println(stringBuilder. reverse()); // gfedcba

10. String 類的常用方法都有那些?

  • indexOf():返回指定字元的索引。
  • charAt():返回指定索引處的字元。
  • replace():字串替換。
  • trim():去除字串兩端空白。
  • split():分割字串,返回一個分割後的字串陣列。
  • getBytes():返回字串的 byte 型別陣列。
  • length():返回字串長度。
  • toLowerCase():將字串轉成小寫字母。
  • toUpperCase():將字串轉成大寫字元。
  • substring():擷取字串。
  • equals():字串比較。

11. 抽象類必須要有抽象方法嗎?

不需要,抽象類不一定非要有抽象方法。

範例程式碼:

abstract class Cat {
    public static void sayHi() {
        System. out. println("hi~");
    }
}

上面程式碼,抽象類並沒有抽象方法但完全可以正常執行。

12. 普通類和抽象類有哪些區別?

  • 普通類不能包含抽象方法,抽象類可以包含抽象方法。
  • 抽象類不能直接範例化,普通類可以直接範例化。

13. 抽象類能使用 final 修飾嗎?

不能,定義抽象類就是讓其他類繼承的,如果定義爲 final 該類就不能被繼承,這樣彼此就會產生矛盾,所以 final 不能修飾抽象類,如下圖所示,編輯器也會提示錯誤資訊:

编译器保存图

14. 介面和抽象類有什麼區別?

  • 實現:抽象類的子類使用 extends 來繼承;介面必須使用 implements 來實現介面。
  • 建構函式:抽象類可以有建構函式;介面不能有。
  • 實現數量:類可以實現很多個介面;但是隻能繼承一個抽象類。
  • 存取修飾符:介面中的方法預設使用 public 修飾;抽象類中的方法可以是任意存取修飾符。

15. Java 中 IO 流分爲幾種?

按功能來分:輸入流(input)、輸出流(output)。

按型別來分:位元組流和字元流。

位元組流和字元流的區別是:位元組流按 8 位傳輸以位元組爲單位輸入輸出數據,字元流按 16 位傳輸以字元爲單位輸入輸出數據。

16. BIO、NIO、AIO 有什麼區別?

  • BIO:Block IO 同步阻塞式 IO,就是我們平常使用的傳統 IO,它的特點是模式簡單使用方便,併發處理能力低。
  • NIO:Non IO 同步非阻塞 IO,是傳統 IO 的升級,用戶端和伺服器端通過 Channel(通道)通訊,實現了多路複用。
  • AIO:Asynchronous IO 是 NIO 的升級,也叫 NIO2,實現了非同步非堵塞 IO ,非同步 IO 的操作基於事件和回撥機制 機製。

17. Files的常用方法都有哪些?

  • Files. exists():檢測檔案路徑是否存在。
  • Files. createFile():建立檔案。
  • Files. createDirectory():建立資料夾。
  • Files. delete():刪除一個檔案或目錄。
  • Files. copy():複製檔案。
  • Files. move():移動檔案。
  • Files. size():檢視檔案個數。
  • Files. read():讀取檔案。
  • Files. write():寫入檔案。

容器

18. Java 容器都有哪些?

Java 容器分爲 Collection 和 Map 兩大類,其下又有很多子類,如下所示:

  • Collection
  • List
    • ArrayList
    • LinkedList
    • Vector
    • Stack
  • Set
    • HashSet
    • LinkedHashSet
    • TreeSet
  • Map
  • HashMap
    • LinkedHashMap
  • TreeMap
  • ConcurrentHashMap
  • Hashtable

19. Collection 和 Collections 有什麼區別?

  • Collection 是一個集合介面,它提供了對集合物件進行基本操作的通用介面方法,所有集合都是它的子類,比如 List、Set 等。
  • Collections 是一個包裝類,包含了很多靜態方法,不能被範例化,就像一個工具類,比如提供的排序方法: Collections. sort(list)。

20. List、Set、Map 之間的區別是什麼?

List、Set、Map 的區別主要體現在兩個方面:元素是否有序、是否允許元素重複。

三者之間的區別,如下表:

区别图

21. HashMap 和 Hashtable 有什麼區別?

  • 儲存:HashMap 執行 key 和 value 爲 null,而 Hashtable 不允許。
  • 執行緒安全:Hashtable 是執行緒安全的,而 HashMap 是非執行緒安全的。
  • 推薦使用:在 Hashtable 的類註釋可以看到,Hashtable 是保留類不建議使用,推薦在單執行緒環境下使用 HashMap 替代,如果需要多執行緒使用則用 ConcurrentHashMap 替代。

22. 如何決定使用 HashMap 還是 TreeMap?

對於在 Map 中插入、刪除、定位一個元素這類操作,HashMap 是最好的選擇,因爲相對而言 HashMap 的插入會更快,但如果你要對一個 key 集合進行有序的遍歷,那 TreeMap 是更好的選擇。

23. 說一下 HashMap 的實現原理?

HashMap 基於 Hash 演算法實現的,我們通過 put(key,value)儲存,get(key)來獲取。當傳入 key 時,HashMap 會根據 key. hashCode() 計算出 hash 值,根據 hash 值將 value 儲存在 bucket 裡。當計算出的 hash 值相同時,我們稱之爲 hash 衝突,HashMap 的做法是用鏈表和紅黑樹儲存相同 hash 值的 value。當 hash 衝突的個數比較少時,使用鏈表否則使用紅黑樹。

24. 說一下 HashSet 的實現原理?

HashSet 是基於 HashMap 實現的,HashSet 底層使用 HashMap 來儲存所有元素,因此 HashSet 的實現比較簡單,相關 HashSet 的操作,基本上都是直接呼叫底層 HashMap 的相關方法來完成,HashSet 不允許重複的值。

25. ArrayList 和 LinkedList 的區別是什麼?

  • 數據結構實現:ArrayList 是動態陣列的數據結構實現,而 LinkedList 是雙向鏈表的數據結構實現。
  • 隨機存取效率:ArrayList 比 LinkedList 在隨機存取的時候效率要高,因爲 LinkedList 是線性的數據儲存方式,所以需要移動指針從前往後依次查詢。
  • 增加和刪除效率:在非首尾的增加和刪除操作,LinkedList 要比 ArrayList 效率要高,因爲 ArrayList 增刪操作要影響陣列內的其他數據的下標。

綜合來說,在需要頻繁讀取集閤中的元素時,更推薦使用 ArrayList,而在插入和刪除操作較多時,更推薦使用 LinkedList。

26. 如何實現陣列和 List 之間的轉換?

  • 陣列轉 List:使用 Arrays. asList(array) 進行轉換。
  • List 轉陣列:使用 List 自帶的 toArray() 方法。

程式碼範例:

// list to array
List<String> list = new ArrayList<String>();
list. add("王磊");
list. add("的部落格");
list. toArray();
// array to list
String[] array = new String[]{"王磊","的部落格"};
Arrays. asList(array);

27. ArrayList 和 Vector 的區別是什麼?

  • 執行緒安全:Vector 使用了 Synchronized 來實現執行緒同步,是執行緒安全的,而 ArrayList 是非執行緒安全的。
  • 效能:ArrayList 在效能方面要優於 Vector。
  • 擴容:ArrayList 和 Vector 都會根據實際的需要動態的調整容量,只不過在 Vector 擴容每次會增加 1 倍,而 ArrayList 只會增加 50%。

28. Array 和 ArrayList 有何區別?

  • Array 可以儲存基本數據型別和物件,ArrayList 只能儲存物件。
  • Array 是指定固定大小的,而 ArrayList 大小是自動擴充套件的。
  • Array 內建方法沒有 ArrayList 多,比如 addAll、removeAll、iteration 等方法只有 ArrayList 有。

29. 在 Queue 中 poll()和 remove()有什麼區別?

  • 相同點:都是返回第一個元素,並在佇列中刪除返回的物件。
  • 不同點:如果沒有元素 poll()會返回 null,而 remove()會直接拋出 NoSuchElementException 異常。

程式碼範例:

Queue<String> queue = new LinkedList<String>();
queue. offer("string"); // add
System. out. println(queue. poll());
System. out. println(queue. remove());
System. out. println(queue. size());

30. 哪些集合類是執行緒安全的?

Vector、Hashtable、Stack 都是執行緒安全的,而像 HashMap 則是非執行緒安全的,不過在 JDK 1.5 之後隨着 Java. util. concurrent 併發包的出現,它們也有了自己對應的執行緒安全類,比如 HashMap 對應的執行緒安全類就是 ConcurrentHashMap。

31. 迭代器 Iterator 是什麼?

Iterator 介面提供遍歷任何 Collection 的介面。我們可以從一個 Collection 中使用迭代器方法來獲取迭代器範例。迭代器取代了 Java 集合框架中的 Enumeration,迭代器允許呼叫者在迭代過程中移除元素。

32. Iterator 怎麼使用?有什麼特點?

Iterator 使用程式碼如下:

List<String> list = new ArrayList<>();
Iterator<String> it = list. iterator();
while(it. hasNext()){
  String obj = it. next();
  System. out. println(obj);
}

Iterator 的特點是更加安全,因爲它可以確保,在當前遍歷的集合元素被更改的時候,就會拋出 ConcurrentModificationException 異常。

33. Iterator 和 ListIterator 有什麼區別?

  • Iterator 可以遍歷 Set 和 List 集合,而 ListIterator 只能遍歷 List。
  • Iterator 只能單向遍歷,而 ListIterator 可以雙向遍歷(向前/後遍歷)。
  • ListIterator 從 Iterator 介面繼承,然後新增了一些額外的功能,比如新增一個元素、替換一個元素、獲取前面或後面元素的索引位置。

34. 怎麼確保一個集合不能被修改?

可以使用 Collections. unmodifiableCollection(Collection c) 方法來建立一個只讀集合,這樣改變集合的任何操作都會拋出 Java. lang. UnsupportedOperationException 異常。

範例程式碼如下:

List<String> list = new ArrayList<>();
list. add("x");
Collection<String> clist = Collections. unmodifiableCollection(list);
clist. add("y"); // 執行時此行報錯
System. out. println(list. size());

多執行緒

35. 並行和併發有什麼區別?

  • 並行:多個處理器或多核處理器同時處理多個任務。
  • 併發:多個任務在同一個 CPU 核上,按細分的時間片輪流(交替)執行,從邏輯上來看那些任務是同時執行。

如下圖:

并发和并行

併發 = 兩個佇列和一臺咖啡機。

並行 = 兩個佇列和兩臺咖啡機。

36. 執行緒和進程的區別?

一個程式下至少有一個進程,一個進程下至少有一個執行緒,一個進程下也可以有多個執行緒來增加程式的執行速度。

37. 守護執行緒是什麼?

守護執行緒是執行在後台的一種特殊進程。它獨立於控制終端並且週期性地執行某種任務或等待處理某些發生的事件。在 Java 中垃圾回收執行緒就是特殊的守護執行緒。

38. 建立執行緒有哪幾種方式?

建立執行緒有三種方式:

  • 繼承 Thread 重寫 run 方法;
  • 實現 Runnable 介面;
  • 實現 Callable 介面。

39. 說一下 runnable 和 callable 有什麼區別?

runnable 沒有返回值,callable 可以拿到有返回值,callable 可以看作是 runnable 的補充。

40. 執行緒有哪些狀態?

執行緒的狀態:

  • NEW 尚未啓動
  • RUNNABLE 正在執行中
  • BLOCKED 阻塞的(被同步鎖或者IO鎖阻塞)
  • WAITING 永久等待狀態
  • TIMED_WAITING 等待指定的時間重新被喚醒的狀態
  • TERMINATED 執行完成

41. sleep() 和 wait() 有什麼區別?

  • 類的不同:sleep() 來自 Thread,wait() 來自 Object。
  • 釋放鎖:sleep() 不釋放鎖;wait() 釋放鎖。
  • 用法不同:sleep() 時間到會自動恢復;wait() 可以使用 notify()/notifyAll()直接喚醒。

42. notify()和 notifyAll()有什麼區別?

notifyAll()會喚醒所有的執行緒,notify()之後喚醒一個執行緒。notifyAll() 呼叫後,會將全部執行緒由等待池移到鎖池,然後參與鎖的競爭,競爭成功則繼續執行,如果不成功則留在鎖池等待鎖被釋放後再次參與競爭。而 notify()只會喚醒一個執行緒,具體喚醒哪一個執行緒由虛擬機器控制。

43. 執行緒的 run() 和 start() 有什麼區別?

start() 方法用於啓動執行緒,run() 方法用於執行執行緒的執行時程式碼。run() 可以重複呼叫,而 start() 只能呼叫一次。

44. 建立執行緒池有哪幾種方式?

執行緒池建立有七種方式,最核心的是最後一種:

  • newSingleThreadExecutor():它的特點在於工作執行緒數目被限製爲 1,操作一個無界的工作佇列,所以它保證了所有任務的都是被順序執行,最多會有一個任務處於活動狀態,並且不允許使用者改動執行緒池範例,因此可以避免其改變執行緒數目;

  • newCachedThreadPool():它是一種用來處理大量短時間工作任務的執行緒池,具有幾個鮮明特點:它會試圖快取執行緒並重用,當無快取執行緒可用時,就會建立新的工作執行緒;如果執行緒閒置的時間超過 60 秒,則被終止並移出快取;長時間閒置時,這種執行緒池,不會消耗什麼資源。其內部使用 SynchronousQueue 作爲工作佇列;

  • newFixedThreadPool(int nThreads):重用指定數目(nThreads)的執行緒,其背後使用的是無界的工作佇列,任何時候最多有 nThreads 個工作執行緒是活動的。這意味着,如果任務數量超過了活動佇列數目,將在工作佇列中等待空閒執行緒出現;如果有工作執行緒退出,將會有新的工作執行緒被建立,以補足指定的數目 nThreads;

  • newSingleThreadScheduledExecutor():建立單執行緒池,返回 ScheduledExecutorService,可以進行定時或週期性的工作排程;

  • newScheduledThreadPool(int corePoolSize):和newSingleThreadScheduledExecutor()類似,建立的是個 ScheduledExecutorService,可以進行定時或週期性的工作排程,區別在於單一工作執行緒還是多個工作執行緒;

  • newWorkStealingPool(int parallelism):這是一個經常被人忽略的執行緒池,Java 8 才加入這個建立方法,其內部會構建ForkJoinPool,利用Work-Stealing演算法,並行地處理任務,不保證處理順序;

  • ThreadPoolExecutor():是最原始的執行緒池建立,上面1-3建立方式都是對ThreadPoolExecutor的封裝。

45. 執行緒池都有哪些狀態?

  • RUNNING:這是最正常的狀態,接受新的任務,處理等待佇列中的任務。
  • SHUTDOWN:不接受新的任務提交,但是會繼續處理等待佇列中的任務。
  • STOP:不接受新的任務提交,不再處理等待佇列中的任務,中斷正在執行任務的執行緒。
  • TIDYING:所有的任務都銷燬了,workCount 爲 0,執行緒池的狀態在轉換爲 TIDYING 狀態時,會執行勾點方法 terminated()。
  • TERMINATED:terminated()方法結束後,執行緒池的狀態就會變成這個。

46. 執行緒池中 submit() 和 execute() 方法有什麼區別?

  • execute():只能執行 Runnable 型別的任務。
  • submit():可以執行 Runnable 和 Callable 型別的任務。

Callable 型別的任務可以獲取執行的返回值,而 Runnable 執行無返回值。

47. 在 Java 程式中怎麼保證多執行緒的執行安全?

  • 方法一:使用安全類,比如 Java. util. concurrent 下的類。
  • 方法二:使用自動鎖 synchronized。
  • 方法三:使用手動鎖 Lock。

手動鎖 Java 範例程式碼如下:

Lock lock = new ReentrantLock();
lock. lock();
try {
    System. out. println("獲得鎖");
} catch (Exception e) {
    // TODO: handle exception
} finally {
    System. out. println("釋放鎖");
    lock. unlock();
}

48. 多執行緒中 synchronized 鎖升級的原理是什麼?

synchronized 鎖升級原理:在鎖物件的物件頭裏面有一個 threadid 欄位,在第一次存取的時候 threadid 爲空,jvm 讓其持有偏向鎖,並將 threadid 設定爲其執行緒 id,再次進入的時候會先判斷 threadid 是否與其執行緒 id 一致,如果一致則可以直接使用此物件,如果不一致,則升級偏向鎖爲輕量級鎖,通過自旋回圈一定次數來獲取鎖,執行一定次數之後,如果還沒有正常獲取到要使用的物件,此時就會把鎖從輕量級升級爲重量級鎖,此過程就構成了 synchronized 鎖的升級。

鎖的升級的目的:鎖升級是爲了減低了鎖帶來的效能消耗。在 Java 6 之後優化 synchronized 的實現方式,使用了偏向鎖升級爲輕量級鎖再升級到重量級鎖的方式,從而減低了鎖帶來的效能消耗。

49. 什麼是死鎖?

當執行緒 A 持有獨佔鎖a,並嘗試去獲取獨佔鎖 b 的同時,執行緒 B 持有獨佔鎖 b,並嘗試獲取獨佔鎖 a 的情況下,就會發生 AB 兩個執行緒由於互相持有對方需要的鎖,而發生的阻塞現象,我們稱爲死鎖。

50. 怎麼防止死鎖?

  • 儘量使用 tryLock(long timeout, TimeUnit unit)的方法(ReentrantLock、ReentrantReadWriteLock),設定超時時間,超時可以退出防止死鎖。
  • 儘量使用 Java. util. concurrent 併發類代替自己手寫鎖。
  • 儘量降低鎖的使用粒度,儘量不要幾個功能用同一把鎖。
  • 儘量減少同步的程式碼塊。

51. ThreadLocal 是什麼?有哪些使用場景?

ThreadLocal 爲每個使用該變數的執行緒提供獨立的變數副本,所以每一個執行緒都可以獨立地改變自己的副本,而不會影響其它執行緒所對應的副本。

ThreadLocal 的經典使用場景是數據庫連線和 session 管理等。

52. 說一下 synchronized 底層實現原理?

synchronized 是由一對 monitorenter/monitorexit 指令實現的,monitor 物件是同步的基本實現單元。在 Java 6 之前,monitor 的實現完全是依靠操作系統內部的互斥鎖,因爲需要進行使用者態到內核態的切換,所以同步操作是一個無差別的重量級操作,效能也很低。但在 Java 6 的時候,Java 虛擬機器 對此進行了大刀闊斧地改進,提供了三種不同的 monitor 實現,也就是常說的三種不同的鎖:偏向鎖(Biased Locking)、輕量級鎖和重量級鎖,大大改進了其效能。

53. synchronized 和 volatile 的區別是什麼?

  • volatile 是變數修飾符;synchronized 是修飾類、方法、程式碼段。
  • volatile 僅能實現變數的修改可見性,不能保證原子性;而 synchronized 則可以保證變數的修改可見性和原子性。
  • volatile 不會造成執行緒的阻塞;synchronized 可能會造成執行緒的阻塞。

54. synchronized 和 Lock 有什麼區別?

  • synchronized 可以給類、方法、程式碼塊加鎖;而 lock 只能給程式碼塊加鎖。
  • synchronized 不需要手動獲取鎖和釋放鎖,使用簡單,發生異常會自動釋放鎖,不會造成死鎖;而 lock 需要自己加鎖和釋放鎖,如果使用不當沒有 unLock()去釋放鎖就會造成死鎖。
  • 通過 Lock 可以知道有沒有成功獲取鎖,而 synchronized 卻無法辦到。

55. synchronized 和 ReentrantLock 區別是什麼?

synchronized 早期的實現比較低效,對比 ReentrantLock,大多數場景效能都相差較大,但是在 Java 6 中對 synchronized 進行了非常多的改進。

主要區別如下:

  • ReentrantLock 使用起來比較靈活,但是必須有釋放鎖的配合動作;
  • ReentrantLock 必須手動獲取與釋放鎖,而 synchronized 不需要手動釋放和開啓鎖;
  • ReentrantLock 只適用於程式碼塊鎖,而 synchronized 可用於修飾方法、程式碼塊等。

56. 說一下 atomic 的原理?

atomic 主要利用 CAS (Compare And Wwap) 和 volatile 和 native 方法來保證原子操作,從而避免 synchronized 的高開銷,執行效率大爲提升。

反射

57. 什麼是反射?

反射是在執行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個物件,都能夠呼叫它的任意一個方法和屬性;這種動態獲取的資訊以及動態呼叫物件的方法的功能稱爲 Java 語言的反射機制 機製。

58. 什麼是 Java 序列化?什麼情況下需要序列化?

Java 序列化是爲了儲存各種物件在記憶體中的狀態,並且可以把儲存的物件狀態再讀出來。

以下情況需要使用 Java 序列化:

  • 想把的記憶體中的物件狀態儲存到一個檔案中或者數據庫中時候;
  • 想用通訊端在網路上傳送物件的時候;
  • 想通過RMI(遠端方法呼叫)傳輸物件的時候。

59. 動態代理是什麼?有哪些應用?

動態代理是執行時動態生成代理類。

動態代理的應用有 spring aop、hibernate 數據查詢、測試框架的後端 mock、rpc,Java註解物件獲取等。

60. 怎麼實現動態代理?

JDK 原生動態代理和 cglib 動態代理。JDK 原生動態代理是基於介面實現的,而 cglib 是基於繼承當前類的子類實現的。

物件拷貝

61. 爲什麼要使用克隆?

克隆的物件可能包含一些已經修改過的屬性,而 new 出來的物件的屬性都還是初始化時候的值,所以當需要一個新的物件來儲存當前物件的「狀態」就靠克隆方法了。

62. 如何實現物件克隆?

  • 實現 Cloneable 介面並重寫 Object 類中的 clone() 方法。
  • 實現 Serializable 介面,通過物件的序列化和反序列化實現克隆,可以實現真正的深度克隆。

63. 深拷貝和淺拷貝區別是什麼?

  • 淺克隆:當物件被複制時只複製它本身和其中包含的值型別的成員變數,而參照型別的成員物件並沒有複製。
  • 深克隆:除了物件本身被複制外,物件所包含的所有成員變數也將複製。

Java Web

64. JSP 和 servlet 有什麼區別?

JSP 是 servlet 技術的擴充套件,本質上就是 servlet 的簡易方式。servlet 和 JSP 最主要的不同點在於,servlet 的應用邏輯是在 Java 檔案中,並且完全從表示層中的 html 裡分離開來,而 JSP 的情況是 Java 和 html 可以組合成一個擴充套件名爲 JSP 的檔案。JSP 側重於檢視,servlet 主要用於控制邏輯。

65. JSP 有哪些內建物件?作用分別是什麼?

JSP 有 9 大內建物件:

  • request:封裝用戶端的請求,其中包含來自 get 或 post 請求的參數;
  • response:封裝伺服器對用戶端的響應;
  • pageContext:通過該物件可以獲取其他物件;
  • session:封裝使用者對談的物件;
  • application:封裝伺服器執行環境的物件;
  • out:輸出伺服器響應的輸出流物件;
  • config:Web 應用的設定物件;
  • page:JSP 頁面本身(相當於 Java 程式中的 this);
  • exception:封裝頁面拋出異常的物件。

66. 說一下 JSP 的 4 種作用域?

  • page:代表與一個頁面相關的物件和屬性。
  • request:代表與用戶端發出的一個請求相關的物件和屬性。一個請求可能跨越多個頁面,涉及多個 Web 元件;需要在頁面顯示的臨時數據可以置於此作用域。
  • session:代表與某個使用者與伺服器建立的一次對談相關的物件和屬性。跟某個使用者相關的數據應該放在使用者自己的 session 中。
  • application:代表與整個 Web 應用程式相關的物件和屬性,它實質上是跨越整個 Web 應用程式,包括多個頁面、請求和對談的一個全域性作用域。

67. session 和 cookie 有什麼區別?

  • 儲存位置不同:session 儲存在伺服器端;cookie 儲存在瀏覽器端。
  • 安全性不同:cookie 安全性一般,在瀏覽器儲存,可以被僞造和修改。
  • 容量和個數限制:cookie 有容量限制,每個站點下的 cookie 也有個數限制。
  • 儲存的多樣性:session 可以儲存在 Redis 中、數據庫中、應用程式中;而 cookie 只能儲存在瀏覽器中。

68. 說一下 session 的工作原理?

session 的工作原理是用戶端登錄完成之後,伺服器會建立對應的 session,session 建立完之後,會把 session 的 id 發送給用戶端,用戶端再儲存到瀏覽器中。這樣用戶端每次存取伺服器時,都會帶着 sessionid,伺服器拿到 sessionid 之後,在記憶體找到與之對應的 session 這樣就可以正常工作了。

69. 如果用戶端禁止 cookie 能實現 session 還能用嗎?

可以用,session 只是依賴 cookie 儲存 sessionid,如果 cookie 被禁用了,可以使用 url 中新增 sessionid 的方式保證 session 能正常使用。

70. spring mvc 和 struts 的區別是什麼?

  • 攔截級別:struts2 是類級別的攔截;spring mvc 是方法級別的攔截。
  • 數據獨立性:spring mvc 的方法之間基本上獨立的,獨享 request 和 response 數據,請求數據通過參數獲取,處理結果通過 ModelMap 交回給框架,方法之間不共用變數;而 struts2 雖然方法之間也是獨立的,但其所有 action 變數是共用的,這不會影響程式執行,卻給我們編碼和讀程式時帶來了一定的麻煩。
  • 攔截機制 機製:struts2 有以自己的 interceptor 機制 機製,spring mvc 用的是獨立的 aop 方式,這樣導致struts2 的組態檔量比 spring mvc 大。
  • 對 ajax 的支援:spring mvc 整合了ajax,所有 ajax 使用很方便,只需要一個註解 @ResponseBody 就可以實現了;而 struts2 一般需要安裝外掛或者自己寫程式碼才行。

71. 如何避免 SQL 注入?

  • 使用預處理 PreparedStatement。
  • 使用正則表達式過濾掉字元中的特殊字元。

72. 什麼是 XSS 攻擊,如何避免?

XSS 攻擊:即跨站指令碼攻擊,它是 Web 程式中常見的漏洞。原理是攻擊者往 Web 頁面裡插入惡意的指令碼程式碼(css 程式碼、Javascript 程式碼等),當使用者瀏覽該頁面時,嵌入其中的指令碼程式碼會被執行,從而達到惡意攻擊使用者的目的,如盜取使用者 cookie、破壞頁面結構、重定向到其他網站等。

預防 XSS 的核心是必須對輸入的數據做過濾處理。

73. 什麼是 CSRF 攻擊,如何避免?

CSRF:Cross-Site Request Forgery(中文:跨站請求僞造),可以理解爲攻擊者盜用了你的身份,以你的名義發送惡意請求,比如:以你名義發送郵件、發訊息、購買商品,虛擬貨幣轉賬等。

防禦手段:

  • 驗證請求來源地址;
  • 關鍵操作新增驗證碼;
  • 在請求地址新增 token 並驗證。

異常

74. throw 和 throws 的區別?

  • throw:是真實拋出一個異常。
  • throws:是宣告可能會拋出一個異常。

75. final、finally、finalize 有什麼區別?

  • final:是修飾符,如果修飾類,此類不能被繼承;如果修飾方法和變數,則表示此方法和此變數不能在被改變,只能使用。
  • finally:是 try{} catch{} finally{} 最後一部分,表示不論發生任何情況都會執行,finally 部分可以省略,但如果 finally 部分存在,則一定會執行 finally 裏面的程式碼。
  • finalize: 是 Object 類的一個方法,在垃圾收集器執行的時候會呼叫被回收物件的此方法。

76. try-catch-finally 中哪個部分可以省略?

try-catch-finally 其中 catch 和 finally 都可以被省略,但是不能同時省略,也就是說有 try 的時候,必須後面跟一個 catch 或者 finally。

77. try-catch-finally 中,如果 catch 中 return 了,finally 還會執行嗎?

finally 一定會執行,即使是 catch 中 return 了,catch 中的 return 會等 finally 中的程式碼執行完之後,纔會執行。

78. 常見的異常類有哪些?

  • NullPointerException 空指針異常
  • ClassNotFoundException 指定類不存在
  • NumberFormatException 字串轉換爲數位異常
  • IndexOutOfBoundsException 陣列下標越界異常
  • ClassCastException 數據型別轉換異常
  • FileNotFoundException 檔案未找到異常
  • NoSuchMethodException 方法不存在異常
  • IOException IO 異常
  • SocketException Socket 異常

網路

79. http 響應碼 301 和 302 代表的是什麼?有什麼區別?

301:永久重定向。

302:暫時重定向。

它們的區別是,301 對搜尋引擎優化(SEO)更加有利;302 有被提示爲網路攔截的風險。

80. forward 和 redirect 的區別?

forward 是轉發 和 redirect 是重定向:

  • 位址列 url 顯示:foward url 不會發生改變,redirect url 會發生改變;
  • 數據共用:forward 可以共用 request 裡的數據,redirect 不能共用;
  • 效率:forward 比 redirect 效率高。

81. 簡述 tcp 和 udp的區別?

tcp 和 udp 是 OSI 模型中的運輸層中的協定。tcp 提供可靠的通訊傳輸,而 udp 則常被用於讓廣播和細節控制交給應用的通訊傳輸。

兩者的區別大致如下:

  • tcp 面向連接,udp 面向非連線即發送數據前不需要建立鏈接;
  • tcp 提供可靠的服務(數據傳輸),udp 無法保證;
  • tcp 面向位元組流,udp 面向報文;
  • tcp 數據傳輸慢,udp 數據傳輸快;

82. tcp 爲什麼要三次握手,兩次不行嗎?爲什麼?

如果採用兩次握手,那麼只要伺服器發出確認數據包就會建立連線,但由於用戶端此時並未響應伺服器端的請求,那此時伺服器端就會一直在等待用戶端,這樣伺服器端就白白浪費了一定的資源。若採用三次握手,伺服器端沒有收到來自用戶端的再此確認,則就會知道用戶端並沒有要求建立請求,就不會浪費伺服器的資源。

83. 說一下 tcp 粘包是怎麼產生的?

tcp 粘包可能發生在發送端或者接收端,分別來看兩端各種產生粘包的原因:

  • 發送端粘包:發送端需要等緩衝區滿才發送出去,造成粘包;
  • 接收方粘包:接收方不及時接收緩衝區的包,造成多個包接收。

84. OSI 的七層模型都有哪些?

  • 物理層:利用傳輸媒介爲數據鏈路層提供物理連線,實現位元流的透明傳輸。
  • 數據鏈路層:負責建立和管理節點間的鏈路。
  • 網路層:通過路由選擇演算法,爲報文或分組通過通訊子網選擇最適當的路徑。
  • 傳輸層:向使用者提供可靠的端到端的差錯和流量控制,保證報文的正確傳輸。
  • 對談層:向兩個實體的表示層提供建立和使用連線的方法。
  • 表示層:處理使用者資訊的表示問題,如編碼、數據格式轉換和加密解密等。
  • 應用層:直接向使用者提供服務,完成使用者希望在網路上完成的各種工作。

85. get 和 post 請求有哪些區別?

  • get 請求會被瀏覽器主動快取,而 post 不會。
  • get 傳遞參數有大小限制,而 post 沒有。
  • post 參數傳輸更安全,get 的參數會明文限制在 url 上,post 不會。

86. 如何實現跨域?

實現跨域有以下幾種方案:

  • 伺服器端執行跨域 設定 CORS 等於 *;
  • 在單個介面使用註解 @CrossOrigin 執行跨域;
  • 使用 jsonp 跨域;

87. 說一下 JSONP 實現原理?

jsonp:JSON with Padding,它是利用script標籤的 src 連線可以存取不同源的特性,載入遠端返回的「JS 函數」來執行的。

設計模式

88. 說一下你熟悉的設計模式?

  • 單例模式:保證被建立一次,節省系統開銷。
  • 工廠模式(簡單工廠、抽象工廠):解耦程式碼。
  • 觀察者模式:定義了物件之間的一對多的依賴,這樣一來,當一個物件改變時,它的所有的依賴者都會收到通知並自動更新。
  • 外觀模式:提供一個統一的介面,用來存取子系統中的一羣介面,外觀定義了一個高層的介面,讓子系統更容易使用。
  • 模版方法模式:定義了一個演算法的骨架,而將一些步驟延遲到子類中,模版方法使得子類可以在不改變演算法結構的情況下,重新定義演算法的步驟。
  • 狀態模式:允許物件在內部狀態改變時改變它的行爲,物件看起來好像修改了它的類。

89. 簡單工廠和抽象工廠有什麼區別?

  • 簡單工廠:用來生產同一等級結構中的任意產品,對於增加新的產品,無能爲力。
  • 工廠方法:用來生產同一等級結構中的固定產品,支援增加任意產品。
  • 抽象工廠:用來生產不同產品族的全部產品,對於增加新的產品,無能爲力;支援增加產品族。

Spring/Spring MVC

90. 爲什麼要使用 spring?

  • spring 提供 ioc 技術,容器會幫你管理依賴的物件,從而不需要自己建立和管理依賴物件了,更輕鬆的實現了程式的解耦。
  • spring 提供了事務支援,使得事務操作變的更加方便。
  • spring 提供了面向切片程式設計,這樣可以更方便的處理某一類的問題。
  • 更方便的框架整合,spring 可以很方便的整合其他框架,比如 MyBatis、hibernate 等。

91. 解釋一下什麼是 aop?

aop 是面向切面程式設計,通過預編譯方式和執行期動態代理實現程式功能的統一維護的一種技術。

簡單來說就是統一處理某一「切面」(類)的問題的程式設計思想,比如統一處理日誌、異常等。

92. 解釋一下什麼是 ioc?

ioc:Inversionof Control(中文:控制反轉)是 spring 的核心,對於 spring 框架來說,就是由 spring 來負責控制物件的生命週期和物件間的關係。

簡單來說,控制指的是當前物件對內部成員的控制權;控制反轉指的是,這種控制權不由當前物件管理了,由其他(類,第三方容器)來管理。

93. spring 有哪些主要模組?

  • spring core:框架的最基礎部分,提供 ioc 和依賴注入特性。
  • spring context:構建於 core 封裝包基礎上的 context 封裝包,提供了一種框架式的物件存取方法。
  • spring dao:Data Access Object 提供了JDBC的抽象層。
  • spring aop:提供了面向切面的程式設計實現,讓你可以自定義攔截器、切點等。
  • spring Web:提供了針對 Web 開發的整合特性,例如檔案上傳,利用 servlet listeners 進行 ioc 容器初始化和針對 Web 的 ApplicationContext。
  • spring Web mvc:spring 中的 mvc 封裝包提供了 Web 應用的 Model-View-Controller(MVC)的實現。

94. spring 常用的注入方式有哪些?

  • setter 屬性注入
  • 構造方法注入
  • 註解方式注入

95. spring 中的 bean 是執行緒安全的嗎?

spring 中的 bean 預設是單例模式,spring 框架並沒有對單例 bean 進行多執行緒的封裝處理。

實際上大部分時候 spring bean 無狀態的(比如 dao 類),所有某種程度上來說 bean 也是安全的,但如果 bean 有狀態的話(比如 view model 物件),那就要開發者自己去保證執行緒安全了,最簡單的就是改變 bean 的作用域,把「singleton」變更爲「prototype」,這樣請求 bean 相當於 new Bean()了,所以就可以保證執行緒安全了。

  • 有狀態就是有數據儲存功能。
  • 無狀態就是不會儲存數據。

96. spring 支援幾種 bean 的作用域?

spring 支援 5 種作用域,如下:

  • singleton:spring ioc 容器中只存在一個 bean 範例,bean 以單例模式存在,是系統預設值;
  • prototype:每次從容器呼叫 bean 時都會建立一個新的範例,既每次 getBean()相當於執行 new Bean()操作;
  • Web 環境下的作用域:
  • request:每次 http 請求都會建立一個 bean;
  • session:同一個 http session 共用一個 bean 範例;
  • global-session:用於 portlet 容器,因爲每個 portlet 有單獨的 session,globalsession 提供一個全域性性的 http session。

注意: 使用 prototype 作用域需要慎重的思考,因爲頻繁建立和銷燬 bean 會帶來很大的效能開銷。

97. spring 自動裝配 bean 有哪些方式?

  • no:預設值,表示沒有自動裝配,應使用顯式 bean 參照進行裝配。
  • byName:它根據 bean 的名稱注入物件依賴項。
  • byType:它根據型別注入物件依賴項。
  • 建構函式:通過建構函式來注入依賴項,需要設定大量的參數。
  • autodetect:容器首先通過建構函式使用 autowire 裝配,如果不能,則通過 byType 自動裝配。

98. spring 事務實現方式有哪些?

  • 宣告式事務:宣告式事務也有兩種實現方式,基於 xml 組態檔的方式和註解方式(在類上新增 @Transaction 註解)。
  • 編碼方式:提供編碼的形式管理和維護事務。

99. 說一下 spring 的事務隔離?

spring 有五大隔離級別,預設值爲 ISOLATION_DEFAULT(使用數據庫的設定),其他四個隔離級別和數據庫的隔離級別一致:

ISOLATION_DEFAULT:用底層數據庫的設定隔離級別,數據庫設定的是什麼我就用什麼;

ISOLATIONREADUNCOMMITTED:未提交讀,最低隔離級別、事務未提交前,就可被其他事務讀取(會出現幻讀、髒讀、不可重複讀);

ISOLATIONREADCOMMITTED:提交讀,一個事務提交後才能 纔能被其他事務讀取到(會造成幻讀、不可重複讀),SQL server 的預設級別;

ISOLATIONREPEATABLEREAD:可重複讀,保證多次讀取同一個數據時,其值都和事務開始時候的內容是一致,禁止讀取到別的事務未提交的數據(會造成幻讀),MySQL 的預設級別;

ISOLATION_SERIALIZABLE:序列化,代價最高最可靠的隔離級別,該隔離級別能防止髒讀、不可重複讀、幻讀。

髒讀 :表示一個事務能夠讀取另一個事務中還未提交的數據。比如,某個事務嘗試插入記錄 A,此時該事務還未提交,然後另一個事務嘗試讀取到了記錄 A。

不可重複讀 :是指在一個事務內,多次讀同一數據。

幻讀 :指同一個事務內多次查詢返回的結果集不一樣。比如同一個事務 A 第一次查詢時候有 n 條記錄,但是第二次同等條件下查詢卻有 n+1 條記錄,這就好像產生了幻覺。發生幻讀的原因也是另外一個事務新增或者刪除或者修改了第一個事務結果集裏面的數據,同一個記錄的數據內容被修改了,所有數據行的記錄就變多或者變少了。

100. 說一下 spring mvc 執行流程?

  • spring mvc 先將請求發送給 DispatcherServlet。
  • DispatcherServlet 查詢一個或多個 HandlerMapping,找到處理請求的 Controller。
  • DispatcherServlet 再把請求提交到對應的 Controller。
  • Controller 進行業務邏輯處理後,會返回一個ModelAndView。
  • Dispathcher 查詢一個或多個 ViewResolver 檢視解析器,找到 ModelAndView 物件指定的檢視物件。
  • 檢視物件負責渲染返回給用戶端。

101. spring mvc 有哪些元件?

  • 前置控制器 DispatcherServlet。
  • 對映控制器 HandlerMapping。
  • 處理器 Controller。
  • 模型和檢視 ModelAndView。
  • 檢視解析器 ViewResolver。

102. @RequestMapping 的作用是什麼?

將 http 請求對映到相應的類/方法上。

103. @Autowired 的作用是什麼?

@Autowired 它可以對類成員變數、方法及建構函式進行標註,完成自動裝配的工作,通過@Autowired 的使用來消除 set/get 方法。

Spring Boot/Spring Cloud

104. 什麼是 spring boot?

spring boot 是爲 spring 服務的,是用來簡化新 spring 應用的初始搭建以及開發過程的。

105. 爲什麼要用 spring boot?

  • 設定簡單
  • 獨立執行
  • 自動裝配
  • 無程式碼生成和 xml 設定
  • 提供應用監控
  • 易上手
  • 提升開發效率

106. spring boot 核心組態檔是什麼?

spring boot 核心的兩個組態檔:

  • bootstrap (. yml 或者 . properties):boostrap 由父 ApplicationContext 載入的,比 applicaton 優先載入,且 boostrap 裏面的屬性不能被覆蓋;
  • application (. yml 或者 . properties):用於 spring boot 專案的自動化設定。

107. spring boot 組態檔有哪幾種類型?它們有什麼區別?

組態檔有 . properties 格式和 . yml 格式,它們主要的區別是書法風格不同。

. properties 設定如下:

spring. RabbitMQ. port=5672

. yml 設定如下:

spring:
    RabbitMQ:
        port: 5672

. yml 格式不支援 @PropertySource 註解匯入。

108. spring boot 有哪些方式可以實現熱部署?

  • 使用 devtools 啓動熱部署,新增 devtools 庫,在組態檔中把 spring. devtools. restart. enabled 設定爲 true;
  • 使用 Intellij Idea 編輯器,勾上自動編譯或手動重新編譯。

109. jpa 和 hibernate 有什麼區別?

jpa 全稱 Java Persistence API,是 Java 持久化介面規範,hibernate 屬於 jpa 的具體實現。

110. 什麼是 spring cloud?

spring cloud 是一系列框架的有序集合。它利用 spring boot 的開發便利性巧妙地簡化了分佈式系統基礎設施的開發,如服務發現註冊、設定中心、訊息匯流排、負載均衡、斷路器、數據監控等,都可以用 spring boot 的開發風格做到一鍵啓動和部署。

111. spring cloud 斷路器的作用是什麼?

在分佈式架構中,斷路器模式的作用也是類似的,當某個服務單元發生故障(類似用電器發生短路)之後,通過斷路器的故障監控(類似熔斷保險絲),向呼叫方返回一個錯誤響應,而不是長時間的等待。這樣就不會使得執行緒因呼叫故障服務被長時間佔用不釋放,避免了故障在分佈式系統中的蔓延。

112. spring cloud 的核心元件有哪些?

  • Eureka:服務註冊於發現。
  • Feign:基於動態代理機制 機製,根據註解和選擇的機器,拼接請求 url 地址,發起請求。
  • Ribbon:實現負載均衡,從一個服務的多臺機器中選擇一臺。
  • Hystrix:提供執行緒池,不同的服務走不同的執行緒池,實現了不同服務呼叫的隔離,避免了服務雪崩的問題。
  • Zuul:閘道器管理,由 Zuul 閘道器轉發請求給對應的服務。

Hibernate

113. 爲什麼要使用 hibernate?

  • hibernate 是對 jdbc 的封裝,大大簡化了數據存取層的繁瑣的重複性程式碼。
  • hibernate 是一個優秀的 ORM 實現,很多程度上簡化了 DAO 層的編碼功能。
  • 可以很方便的進行數據庫的移植工作。
  • 提供了快取機制 機製,是程式執行更改的高效。

114. 什麼是 ORM 框架?

ORM(Object Relation Mapping)物件關係對映,是把數據庫中的關係數據對映成爲程式中的物件。

使用 ORM 的優點:提高了開發效率降低了開發成本、開發更簡單更物件化、可移植更強。

115. hibernate 中如何在控制檯檢視列印的 SQL 語句?

在 Config 裏面把 hibernate. show_SQL 設定爲 true 就可以。但不建議開啓,開啓之後會降低程式的執行效率。

116. hibernate 有幾種查詢方式?

三種:hql、原生 SQL、條件查詢 Criteria。

117. hibernate 實體類可以被定義爲 final 嗎?

實體類可以定義爲 final 類,但這樣的話就不能使用 hibernate 代理模式下的延遲關聯提供效能了,所以不建議定義實體類爲 final。

118. 在 hibernate 中使用 Integer 和 int 做對映有什麼區別?

Integer 型別爲物件,它的值允許爲 null,而 int 屬於基礎數據型別,值不能爲 null。

119. hibernate 是如何工作的?

  • 讀取並解析組態檔。
  • 讀取並解析對映檔案,建立 SessionFactory。
  • 開啓 Session。
  • 建立事務。
  • 進行持久化操作。
  • 提交事務。
  • 關閉 Session。
  • 關閉 SessionFactory。

120. get()和 load()的區別?

  • 數據查詢時,沒有 OID 指定的物件,get() 返回 null;load() 返回一個代理物件。
  • load()支援延遲載入;get() 不支援延遲載入。

121. 說一下 hibernate 的快取機制 機製?

hibernate 常用的快取有一級快取和二級快取:

一級快取:也叫 Session 快取,只在 Session 作用範圍內有效,不需要使用者幹涉,由 hibernate 自身維護,可以通過:evict(object)清除 object 的快取;clear()清除一級快取中的所有快取;flush()刷出快取;

二級快取:應用級別的快取,在所有 Session 中都有效,支援設定第三方的快取,如:EhCache。

122. hibernate 物件有哪些狀態?

  • 臨時/瞬時狀態:直接 new 出來的物件,該物件還沒被持久化(沒儲存在數據庫中),不受 Session 管理。
  • 持久化狀態:當呼叫 Session 的 save/saveOrupdate/get/load/list 等方法的時候,物件就是持久化狀態。
  • 遊離狀態:Session 關閉之後物件就是遊離狀態。

123. 在 hibernate 中 getCurrentSession 和 openSession 的區別是什麼?

  • getCurrentSession 會系結當前執行緒,而 openSession 則不會。
  • getCurrentSession 事務是 Spring 控制的,並且不需要手動關閉,而 openSession 需要我們自己手動開啓和提交事務。

124. hibernate 實體類必須要有無參建構函式嗎?爲什麼?

hibernate 中每個實體類必須提供一個無參建構函式,因爲 hibernate 框架要使用 reflection api,通過呼叫 ClassnewInstance() 來建立實體類的範例,如果沒有無參的建構函式就會拋出異常。

MyBatis

125. MyBatis 中 #{}和 ${}的區別是什麼?

\#{}是預編譯處理,${}是字元替換。 在使用 #{}時,MyBatis 會將 SQL 中的 #{}替換成「?」,配合 PreparedStatement 的 set 方法賦值,這樣可以有效的防止 SQL 注入,保證程式的執行安全。

126. MyBatis 有幾種分頁方式?

分頁方式:邏輯分頁和物理分頁。

邏輯分頁: 使用 MyBatis 自帶的 RowBounds 進行分頁,它是一次性查詢很多數據,然後在數據中再進行檢索。

物理分頁: 自己手寫 SQL 分頁或使用分頁外掛 PageHelper,去數據庫查詢指定條數的分頁數據的形式。

127. RowBounds 是一次性查詢全部結果嗎?爲什麼?

RowBounds 表面是在「所有」數據中檢索數據,其實並非是一次性查詢出所有數據,因爲 MyBatis 是對 jdbc 的封裝,在 jdbc 驅動中有一個 Fetch Size 的設定,它規定了每次最多從數據庫查詢多少條數據,假如你要查詢更多數據,它會在你執行 next()的時候,去查詢更多的數據。就好比你去自動取款機取 10000 元,但取款機每次最多能取 2500 元,所以你要取 4 次才能 纔能把錢取完。只是對於 jdbc 來說,當你呼叫 next()的時候會自動幫你完成查詢工作。這樣做的好處可以有效的防止記憶體溢位。

Fetch Size 官方相關文件:http://t. cn/EfSE2g3

128. MyBatis 邏輯分頁和物理分頁的區別是什麼?

  • 邏輯分頁是一次性查詢很多數據,然後再在結果中檢索分頁的數據。這樣做弊端是需要消耗大量的記憶體、有記憶體溢位的風險、對數據庫壓力較大。
  • 物理分頁是從數據庫查詢指定條數的數據,彌補了一次性全部查出的所有數據的種種缺點,比如需要大量的記憶體,對數據庫查詢壓力較大等問題。

129. MyBatis 是否支援延遲載入?延遲載入的原理是什麼?

MyBatis 支援延遲載入,設定 lazyLoadingEnabled=true 即可。

延遲載入的原理的是呼叫的時候觸發載入,而不是在初始化的時候就載入資訊。比如呼叫 a. getB(). getName(),這個時候發現 a. getB() 的值爲 null,此時會單獨觸發事先儲存好的關聯 B 物件的 SQL,先查詢出來 B,然後再呼叫 a. setB(b),而這時候再呼叫 a. getB(). getName() 就有值了,這就是延遲載入的基本原理。

130. 說一下 MyBatis 的一級快取和二級快取?

  • 一級快取:基於 PerpetualCache 的 HashMap 本地快取,它的宣告週期是和 SQLSession 一致的,有多個 SQLSession 或者分佈式的環境中數據庫操作,可能會出現髒數據。當 Session flush 或 close 之後,該 Session 中的所有 Cache 就將清空,預設一級快取是開啓的。
  • 二級快取:也是基於 PerpetualCache 的 HashMap 本地快取,不同在於其儲存作用域爲 Mapper 級別的,如果多個SQLSession之間需要共用快取,則需要使用到二級快取,並且二級快取可自定義儲存源,如 Ehcache。預設不開啓二級快取,要開啓二級快取,使用二級快取屬性類需要實現 Serializable 序列化介面(可用來儲存物件的狀態)。

開啓二級快取數據查詢流程:二級快取 -> 一級快取 -> 數據庫。

快取更新機制 機製:當某一個作用域(一級快取 Session/二級快取 Mapper)進行了C/U/D 操作後,預設該作用域下所有 select 中的快取將被 clear。

131. MyBatis 和 hibernate 的區別有哪些?

  • 靈活性:MyBatis 更加靈活,自己可以寫 SQL 語句,使用起來比較方便。
  • 可移植性:MyBatis 有很多自己寫的 SQL,因爲每個數據庫的 SQL 可以不相同,所以可移植性比較差。
  • 學習和使用門檻:MyBatis 入門比較簡單,使用門檻也更低。
  • 二級快取:hibernate 擁有更好的二級快取,它的二級快取可以自行更換爲第三方的二級快取。

132. MyBatis 有哪些執行器(Executor)?

MyBatis 有三種基本的Executor執行器:

  • SimpleExecutor:每執行一次 update 或 select 就開啓一個 Statement 物件,用完立刻關閉 Statement 物件;
  • ReuseExecutor:執行 update 或 select,以 SQL 作爲 key 查詢 Statement 物件,存在就使用,不存在就建立,用完後不關閉 Statement 物件,而是放置於 Map 內供下一次使用。簡言之,就是重複使用 Statement 物件;
  • BatchExecutor:執行 update(沒有 select,jdbc 批次處理不支援 select),將所有 SQL 都新增到批次處理中(addBatch()),等待統一執行(executeBatch()),它快取了多個 Statement 物件,每個 Statement 物件都是 addBatch()完畢後,等待逐一執行 executeBatch()批次處理,與 jdbc 批次處理相同。

133. MyBatis 分頁外掛的實現原理是什麼?

分頁外掛的基本原理是使用 MyBatis 提供的外掛介面,實現自定義外掛,在外掛的攔截方法內攔截待執行的 SQL,然後重寫 SQL,根據 dialect 方言,新增對應的物理分頁語句和物理分頁參數。

134. MyBatis 如何編寫一個自定義外掛?

自定義外掛實現原理

MyBatis 自定義外掛針對 MyBatis 四大物件(Executor、StatementHandler、ParameterHandler、ResultSetHandler)進行攔截:

  • Executor:攔截內部執行器,它負責呼叫 StatementHandler 操作數據庫,並把結果集通過 ResultSetHandler 進行自動對映,另外它還處理了二級快取的操作;
  • StatementHandler:攔截 SQL 語法構建的處理,它是 MyBatis 直接和數據庫執行 SQL 指令碼的物件,另外它也實現了 MyBatis 的一級快取;
  • ParameterHandler:攔截參數的處理;
  • ResultSetHandler:攔截結果集的處理。

自定義外掛實現關鍵

MyBatis 外掛要實現 Interceptor 介面,介面包含的方法,如下:

public interface Interceptor {   
   Object intercept(Invocation invocation) throws Throwable;       
   Object plugin(Object target);    
   void setProperties(Properties properties);
}
  • setProperties 方法是在 MyBatis 進行設定外掛的時候可以設定自定義相關屬性,即:介面實現物件的參數設定;
  • plugin 方法是外掛用於封裝目標物件的,通過該方法我們可以返回目標物件本身,也可以返回一個它的代理,可以決定是否要進行攔截進而決定要返回一個什麼樣的目標物件,官方提供了範例:return Plugin. wrap(target, this);
  • intercept 方法就是要進行攔截的時候要執行的方法。

自定義外掛實現範例

官方外掛實現:

@Intercepts({@Signature(type = Executor. class, method = "query",
        args = {MappedStatement. class, Object. class, RowBounds. class, ResultHandler. class})})
public class TestInterceptor implements Interceptor {
   public Object intercept(Invocation invocation) throws Throwable {
     Object target = invocation. getTarget(); //被代理物件
     Method method = invocation. getMethod(); //代理方法
     Object[] args = invocation. getArgs(); //方法參數
     // do something . . . . . .  方法攔截前執行程式碼塊
     Object result = invocation. proceed();
     // do something . . . . . . . 方法攔截後執行程式碼塊
     return result;
   }
   public Object plugin(Object target) {
     return Plugin. wrap(target, this);
   }
}

RabbitMQ

135. RabbitMQ 的使用場景有哪些?

  • 搶購活動,削峯填谷,防止系統崩塌。
  • 延遲資訊處理,比如 10 分鐘之後給下單未付款的使用者發送郵件提醒。
  • 解耦系統,對於新增的功能可以單獨寫模組擴充套件,比如使用者確認評價之後,新增了給使用者返積分的功能,這個時候不用在業務程式碼裡新增新增積分的功能,只需要把新增積分的介面訂閱確認評價的訊息佇列即可,後面再新增任何功能只需要訂閱對應的訊息佇列即可。

136. RabbitMQ 有哪些重要的角色?

RabbitMQ 中重要的角色有:生產者、消費者和代理:

  • 生產者:訊息的建立者,負責建立和推播數據到訊息伺服器;
  • 消費者:訊息的接收方,用於處理數據和確認訊息;
  • 代理:就是 RabbitMQ 本身,用於扮演「快遞」的角色,本身不生產訊息,只是扮演「快遞」的角色。

137. RabbitMQ 有哪些重要的元件?

  • ConnectionFactory(連線管理器):應用程式與Rabbit之間建立連線的管理器,程式程式碼中使用。
  • Channel(通道):訊息推播使用的通道。
  • Exchange(交換器):用於接受、分配訊息。
  • Queue(佇列):用於儲存生產者的訊息。
  • RoutingKey(路由鍵):用於把生成者的數據分配到交換器上。
  • BindingKey(系結鍵):用於把交換器的訊息系結到佇列上。

138. RabbitMQ 中 vhost 的作用是什麼?

vhost:每個 RabbitMQ 都能建立很多 vhost,我們稱之爲虛擬主機,每個虛擬主機其實都是 mini 版的RabbitMQ,它擁有自己的佇列,交換器和系結,擁有自己的許可權機制 機製。

139. RabbitMQ 的訊息是怎麼發送的?

首先用戶端必須連線到 RabbitMQ 伺服器才能 纔能發佈和消費訊息,用戶端和 rabbit server 之間會建立一個 tcp 連線,一旦 tcp 開啓並通過了認證(認證就是你發送給 rabbit 伺服器的使用者名稱和密碼),你的用戶端和 RabbitMQ 就建立了一條 amqp 通道(channel),通道是建立在「真實」 tcp 上的虛擬連線,amqp 命令都是通過通道發送出去的,每個通道都會有一個唯一的 id,不論是發佈訊息,訂閱佇列都是通過這個通道完成的。

140. RabbitMQ 怎麼保證訊息的穩定性?

  • 提供了事務的功能。
  • 通過將 channel 設定爲 confirm(確認)模式。

141. RabbitMQ 怎麼避免訊息丟失?

  • 把訊息持久化磁碟,保證伺服器重新啓動訊息不丟失。
  • 每個叢集中至少有一個物理磁碟,保證訊息落入磁碟。

142. 要保證訊息持久化成功的條件有哪些?

  • 宣告佇列必須設定持久化 durable 設定爲 true.
  • 訊息推播投遞模式必須設定持久化,deliveryMode 設定爲 2(持久)。
  • 訊息已經到達持久化交換器。
  • 訊息已經到達持久化佇列。

以上四個條件都滿足才能 纔能保證訊息持久化成功。

143. RabbitMQ 持久化有什麼缺點?

持久化的缺地就是降低了伺服器的吞吐量,因爲使用的是磁碟而非記憶體儲存,從而降低了吞吐量。可儘量使用 ssd 硬碟來緩解吞吐量的問題。

144. RabbitMQ 有幾種廣播型別?

  • direct(預設方式):最基礎最簡單的模式,發送方把訊息發送給訂閱方,如果有多個訂閱者,預設採取輪詢的方式進行訊息發送。
  • headers:與 direct 類似,只是效能很差,此型別幾乎用不到。
  • fanout:分發模式,把消費分發給所有訂閱者。
  • topic:匹配訂閱模式,使用正則匹配到訊息佇列,能匹配到的都能接收到。

145. RabbitMQ 怎麼實現延遲訊息佇列?

延遲佇列的實現有兩種方式:

  • 通過訊息過期後進入死信交換器,再由交換器轉發到延遲消費佇列,實現延遲功能;
  • 使用 RabbitMQ-delayed-message-exchange 外掛實現延遲功能。

146. RabbitMQ 叢集有什麼用?

叢集主要有以下兩個用途:

  • 高可用:某個伺服器出現問題,整個 RabbitMQ 還可以繼續使用;
  • 高容量:叢集可以承載更多的訊息量。

147. RabbitMQ 節點的型別有哪些?

  • 磁碟節點:訊息會儲存到磁碟。
  • 記憶體節點:訊息都儲存在記憶體中,重新啓動伺服器訊息丟失,效能高於磁碟型別。

148. RabbitMQ 叢集搭建需要注意哪些問題?

  • 各節點之間使用「--link」連線,此屬性不能忽略。
  • 各節點使用的 erlang cookie 值必須相同,此值相當於「祕鑰」的功能,用於各節點的認證。
  • 整個叢集中必須包含一個磁碟節點。

149. RabbitMQ 每個節點是其他節點的完整拷貝嗎?爲什麼?

不是,原因有以下兩個:

  • 儲存空間的考慮:如果每個節點都擁有所有佇列的完全拷貝,這樣新增節點不但沒有新增儲存空間,反而增加了更多的冗餘數據;
  • 效能的考慮:如果每條訊息都需要完整拷貝到每一個叢集節點,那新增節點並沒有提升處理訊息的能力,最多是保持和單節點相同的效能甚至是更糟。

150. RabbitMQ 叢集中唯一一個磁碟節點崩潰了會發生什麼情況?

如果唯一磁碟的磁碟節點崩潰了,不能進行以下操作:

  • 不能建立佇列
  • 不能建立交換器
  • 不能建立系結
  • 不能新增使用者
  • 不能更改許可權
  • 不能新增和刪除叢集節點

唯一磁碟節點崩潰了,叢集是可以保持執行的,但你不能更改任何東西。

151. RabbitMQ 對叢集節點停止順序有要求嗎?

RabbitMQ 對叢集的停止的順序是有要求的,應該先關閉記憶體節點,最後再關閉磁碟節點。如果順序恰好相反的話,可能會造成訊息的丟失。

Kafka

152. kafka 可以脫離 zookeeper 單獨使用嗎?爲什麼?

kafka 不能脫離 zookeeper 單獨使用,因爲 kafka 使用 zookeeper 管理和協調 kafka 的節點伺服器。

153. kafka 有幾種數據保留的策略?

kafka 有兩種數據儲存策略:按照過期時間保留和按照儲存的訊息大小保留。

154. kafka 同時設定了 7 天和 10G 清除數據,到第五天的時候訊息達到了 10G,這個時候 kafka 將如何處理?

這個時候 kafka 會執行數據清除工作,時間和大小不論那個滿足條件,都會清空數據。

155. 什麼情況會導致 kafka 執行變慢?

  • cpu 效能瓶頸
  • 磁碟讀寫瓶頸
  • 網路瓶頸

156. 使用 kafka 叢集需要注意什麼?

  • 叢集的數量不是越多越好,最好不要超過 7 個,因爲節點越多,訊息複製需要的時間就越長,整個羣組的吞吐量就越低。
  • 叢集數量最好是單數,因爲超過一半故障叢集就不能用了,設定爲單數容錯率更高。

Zookeeper

157. zookeeper 是什麼?

zookeeper 是一個分佈式的,開放原始碼的分佈式應用程式協調服務,是 google chubby 的開源實現,是 hadoop 和 hbase 的重要元件。它是一個爲分佈式應用提供一致性服務的軟體,提供的功能包括:設定維護、域名服務、分佈式同步、組服務等。

158. zookeeper 都有哪些功能?

  • 叢集管理:監控節點存活狀態、執行請求等。
  • 主節點選舉:主節點掛掉了之後可以從備用的節點開始新一輪選主,主節點選舉說的就是這個選舉的過程,使用 zookeeper 可以協助完成這個過程。
  • 分佈式鎖:zookeeper 提供兩種鎖:獨佔鎖、共用鎖。獨佔鎖即一次只能有一個執行緒使用資源,共用鎖是讀鎖共用,讀寫互斥,即可以有多線執行緒同時讀同一個資源,如果要使用寫鎖也只能有一個執行緒使用。zookeeper可以對分佈式鎖進行控制。
  • 命名服務:在分佈式系統中,通過使用命名服務,用戶端應用能夠根據指定名字來獲取資源或服務的地址,提供者等資訊。

159. zookeeper 有幾種部署模式?

zookeeper 有三種部署模式:

  • 單機部署:一臺叢集上執行;
  • 叢集部署:多臺叢集執行;
  • 僞叢集部署:一臺叢集啓動多個 zookeeper 範例執行。

160. zookeeper 怎麼保證主從節點的狀態同步?

zookeeper 的核心是原子廣播,這個機制 機製保證了各個 server 之間的同步。實現這個機制 機製的協定叫做 zab 協定。 zab 協定有兩種模式,分別是恢復模式(選主)和廣播模式(同步)。當服務啓動或者在領導者崩潰後,zab 就進入了恢復模式,當領導者被選舉出來,且大多數 server 完成了和 leader 的狀態同步以後,恢復模式就結束了。狀態同步保證了 leader 和 server 具有相同的系統狀態。

161. 叢集中爲什麼要有主節點?

在分佈式環境中,有些業務邏輯只需要叢集中的某一臺機器進行執行,其他的機器可以共用這個結果,這樣可以大大減少重複計算,提高效能,所以就需要主節點。

162. 叢集中有 3 台伺服器,其中一個節點宕機,這個時候 zookeeper 還可以使用嗎?

可以繼續使用,單數伺服器只要沒超過一半的伺服器宕機就可以繼續使用。

163. 說一下 zookeeper 的通知機制 機製?

用戶端端會對某個 znode 建立一個 watcher 事件,當該 znode 發生變化時,這些用戶端會收到 zookeeper 的通知,然後用戶端可以根據 znode 變化來做出業務上的改變。

MySQL

164. 數據庫的三範式是什麼?

  • 第一範式:強調的是列的原子性,即數據庫表的每一列都是不可分割的原子數據項。
  • 第二範式:要求實體的屬性完全依賴於主關鍵字。所謂完全依賴是指不能存在僅依賴主關鍵字一部分的屬性。
  • 第三範式:任何非主屬性不依賴於其它非主屬性。

165. 一張自增表裏面總共有 7 條數據,刪除了最後 2 條數據,重新啓動 MySQL 數據庫,又插入了一條數據,此時 id 是幾?

  • 表型別如果是 MyISAM ,那 id 就是 8。
  • 表型別如果是 InnoDB,那 id 就是 6。

InnoDB 表只會把自增主鍵的最大 id 記錄在記憶體中,所以重新啓動之後會導致最大 id 丟失。

166. 如何獲取當前數據庫版本?

使用 select version() 獲取當前 MySQL 數據庫版本。

167. 說一下 ACID 是什麼?

  • Atomicity(原子性):一個事務(transaction)中的所有操作,或者全部完成,或者全部不完成,不會結束在中間某個環節。事務在執行過程中發生錯誤,會被恢復(Rollback)到事務開始前的狀態,就像這個事務從來沒有執行過一樣。即,事務不可分割、不可約簡。
  • Consistency(一致性):在事務開始之前和事務結束以後,數據庫的完整性沒有被破壞。這表示寫入的資料必須完全符合所有的預設約束、觸發器、級聯回滾等。
  • Isolation(隔離性):數據庫允許多個併發事務同時對其數據進行讀寫和修改的能力,隔離性可以防止多個事務併發執行時由於交叉執行而導致數據的不一致。事務隔離分爲不同級別,包括讀未提交(Read uncommitted)、讀提交(read committed)、可重複讀(repeatable read)和序列化(Serializable)。
  • Durability(永續性):事務處理結束後,對數據的修改就是永久的,即便系統故障也不會丟失。

168. char 和 varchar 的區別是什麼?

  • char(n) :固定長度型別,比如訂閱 char(10),當你輸入"abc"三個字元的時候,它們佔的空間還是 10 個位元組,其他 7 個是空位元組。

chat 優點:效率高;缺點:佔用空間;適用場景:儲存密碼的 md5 值,固定長度的,使用 char 非常合適。

  • varchar(n) :可變長度,儲存的值是每個值佔用的位元組再加上一個用來記錄其長度的位元組的長度。

所以,從空間上考慮 varcahr 比較合適;從效率上考慮 char 比較合適,二者使用需要權衡。

169. float 和 double 的區別是什麼?

  • float 最多可以儲存 8 位的十進制數,並在記憶體中佔 4 位元組。
  • double 最可可以儲存 16 位的十進制數,並在記憶體中佔 8 位元組。

170. MySQL 的內連線、左連線、右連線有什麼區別?

內連線關鍵字:inner join;左連線:left join;右連線:right join。

內連線是把匹配的關聯數據顯示出來;左連線是左邊的表全部顯示出來,右邊的表顯示出符合條件的數據;右連線正好相反。

171. MySQL 索引是怎麼實現的?

索引是滿足某種特定查詢演算法的數據結構,而這些數據結構會以某種方式指向數據,從而實現高效查詢數據。

具體來說 MySQL 中的索引,不同的數據引擎實現有所不同,但目前主流的數據庫引擎的索引都是 B+ 樹實現的,B+ 樹的搜尋效率,可以到達二分法的效能,找到數據區域之後就找到了完整的數據結構了,所有索引的效能也是更好的。

172. 怎麼驗證 MySQL 的索引是否滿足需求?

使用 explain 檢視 SQL 是如何執行查詢語句的,從而分析你的索引是否滿足需求。

explain 語法:explain select * from table where type=1。

173. 說一下數據庫的事務隔離?

MySQL 的事務隔離是在 MySQL. ini 組態檔裡新增的,在檔案的最後新增:

transaction-isolation = REPEATABLE-READ

可用的設定值:READ-UNCOMMITTED、READ-COMMITTED、REPEATABLE-READ、SERIALIZABLE。

  • READ-UNCOMMITTED:未提交讀,最低隔離級別、事務未提交前,就可被其他事務讀取(會出現幻讀、髒讀、不可重複讀)。
  • READ-COMMITTED:提交讀,一個事務提交後才能 纔能被其他事務讀取到(會造成幻讀、不可重複讀)。
  • REPEATABLE-READ:可重複讀,預設級別,保證多次讀取同一個數據時,其值都和事務開始時候的內容是一致,禁止讀取到別的事務未提交的數據(會造成幻讀)。
  • SERIALIZABLE:序列化,代價最高最可靠的隔離級別,該隔離級別能防止髒讀、不可重複讀、幻讀。

髒讀 :表示一個事務能夠讀取另一個事務中還未提交的數據。比如,某個事務嘗試插入記錄 A,此時該事務還未提交,然後另一個事務嘗試讀取到了記錄 A。

不可重複讀 :是指在一個事務內,多次讀同一數據。

幻讀 :指同一個事務內多次查詢返回的結果集不一樣。比如同一個事務 A 第一次查詢時候有 n 條記錄,但是第二次同等條件下查詢卻有 n+1 條記錄,這就好像產生了幻覺。發生幻讀的原因也是另外一個事務新增或者刪除或者修改了第一個事務結果集裏面的數據,同一個記錄的數據內容被修改了,所有數據行的記錄就變多或者變少了。

174. 說一下 MySQL 常用的引擎?

  • InnoDB 引擎:mysql 5.1 後預設的數據庫引擎,提供了對數據庫 acid 事務的支援,並且還提供了行級鎖和外來鍵的約束,它的設計的目標就是處理大數據容量的數據庫系統。MySQL 執行的時候,InnoDB 會在記憶體中建立緩衝池,用於緩衝數據和索引。但是該引擎是不支援全文搜尋,同時啓動也比較的慢,它是不會儲存表的行數的,所以當進行 select count(*) from table 指令的時候,需要進行掃描全表。由於鎖的粒度小,寫操作是不會鎖定全表的,所以在併發度較高的場景下使用會提升效率的。

  • MyIASM 引擎:不提供事務的支援,也不支援行級鎖和外來鍵。因此當執行插入和更新語句時,即執行寫操作的時候需要鎖定這個表,所以會導致效率會降低。不過和 InnoDB 不同的是,MyIASM 引擎是儲存了表的行數,於是當進行 select count(*) from table 語句時,可以直接的讀取已經儲存的值而不需要進行掃描全表。所以,如果表的讀操作遠遠多於寫操作時,並且不需要事務的支援的,可以將 MyIASM 作爲數據庫引擎的首選。

175. 說一下 MySQL 的行鎖和表鎖?

MyISAM 只支援表鎖,InnoDB 支援表鎖和行鎖,預設爲行鎖。

  • 表級鎖:開銷小,加鎖快,不會出現死鎖。鎖定粒度大,發生鎖衝突的概率最高,併發量最低。
  • 行級鎖:開銷大,加鎖慢,會出現死鎖。鎖力度小,發生鎖衝突的概率小,併發度最高。

176. 說一下樂觀鎖和悲觀鎖?

  • 樂觀鎖:每次去拿數據的時候都認爲別人不會修改,所以不會上鎖,但是在提交更新的時候會判斷一下在此期間別人有沒有去更新這個數據。
  • 悲觀鎖:每次去拿數據的時候都認爲別人會修改,所以每次在拿數據的時候都會上鎖,這樣別人想拿這個數據就會阻止,直到這個鎖被釋放。

數據庫的樂觀鎖需要自己實現,在表裏面新增一個 version 欄位,每次修改成功值加 1,這樣每次修改的時候先對比一下,自己擁有的 version 和數據庫現在的 version 是否一致,如果不一致就不修改,這樣就實現了樂觀鎖。

177. MySQL 問題排查都有哪些手段?

  • 使用 show processlist 命令檢視當前所有連線資訊。
  • 使用 explain 命令查詢 SQL 語句執行計劃。
  • 開啓慢查詢日誌,檢視慢查詢的 SQL。

178. 如何做 MySQL 的效能優化?

  • 爲搜尋欄位建立索引。
  • 避免使用 select *,列出需要查詢的欄位。
  • 垂直分割分表。
  • 選擇正確的儲存引擎。

Redis

179. Redis 是什麼?都有哪些使用場景?

Redis 是一個使用 C 語言開發的快取記憶體數據庫。

Redis 使用場景:

  • 記錄貼文點贊數、點選數、評論數;
  • 快取近期熱帖;
  • 快取文章詳情資訊;
  • 記錄使用者對談資訊。

180. Redis 有哪些功能?

  • 數據快取功能
  • 分佈式鎖的功能
  • 支援數據持久化
  • 支援事務
  • 支援訊息佇列

181. Redis 和 memcache 有什麼區別?

  • 儲存方式不同:memcache 把數據全部存在記憶體之中,斷電後會掛掉,數據不能超過記憶體大小;Redis 有部份存在硬碟上,這樣能保證數據的永續性。
  • 數據支援型別:memcache 對數據型別支援相對簡單;Redis 有複雜的數據型別。
  • 使用底層模型不同:它們之間底層實現方式,以及與用戶端之間通訊的應用協定不一樣,Redis 自己構建了 vm 機制 機製,因爲一般的系統呼叫系統函數的話,會浪費一定的時間去移動和請求。
  • value 值大小不同:Redis 最大可以達到 512mb;memcache 只有 1mb。

182. Redis 爲什麼是單執行緒的?

因爲 cpu 不是 Redis 的瓶頸,Redis 的瓶頸最有可能是機器記憶體或者網路頻寬。既然單執行緒容易實現,而且 cpu 又不會成爲瓶頸,那就順理成章地採用單執行緒的方案了。

關於 Redis 的效能,官方網站也有,普通筆電輕鬆處理每秒幾十萬的請求。

而且單執行緒並不代表就慢 nginx 和 nodejs 也都是高效能單執行緒的代表。

183. 什麼是快取穿透?怎麼解決?

快取穿透:指查詢一個一定不存在的數據,由於快取是不命中時需要從數據庫查詢,查不到數據則不寫入快取,這將導致這個不存在的數據每次請求都要到數據庫去查詢,造成快取穿透。

解決方案:最簡單粗暴的方法如果一個查詢返回的數據爲空(不管是數據不存在,還是系統故障),我們就把這個空結果進行快取,但它的過期時間會很短,最長不超過五分鐘。

184. Redis 支援的數據型別有哪些?

Redis 支援的數據型別:string(字串)、list(列表)、hash(字典)、set(集合)、zset(有序集合)。

185. Redis 支援的 Java 用戶端都有哪些?

支援的 Java 用戶端有 Redisson、jedis、lettuce 等。

186. jedis 和 Redisson 有哪些區別?

  • jedis:提供了比較全面的 Redis 命令的支援。
  • Redisson:實現了分佈式和可延伸的 Java 數據結構,與 jedis 相比 Redisson 的功能相對簡單,不支援排序、事務、管道、分割區等 Redis 特性。

187. 怎麼保證快取和數據庫數據的一致性?

  • 合理設定快取的過期時間。
  • 新增、更改、刪除數據庫操作時同步更新 Redis,可以使用事物機制 機製來保證數據的一致性。

188. Redis 持久化有幾種方式?

Redis 的持久化有兩種方式,或者說有兩種策略:

  • RDB(Redis Database):指定的時間間隔能對你的數據進行快照儲存。
  • AOF(Append Only File):每一個收到的寫命令都通過write函數追加到檔案中。

189. Redis 怎麼實現分佈式鎖?

Redis 分佈式鎖其實就是在系統裏面佔一個「坑」,其他程式也要佔「坑」的時候,佔用成功了就可以繼續執行,失敗了就只能放棄或稍後重試。

佔坑一般使用 setnx(set if not exists)指令,只允許被一個程式佔有,使用完呼叫 del 釋放鎖。

190. Redis 分佈式鎖有什麼缺陷?

Redis 分佈式鎖不能解決超時的問題,分佈式鎖有一個超時時間,程式的執行如果超出了鎖的超時時間就會出現問題。

191. Redis 如何做記憶體優化?

儘量使用 Redis 的雜湊表,把相關的資訊放到雜湊表裏面儲存,而不是把每個欄位單獨儲存,這樣可以有效的減少記憶體使用。比如將 Web 系統的使用者物件,應該放到雜湊表裏面再整體儲存到 Redis,而不是把使用者的姓名、年齡、密碼、郵箱等欄位分別設定 key 進行儲存。

192. Redis 淘汰策略有哪些?

  • volatile-lru:從已設定過期時間的數據集(server. db[i]. expires)中挑選最近最少使用的數據淘汰。

  • volatile-ttl:從已設定過期時間的數據集(server. db[i]. expires)中挑選將要過期的數據淘汰。

  • volatile-random:從已設定過期時間的數據集(server. db[i]. expires)中任意選擇數據淘汰。

  • allkeys-lru:從數據集(server. db[i]. dict)中挑選最近最少使用的數據淘汰。

  • allkeys-random:從數據集(server. db[i]. dict)中任意選擇數據淘汰。

  • no-enviction(驅逐):禁止驅逐數據。

193. Redis 常見的效能問題有哪些?該如何解決?

  • 主伺服器寫記憶體快照,會阻塞主執行緒的工作,當快照比較大時對效能影響是非常大的,會間斷性暫停服務,所以主伺服器最好不要寫記憶體快照。
  • Redis 主從複製的效能問題,爲了主從複製的速度和連線的穩定性,主從庫最好在同一個區域網內。

JVM

194. 說一下 JVM 的主要組成部分?及其作用?

  • 類載入器(ClassLoader)
  • 執行時數據區(Runtime Data Area)
  • 執行引擎(Execution Engine)
  • 本地庫介面(Native Interface)

元件的作用: 首先通過類載入器(ClassLoader)會把 Java 程式碼轉換成位元組碼,執行時數據區(Runtime Data Area)再把位元組碼載入到記憶體中,而位元組碼檔案只是 JVM 的一套指令集規範,並不能直接交給底層操作系統去執行,因此需要特定的命令解析器執行引擎(Execution Engine),將位元組碼翻譯成底層系統指令,再交由 CPU 去執行,而這個過程中需要呼叫其他語言的本地庫介面(Native Interface)來實現整個程式的功能。

195. 說一下 JVM 執行時數據區?

不同虛擬機器的執行時數據區可能略微有所不同,但都會遵從 Java 虛擬機器規範, Java 虛擬機器規範規定的區域分爲以下 5 個部分:

  • 程式計數器(Program Counter Register):當前執行緒所執行的位元組碼的行號指示器,位元組碼解析器的工作是通過改變這個計數器的值,來選取下一條需要執行的位元組碼指令,分支、回圈、跳轉、例外處理、執行緒恢復等基礎功能,都需要依賴這個計數器來完成;

  • Java 虛擬機器棧(Java Virtual Machine Stacks):用於儲存區域性變數表、運算元棧、動態鏈接、方法出口等資訊;

  • 本地方法棧(Native Method Stack):與虛擬機器棧的作用是一樣的,只不過虛擬機器棧是服務 Java 方法的,而本地方法棧是爲虛擬機器呼叫 Native 方法服務的;

  • Java 堆(Java Heap):Java 虛擬機器中記憶體最大的一塊,是被所有執行緒共用的,幾乎所有的物件範例都在這裏分配記憶體;

  • 方法區(Methed Area):用於儲存已被虛擬機器載入的類資訊、常數、靜態變數、即時編譯後的程式碼等數據。

196. 說一下堆疊的區別?

  • 功能方面:堆是用來存放物件的,棧是用來執行程式的。
  • 共用性:堆是執行緒共用的,棧是執行緒私有的。
  • 空間大小:堆大小遠遠大於棧。

197. 佇列和棧是什麼?有什麼區別?

佇列和棧都是被用來預儲存數據的。

佇列允許先進先出檢索元素,但也有例外的情況,Deque 介面允許從兩端檢索元素。

棧和佇列很相似,但它執行對元素進行後進先出進行檢索。

198. 什麼是雙親委派模型?

在介紹雙親委派模型之前先說下類載入器。對於任意一個類,都需要由載入它的類載入器和這個類本身一同確立在 JVM 中的唯一性,每一個類載入器,都有一個獨立的類名稱空間。類載入器就是根據指定全限定名稱將 class 檔案載入到 JVM 記憶體,然後再轉化爲 class 物件。

類載入器分類:

  • 啓動類載入器(Bootstrap ClassLoader),是虛擬機器自身的一部分,用來載入Java_HOME/lib/目錄中的,或者被 -Xbootclasspath 參數所指定的路徑中並且被虛擬機器識別的類庫;
  • 其他類載入器:
  • 擴充套件類載入器(Extension ClassLoader):負責載入<java_home style="box-sizing: border-box; outline: 0px !important;">\lib\ext目錄或Java. ext. dirs系統變數指定的路徑中的所有類庫;
  • 應用程式類載入器(Application ClassLoader)。負責載入使用者類路徑(classpath)上的指定類庫,我們可以直接使用這個類載入器。一般情況,如果我們沒有自定義類載入器預設就是用這個載入器。

雙親委派模型:如果一個類載入器收到了類載入的請求,它首先不會自己去載入這個類,而是把這個請求委派給父類別載入器去完成,每一層的類載入器都是如此,這樣所有的載入請求都會被傳送到頂層的啓動類載入器中,只有當父載入無法完成載入請求(它的搜尋範圍中沒找到所需的類)時,子載入器纔會嘗試去載入類。

199. 說一下類裝載的執行過程?

類裝載分爲以下 5 個步驟:

  • 載入:根據查詢路徑找到相應的 class 檔案然後匯入;
  • 檢查:檢查載入的 class 檔案的正確性;
  • 準備:給類中的靜態變數分配記憶體空間;
  • 解析:虛擬機器將常數池中的符號參照替換成直接參照的過程。符號參照就理解爲一個標示,而在直接參照直接指向記憶體中的地址;
  • 初始化:對靜態變數和靜態程式碼塊執行初始化工作。

200. 怎麼判斷物件是否可以被回收?

一般有兩種方法來判斷:

  • 參照計數器:爲每個物件建立一個參照計數,有物件參照時計數器 +1,參照被釋放時計數 -1,當計數器爲 0 時就可以被回收。它有一個缺點不能解決回圈參照的問題;
  • 可達性分析:從 GC Roots 開始向下搜尋,搜尋所走過的路徑稱爲參照鏈。當一個物件到 GC Roots 沒有任何參照鏈相連時,則證明此物件是可以被回收的。

201. Java 中都有哪些參照型別?

  • 強參照:發生 gc 的時候不會被回收。
  • 軟參照:有用但不是必須的物件,在發生記憶體溢位之前會被回收。
  • 弱參照:有用但不是必須的物件,在下一次GC時會被回收。
  • 虛參照(幽靈參照/幻影參照):無法通過虛參照獲得物件,用 PhantomReference 實現虛參照,虛參照的用途是在 gc 時返回一個通知。

202. 說一下 JVM 有哪些垃圾回收演算法?

  • 標記-清除演算法:標記無用物件,然後進行清除回收。缺點:效率不高,無法清除垃圾碎片。
  • 標記-整理演算法:標記無用物件,讓所有存活的物件都向一端移動,然後直接清除掉端邊界以外的記憶體。
  • 複製演算法:按照容量劃分二個大小相等的記憶體區域,當一塊用完的時候將活着的物件複製到另一塊上,然後再把已使用的記憶體空間一次清理掉。缺點:記憶體使用率不高,只有原來的一半。
  • 分代演算法:根據物件存活週期的不同將記憶體劃分爲幾塊,一般是新生代和老年代,新生代基本採用複製演算法,老年代採用標記整理演算法。

203. 說一下 JVM 有哪些垃圾回收器?

  • Serial:最早的單執行緒序列垃圾回收器。
  • Serial Old:Serial 垃圾回收器的老年版本,同樣也是單執行緒的,可以作爲 CMS 垃圾回收器的備選預案。
  • ParNew:是 Serial 的多執行緒版本。
  • Parallel 和 ParNew 收集器類似是多執行緒的,但 Parallel 是吞吐量優先的收集器,可以犧牲等待時間換取系統的吞吐量。
  • Parallel Old 是 Parallel 老生代版本,Parallel 使用的是複製的記憶體回收演算法,Parallel Old 使用的是標記-整理的記憶體回收演算法。
  • CMS:一種以獲得最短停頓時間爲目標的收集器,非常適用 B/S 系統。
  • G1:一種兼顧吞吐量和停頓時間的 GC 實現,是 JDK 9 以後的預設 GC 選項。

204. 詳細介紹一下 CMS 垃圾回收器?

CMS 是英文 Concurrent Mark-Sweep 的簡稱,是以犧牲吞吐量爲代價來獲得最短回收停頓時間的垃圾回收器。對於要求伺服器響應速度的應用上,這種垃圾回收器非常適合。在啓動 JVM 的參數加上「-XX:+UseConcMarkSweepGC」來指定使用 CMS 垃圾回收器。

CMS 使用的是標記-清除的演算法實現的,所以在 gc 的時候回產生大量的記憶體碎片,當剩餘記憶體不能滿足程式執行要求時,系統將會出現 Concurrent Mode Failure,臨時 CMS 會採用 Serial Old 回收器進行垃圾清除,此時的效能將會被降低。

205. 新生代垃圾回收器和老生代垃圾回收器都有哪些?有什麼區別?

  • 新生代回收器:Serial、ParNew、Parallel Scavenge
  • 老年代回收器:Serial Old、Parallel Old、CMS
  • 整堆回收器:G1

新生代垃圾回收器一般採用的是複製演算法,複製演算法的優點是效率高,缺點是記憶體利用率低;老年代回收器一般採用的是標記-整理的演算法進行垃圾回收。

206. 簡述分代垃圾回收器是怎麼工作的?

分代回收器有兩個分割區:老生代和新生代,新生代預設的空間佔比總空間的 1/3,老生代的預設佔比是 2/3。

新生代使用的是複製演算法,新生代裡有 3 個分割區:Eden、To Survivor、From Survivor,它們的預設佔比是 8:1:1,它的執行流程如下:

  • 把 Eden + From Survivor 存活的物件放入 To Survivor 區;
  • 清空 Eden 和 From Survivor 分割區;
  • From Survivor 和 To Survivor 分割區交換,From Survivor 變 To Survivor,To Survivor 變 From Survivor。

每次在 From Survivor 到 To Survivor 移動時都存活的物件,年齡就 +1,當年齡到達 15(預設設定是 15)時,升級爲老生代。大物件也會直接進入老生代。

老生代當空間佔用到達某個值之後就會觸發全域性垃圾收回,一般使用標記整理的執行演算法。以上這些循環往復就構成了整個分代垃圾回收的整體執行流程。

207. 說一下 JVM 調優的工具?

JDK 自帶了很多監控工具,都位於 JDK 的 bin 目錄下,其中最常用的是 jconsole 和 jvisualvm 這兩款檢視監控工具。

  • jconsole:用於對 JVM 中的記憶體、執行緒和類等進行監控;
  • jvisualvm:JDK 自帶的全能分析工具,可以分析:記憶體快照、執行緒快照、程式死鎖、監控記憶體的變化、gc 變化等。

208. 常用的 JVM 調優的參數都有哪些?

  • -Xms2g:初始化推大小爲 2g;
  • -Xmx2g:堆最大記憶體爲 2g;
  • -XX:NewRatio=4:設定年輕的和老年代的記憶體比例爲 1:4;
  • -XX:SurvivorRatio=8:設定新生代 Eden 和 Survivor 比例爲 8:2;
  • –XX:+UseParNewGC:指定使用 ParNew + Serial Old 垃圾回收器組合;
  • -XX:+UseParallelOldGC:指定使用 ParNew + ParNew Old 垃圾回收器組合;
  • -XX:+UseConcMarkSweepGC:指定使用 CMS + Serial Old 垃圾回收器組合;
  • -XX:+PrintGC:開啓列印 gc 資訊;
  • -XX:+PrintGCDetails:列印 gc 詳細資訊。

 

 轉載地址 :https://www.cnblogs.com/bailing80/p/11443409.html