一. java基礎篇
1.final 關鍵字的作用?
- 被 final 修飾的類不可以被繼承。
- 被 final 修飾的方法不可以被重寫。
- 被 final 修飾的變量不可以被改變,如果修飾引用,那么表示引用不可變,引用指向的內容可變。
- 被 final 修飾的方法,JVM 會嘗試將其內聯,以提高運行效率,被 final 修飾的變量,在編譯階段會存入常量池中。
2.abstract class 和 interface 有什么區別?
- 聲明方法的存在而不去實現它的類被叫做抽象類(abstract class),它用于要創建一個體現某些基本行為的類,并為該類聲明方法,但不能在該類中實現該類的情況。不能創建 abstract 類的實例。然而可以創建一個變量,其類型是一個抽象類,并讓它指向具體子類的一個實例。不能有抽象構造函數或抽象靜態方法。Abstract 類的子類為它們父類中的所有抽象方法提供實現,否則它們也是抽象類行為。取而代之,在子類中實現該方法。知道其行為的其它類可以在類中實現這些方法。
- 接口(interface)是抽象類的變體。在接口中,所有方法都是抽象的。多繼承性可通過實現這樣的接口而獲得。接口中的所有方法都是抽象的,沒有一個有程序體。接口只可以定義 static final 成員變量。接口的實現與子類相似,除了該實現類不能從接口定義中繼承行為。當類實現特殊接口時,它定義(即將程序體給予)所有這種接口的方法。然后,它可以在實現了該接口的類的任何對象上調用接口的方法。由于有抽象類,它允許使用接口名作為引用變量的類型。通常的動態聯編將生效。引用可以轉換到接口類型或從接口類型轉換, instanceof 運算符可以用來決定某對象的類是否實現了接口。
3. Java 集合類:list、set、queue、map、stack 的特點與用法?
- Map
-
Map 是鍵值對,鍵 Key 是唯一不能重復的,一個鍵對應一個值,值可以重復。
-
TreeMap 可以保證順序,HashMap 不保證順序,即為無序的。
-
Map 中可以將 Key 和 Value 單獨抽取出來,其中 KeySet()方法可以將所有的 keys 抽取成一個 Set,而 Values()方法可以將 map 中所有的 values 抽取成一個集合。
-
- Set
- 不包含重復元素的集合,set 中最多包含一個 null 元素,只能用 Iterator 實現單向遍歷, Set 中沒有同步方法。
- List
- 有序的可重復集合,可以在任意位置增加刪除元素,用 Iterator 實現單向遍歷,也可用 ListIterator 實現雙向遍歷。
- Queue
- Queue 遵從先進先出原則,使用時盡量避免 add()和 remove()方法,而是使用 offer()來添加元素,使用 poll()來移除元素,它的優點是可以通過返回值來判斷是否成功,LinkedList實現了 Queue 接口,Queue 通常不允許插入 null 元素。
- Stack
- Stack 遵從后進先出原則,Stack 繼承自 Vector,它通過五個操作對類 Vector 進行擴展允許將向量視為堆棧,它提供了通常的 push 和 pop 操作,以及取堆棧頂點的 peek()方法、測試堆棧是否為空的 empty 方法等。
- 用法
- 如果涉及堆棧,隊列等操作,建議使用 List。
- 對于快速插入和刪除元素的,建議使用 LinkedList。如果需要快速隨機訪問元素的,建議使用ArrayList。
4.說出 ArrayList,Vector,LinkedList 的存儲性能和特性?
- ArrayList 和 Vector 都是使用數組方式存儲數據,此數組元素數大于實際存儲的數據以便增加和插入元素,它們都允許直接按序號索引元素,但是插入元素要涉及數組元素移動等內存操作,所以索引數據快而插入數據慢,Vector 由于使用了 synchronized 方法(線程安全),通常性能上較 ArrayList 差,而 LinkedList 使用雙向鏈表實現存儲,按序號索引數據需要進行前向或后向遍歷,但是插入數據時只需要記錄本項的前后項即可,所以插入速度較快。
5.內存泄漏和內存溢出?
-
內存泄漏(memoryleak),是指應用程序在申請內存后,無法釋放已經申請的內存空間,一次內存泄漏危害可以忽略,但如果任其發展最終會導致內存溢出(outofmemory)。如讀取文件后流要進行及時的關閉以及對數據庫連接的釋放。
-
內存溢出(outofmemory)是指應用程序在申請內存時,沒有足夠的內存空間供其使用。如我們在項目中對于大批量數據的導入,采用分批量提交的方式。
6. 反射中,Class.forName()和 ClassLoader.loadClass()的區別?
-
Class.forName(className) 方 法 , 內 部 實 際 調 用 的 方 法 是Class.forName(className,true,classloader); 第 2 個 boolean 參數表示類是否需要初始化, Class.forName(className)默認是需要初始化。
-
一旦初始化,就會觸發目標對象的 static 塊代碼執行, static 參數也會被再次初始化 ,ClassLoader.loadClass(className) 方 法 , 內 部 實 際 調 用 的 方 法 是ClassLoader.loadClass(className,false);第 2 個 boolean 參數表示目標對象是否進行鏈接, false 表示不進行鏈接,由上面介紹可以,不進行鏈接意味著不進行包括初始化等一些列步驟,那么靜態塊和靜態對象就不會得到執行。
7. int 和 Integer 的區別?
- Integer 是 int 的包裝類型,在拆箱和裝箱中,二者自動轉換.int 是基本類型,直接存數值;而 integer 是對象;用一個引用指向這個對象.由于 Integer 是一個對象,在 JVM 中對象需要一定的數據結構進行描述,相比 int 而言,其占用的內存更大一些.
8. String、StringBuilder、StringBuffer 區別?
String | 字符串常量 | 不可變 | 使用字符串拼接時是不同的 2 個空間 | |
---|---|---|---|---|
StringBuffer | 字符串變量 | 可變 | 線程安全 | 字符串拼接直接在字符串后追加 |
StringBuilder 字符串變量 | 可變 | 非線程安全 | 字符串拼接直接在字符串后追加 |
- StringBuilder 執行效率高于 StringBuffer 高于 String.
- String 是一個常量,是不可變的,所以對于每一次+=賦值都會創建一個新的對象,StringBuffer 和 StringBuilder 都是可變的,當進行字符串拼接時采用 append 方法,在原來的基礎上進行追加,所以性能比 String 要高,又因為 StringBuffer是線程安全的,而 StringBuilder 是線程非安全的,所以 StringBuilder 的效率高于 StringBuffer.
- 對于大數據量的字符串的拼接,采用 StringBuffer,StringBuilder.
9. Hashtable 和 Hashmap 的區別?
- 1、HashTable 線程安全,HashMap 非線程安全;
- 2、Hashtable 不允許 null 值(key 和 value 都不可以),HashMap 允許 null 值(key 和 value 都可以)。
兩者的遍歷方式大同小異,Hashtable 僅僅比 HashMap 多一個 elements 方法。
10. 說幾個常見的編譯時異常?
- SQLException 提供有關數據庫訪問錯誤或其他錯誤的信息的異常;
- IOException 表示發生了某種 I / O 異常的信號,此類是由失敗或中斷的 I / O 操作產生的;
- FileNotFoundException 表示當試圖打開指定路徑名表示的文件失敗時,會拋出此異常;
- ClassNotFoundException 找不到具有指定名稱的類的定義時,會拋出此異常;
- EOFException 當輸入過程中意外到達文件或流的末尾時,拋出此異常。
11. 方法重載的規則?
- 方法名一致,參數列表中參數的順序,類型,個數不同。
- 重載與方法的返回值無關,存在于父類和子類中,同類中可以拋出不同的異常,可以有不同修飾符。
12. 方法重寫的規則?
- 方法名、參數列表、返回值類型必須完全一致,構造方法不能被重寫。
- 聲明為 final 的方法不能被重寫。
- 聲明為 static 的方法不存在重寫(重寫和多態聯合才有意義)。
- 訪問權限不能比父類更低。
- 重寫之后的方法不能拋出更寬泛的異常。
13. throw 和 throws 的區別?
- throw:
throw 語句用在方法體內,表示拋出異常,由方法體內的語句處理。throw 是具體向外拋出異常的動作,所以它拋出的是一個異常實例,執行 throw 一定是拋出了某種異常。 - throws:
throws 語句是用在方法聲明后面,表示如果拋出異常,由該方法的調用者來進行異常的處理。throws 主要是聲明這個方法會拋出某種類型的異常,讓它的使用者要知道需要捕獲的異常類型,throws 表示出現異常的一種可能性,并不一定會發生這種異常。
14. 抽象類和接口的區別?
- 接口中所有的方法隱含的都是抽象的,而抽象類則可以同時包含抽象和非抽象的方法。
- 類可以實現很多個接口,但是只能繼承一個抽象類。
- 類如果要實現一個接口,它必須要實現接口聲明的所有方法。但是,類可以不實現抽象類聲明的所有方法,當然,在這種情況下,類也必須得聲明成是抽象的。
- 抽象類可以在不提供接口方法實現的情況下實現接口。
- Java 接口中聲明的變量默認都是 final 的。抽象類可以包含非 final 的變量。
- Java 接口中的成員函數默認是 public 的。抽象類的成員函數可以是 private,protecte或者是 public 。
- 接口是絕對抽象的,不可以被實例化(java 8 已支持在接口中實現默認的方法)。抽象類也不可以被實例化,但是,如果它包含 main 方法的話是可以被調用的。
15. Java 的基礎類型和字節大小?
byte | short | int | long | float | double | char | boolean |
---|---|---|---|---|---|---|---|
1個字節8位 | 2個字節16 位 | 4個字節32位 | 8個字節64 位 | 4個字節32位 | 8個字節64位 | 2個字節16位 | 8分之1個字節1位 |
16. 四個訪問修飾符的訪問級別?
從高到低:public、protected、 沒有訪問修飾符(默認)、private
17. String 和 StringBuffer 的區別?
- String 和 StringBuffer 主要區別是性能:String 是不可變對象,每次對 String 類型進行操作都等同于產生了一個新的 String 對象,然后指向新的 String 對象,所以盡量不要對 String 進行大量的拼接操作,否則會產生很多臨時對象,導致 GC 開始工作,影響系統性能。
- StringBuffer 是對象本身操作,而不是產生新的對象,因此在有大量拼接的情況下,我們建議使用StringBuffer(線程安全).
18. HashSet 的底層實現是什么?
- HashSet 的實現是依賴于 HashMap 的,HashSet 的值都是存儲在 HashMap 中的,在 HashSet 的構造法中會初始化一個 HashMap 對象,HashSet 不允許值重復。因此,HashSet 的值是作為 HashMap 的 key 存儲在 HashMap 中的,當存儲的值已經存在時返回 false。
19. 抽象類的作用?
抽象類的意義可以用三句話來概括:
- 1、為其他子類提供一個公共的類型;
- 2、封裝子類中重復定義的內容;
- 3、定義抽象方法,子類雖然有不同的實現,但是定義時一致的。
20. 為什么重寫 equals 時必須重寫 hashCode 方法?
- hashCode() 的作用是獲取哈希碼,也稱為散列碼;它實際上是返回一個 int 整數。這個哈希碼的作用是確定該對象在哈希表中的索引位置。如果兩個對象相等,則 hashcode 一定也是相同的,如果兩個對象相等,對兩個對象分別調用 equals 方法都返回 true ,如果兩個對象有相同的 hashcode 值,它們也不一定是相等的,因此,equals 方法被覆蓋過,則 hashCode 方法也必須被覆蓋。hashCode()的默認行為是對堆上的對象產生獨特值。如果沒有重寫 hashCode(),則該 class 的兩個對象無論如何都不會相等(即使這兩個對象指向相同的數據).
21. HashSet 和 TreeSet 有什么區別?
- HashSet 是由一個 hash 表來實現的,因此,它的元素是無序的,add(),remove(),contains() 方法的時間復雜度是 O(1)。
- TreeSet 是由一個樹形的結構來實現的,因此,它里面的元素是有序的,add(),remove(),contains()方法的時間復雜度是 O(logn)。
22. 強引用和軟引用和弱引用以及虛引用?
- 強引用
最普遍的一種引用方式,如 String s = “abc”,變量 s 就是字符串“abc”的強引用,只要強引用存在,則垃圾回收器就不會回收這個對象。 - 軟引用(SoftReference)
用于描述還有用但非必須的對象,如果內存足夠,不回收,如果內存不足,則回收。一般用于實現內存敏感的高速緩存,軟引用可以和引用隊列 ReferenceQueue 聯合使用,如果軟引用的對象被垃圾回收,JVM 就會把這個軟引用加入到與之關聯的引用隊列中。 - 弱引用(WeakReference)
弱引用和軟引用大致相同,弱引用與軟引用的區別在于:只具有弱引用的對象擁有更短暫的生命周期。在垃圾回收器線程掃描它所管轄的內存區域的過程中,一旦發現了只具有弱引用的對象,不管當前內存空間足夠與否,都會回收它的內存。 - 虛引用(PhantomReference)
就是形同虛設,與其他幾種引用都不同,虛引用并不會決定對象的生命周期。如果一個對象僅持有虛引用,那么它就和沒有任何引用一樣,在任何時候都可能被垃圾回收器回收。 虛引用主要用來跟蹤對象被垃圾回收器回收的活動。
虛引用與軟引用和弱引用的一個區別在于:
虛引用必須和引用隊列 (ReferenceQueue)聯合使用。當垃圾回收器準備回收一個對象時,如果發現它還有虛引用,就會在回收對象的內存之前,把這個虛引用加入到與之關聯的引用隊列中。
23. 數組在內存中如何分配?
- 當一個對象使用 new 關鍵字創建的時候,會在堆上分配內存空間,然后才返回到對象的引用。這對數組來說也是一樣的,因為數組也是一個對象,簡單的值類型的數組,每個數組成員是一個引用(指針)引用到棧上的空間。
24. Java 中怎么創建一個不可變對象?
-
對象的狀態在構造函數之后都不能被修改,任何修改應該通過創建一個新對象來實現。
-
所有的對象屬性應該都設置為 final。
-
對象創建要正確,例如:對象的應用不能在構造函數中被泄露出去。
-
對象要設置為 final,確保不要繼承的 Class 修改了 immutability 特性。
25. Java 中 ++ 操作符是線程安全的嗎?
- 不是線程安全的操作,因為它涉及到多個指令,如讀取變量值,增加,然后存儲回內存,這個過程可能會出現多個線程交差。
26. new 一個對象的過程和 clone 一個對象的過程?
- new 操作符的本意是分配內存。程序執行到 new 操作符時,首先去看 new 操作符后面的類型,因為知道了類型,才能知道要分配多大的內存空間。分配完內存之后,再調用構造函數,填充對象的各個域,這一步叫做對象的初始化,構造方法返回后,一個對象創建完畢,可以把他的引用(地址)發布到外部,在外部就可以使用這個引用操縱這個對象。 clone 在第一步是和 new 相似的,都是分配內存,調用 clone 方法時,分配的內存和原對象(即調用 clone 方法的對象)相同,然后再使用原對象中對應的各個域,填充新對象的域,填充完成之后,clone 方法返回,一個新的相同的對象被創建,同樣可以把這個新對象的引用發布到外部。
- 想要完成clone,需要兩步:(1)重寫Object中的clone方法,放大權限;(2)實現Cloneable接口。
- 淺克隆:只clone這個對象本身,對象中成員變量應用的對象不去clone。
- 深克隆:不光clone這個對象,還要clone對象內引用的對象(需要自己去做)。
27. Java 中 == 和 equals()的區別?
- 使用 == 比較原生類型如:boolean、int、char 等等,使用 equals()比較對象。
- 1、== 是判斷兩個變量或實例是不是指向同一個內存空間,equals 是判斷兩個變量或實例所指向的內存空間的值是不是相同。
- 2、== 是指對內存地址進行比較,equals() 是指對字符串的內容進行比較。
- 3、== 指的是引用是否相同,equals()指的是值是否相同。
28. final、finalize 和 finally 的不同之處?
- final 用于聲明屬性,方法和類,分別表示屬性不可變,方法不可覆蓋,類不可繼承。
- finally 是異常處理語句結構的一部分,表示總是執行。
- finalize 是 Object 類的一個方法,在垃圾收集器執行的時候會調用被回收對象的此方法,可以覆蓋此方法提供垃圾收集時的其他資源回收,例如關閉文件等。
29. Java 的多態表現在哪里?
- 多態要有動態綁定,否則就不是多態,方法重載也不是多態(因為方法重載是編譯期決定好的,沒有后期也就是運行期的動態綁定)當滿足這三個條件:
- 1、子父類繼承關系;
- 2、要有重寫方法;
- 3、要有父類引用指向子類對象。
30. 靜態類型有什么特點?
-
靜態屬性:隨著類的加載而加載,該屬性不再屬于某個對象,而是屬于整個類;
-
靜態方法:直接用類名調用,靜態方法中不能訪問非靜態成員變量;
-
靜態類:不能直接創建對象,也不能被繼承。
31.Java 創建對象的幾種方式?
- new 創建新對象;
- 通過反射機制;
- 采用 clone 機制;
- 通過序列化機制。
32.Object 中有哪些公共方法?
- Object 是所有類的父類,任何類都默認繼承 Object clone 保護方法,從而實現對象的淺復制,只有實現了 Cloneable 接口才可以調用該方法,否則會拋出 CloneNotSupportedException 異常。
- equals 在 Object 中與 == 是一樣的,子類一般需要重寫該方法。
- hashCode 該方法用于哈希查找,重寫了 equals 方法一般都要重寫 hashCode 方法,這個方法在一些具有哈希功能的 Collection 中用得到。
- getClass 方法,獲得運行時類型 。
- wait 使當前線程等待該對象的鎖,當前線程必須是該對象的擁有者,也就是具有該對象的鎖。
- wait() 方法一直等待,直到獲得鎖或者被中斷。
- wait(long timeout) 設定一個超時間隔,如果在規定時間內沒有獲得鎖就返回。
33.&和&&的區別?
- &是位運算符,表示按位與運算,兩邊都要判斷。
- &&是邏輯運算符,表示邏輯與(and)運算,如果一邊不成功,另一邊就不需要再比較了。
34.在.java 源文件中可以有多個類嗎(內部類除外)?
- 一個.java 源文件中可以包括多個類(內部類除外),但是單個文件中只能有一個 public 類,并且該 public 類必須與文件名相同。
35.如何正確的退出多層嵌套循環?
-
break;
-
使用別名。
36.內部類有什么作用?
-
內部類可以很好的實現隱藏,一般的非內部類,是不允許有 private 與 protected 權限的,但內部類可以。
-
內部類擁有外部類的所有元素的訪問權限。
-
可以實現多重繼承。
-
可以避免修改接口而實現同一個類中兩種同名方法的調用。
37.深拷貝和淺拷貝的區別是什么?
-
淺拷貝:被復制對象的所有變量都含有與原來的對象相同的值,而所有的對其他對象的引用仍然指向原來的對象.換言之,淺拷貝僅僅復制所考慮的對象,而不復制它所引用的對象。
-
深拷貝:被復制對象的所有變量都含有與原來的對象相同的值,而那些引用其他對象的變量將指向被復制過的新對象,而不再是原有的那些被引用的對象,換言之,深拷貝把要復制的對象所引用的對象都復制了一遍。
38.String 是基本數據類型嗎?
- 基本數據類型包括 byte、short、int、long、float、double、char、boolean,java.lang.String 類是 final 類型的,因此不可以繼承這個類,也不能修改這個類,為了提高效率節省空間,我們應該用 StringBuffer 類。
39. static 的用法?
- Static 可以修飾內部類、方法、變量、代碼塊,Static 修飾的類是靜態內部類,Static 修飾的方法是靜態方法,表示該方法屬于當前類的,而不屬于某個對象的,靜態方法也不能被重寫,可以直接使用類名來調用。在 static 方法中不能使用 this 或者 super 關鍵字。
- Static 修飾變量是靜態變量或者叫類變量,靜態變量被所有實例所共享,不會依賴于對象。靜態變量在內存中只有一份拷貝,在 JVM 加載類的時候,只為靜態分配一次內存。
- Static 修飾的代碼塊叫靜態代碼塊,通常用來做程序優化的,靜態代碼塊中的代碼在整個類加載的時候只會執行一次,靜態代碼塊可以有多個,如果有多個,按照先后順序依次執行。
40. 什么是值傳遞和引用傳遞?
- 基本數據類型傳遞值,引用數據類型傳遞地址;
- 基本數據類型之間的傳遞就是值傳遞,引用數據類型之間的傳遞就是引用傳遞。
41. 重載和重寫的區別?
- 方法的重寫 Overriding 和重載 Overloading 是 Java 多態性的不同表現。重寫 Overriding 是父類與子類之間多態性的一種表現,重載 Overloading 是一個類中多態性的一種表現。
- 如果在子類中定義某方法與其父類有相同的名稱和參數,我們說該方法被重寫 (Overriding)。子類的對象使用這個方法時,將調用子類中的定義,對它而言,父類中的定義如同被“屏蔽”了。
- 如果在一個類中定義了多個同名的方法,它們或有不同的參數個數或有不同的參數類型,則稱為方法的重載(Overloading)。
42. 成員變量和局部變量的區別有哪些?
- 從語法形式上,看成員變量是屬于類的,而局部變量是在方法中定義的變量或是方法的參數,成員變量可以被 public,private,static 等修飾符所修飾,而局部變量不能被訪問控制修飾符及 static 所修飾,成員變量和局部變量都能被 final 所修飾。
- 從變量在內存中的存儲方式來看,成員變量是對象的一部分,而對象存在于堆內存,局部變量存在于棧內存
- 從變量在內存中的生存時間上看,成員變量是對象的一部分,它隨著對象的創建而存在,而局部變量隨著方法的調用而自動消失。
- 成員變量如果沒有被賦初值,則會自動以類型的默認值而賦值(一種情況例外被 final 修飾但沒有被 static 修飾的成員變量必須顯示地賦值),而局部變量則不會自動賦值。
43. 靜態方法和實例方法有何不同?
靜態方法和實例方法的區別主要體現在兩個方面:
- 在外部調用靜態方法時,可以使用 “類名.方法名” 的方式,也可以使用 “對象名.方法名” 的方式,而實例方法只有后面這種方式,也就是說,調用靜態方法可以無需創建對象。
- 靜態方法在訪問本類的成員時,只允許訪問靜態成員(即靜態成員變量和靜態方法),而不允許訪問實例成員變量和實例方法;實例方法則無此限制
44.什么是多態?
允許不同類的對象對同一消息做出響應,即同一消息可以根據發送對象的不同而采用多種不同的行為方式(發送消息就是函數調用)。
- 1、子父類繼承關系;
- 2、要有重寫方法;
- 3、要有父類引用指向子類對象。
45.多態的優點?
- 可替換性(substitutability),多態對已存在代碼具有可替換性。例如,多態對圓 Circle 類工作,對其他任何圓形幾何體,如圓環,也同樣工作。
- 可擴充性(extensibility),多態對代碼具有可擴充性。增加新的子類不影響已存在類的多態性、繼承性,以及其他特性的運行和操作,實際上新加子類更容易獲得多態功能。
46. 多態存在的三個必要條件?
- 1、子父類繼承關系;
- 2、要有重寫方法;
- 3、要有父類引用指向子類對象。
47. TreeMap、HashMap、LindedHashMap 的區別?
- LinkedHashMap 可以保證 HashMap 集合有序,存入的順序和取出的順序一致。
- TreeMap 實現 SortMap 接口,能夠把它保存的記錄根據鍵排序,默認是按鍵值的升序排序,也可以指定排序的比較器,當用 Iterator 遍歷 TreeMap 時,得到的記錄是排過序的。
- HashMap 不保證順序,即為無序的,具有很快的訪問速度,HashMap 最多只允許一條記錄的鍵為 Null,允許多條記錄的值為 Null,HashMap 不支持線程的同步,是線程不安全的。
48.Java(OOP)面向對象的特征有哪些方面?
-
抽象:抽象是將一類對象的共同特征總結出來構造類的過程,包括數據抽象和行為抽象兩方面。抽象只關注對象有哪些屬性和行為,并不關注這些行為的細節是什么。
-
繼承:繼承是從已有類得到繼承信息創建新類的過程。提供繼承信息的類被稱為父類(超類、基類);得到繼承信息的類被稱為子類(派生類)。繼承讓變化中的軟件系統有了一定的延續性,同時繼承也是封裝程序中可變因素的重要手段。
-
封裝:通常認為封裝是把數據和操作數據的方法綁定起來,對數據的訪問只能通過已定義的接口。面向對象的本質就是將現實世界描繪成一系列完全自治、封閉的對象。我們在類中編寫的方法就是對實現細節的一種封裝;我們編寫一個類就是對數據和數據操作的封裝。可以說,封裝就是隱藏一切可隱藏的東西,只向外界提供最簡單的編程接口。
-
多態性:多態性是指允許不同子類型的對象對同一消息作出不同的響應。簡單的說就是用同樣的對象引用調用同樣的方法但是做了不同的事情。多態性分為編譯時的多態性和運行時的多態性。如果將對象的方法視為對象向外界提供的服務,那么運行時的多態性可以解釋為:當 A 系統訪問 B 系統提供的服務時,B 系統有多種提供服務的方式,但一切對 A 系統來說都是透明的。方法重載(overload)實現的是編譯時的多態性(也稱為前綁定),而方法重寫(override)實現的是運行時的多態性(也稱為后綁定)。運行時的多態是面向對象最精髓的東西,要實現多態需要做兩件事:1. 方法重寫(子類繼承父類并重寫父類中已有的或抽象的方法);2. 對象造型(用父類型引用引用子類型對象,這樣同樣的引用調用同樣的方法就會根據子類對象的不同而表現出不同的行為)
49.列出一些常見的運行時異常?
- ArithmeticException(算術異常)
- ClassCastException (類轉換異常)
- IllegalArgumentException (非法參數異常)
- IndexOutOfBoundsException (下標越界異常)
- NullPointerException (空指針異常)
- SecurityException (安全異常)
50.什么是反射?
- 反射就是動態加載對象,并對對象進行剖析。在運行狀態中,對于任意一個類,都能夠知道這個類的所有屬性和方法,對于任意一個對象,都能夠調用它的任意一個方法,這種動態獲取信息以及動態調用對象方法的功能成為 Java 反射機制。
51.反射的作用?
- 在運行時能判斷任意一個對象所屬的類;
- 在運行時能構造任意一個類的對象;
- 在運行時能判斷任意一個類所具有的成員變量和方法;
- 在運行時能調用任意一個對象的方法。
52.獲取 class 的三種方式?
- 通過對象調用 getClass() 方法來獲取;
- 通過類名.class 的方式來獲取;
- 通過 Class 對象的 forName()靜態方法來獲取。
53.break 和 continue 的區別?
break 和 continue 都是用來控制循環的語句。
- break 用于完全結束一個循環,跳出循環體執行循環后面的語句。
- continue 用于跳過本次循環,繼續下次循環。
54.一般異常與運行時異常有何異同?
- 一般異常表示程序運行過程中可能出現的非正常狀態。
- 運行時異常表示虛擬機的通常操作中可能遇到的異常,是一種常見運行錯誤。
- java 編譯器要求方法必須聲明拋出可能發生的非運行時異常,但是并不要求必須聲明拋出未被捕獲的運行時異常。
55.List、Set、Map 三個接口存取元素時,各有什么特點?
- List 以特定索引來存取元素,可以有重復元素;
- Set 不能存放重復元素(用對象的 equals() 方法來區分元素是否重復);
- Map 保存鍵值對(key-value pair)映射,映射關系可以是一對一或一對多。
56.Collection 和 Collections 的區別?
- Collection 是集合類的上級接口,繼承與他的接口主要有 Set 和 List;
- Collections 是針對集合類的一個幫助類,他提供一系列靜態方法實現對各種集合的搜索、排序、線程安全化等操作。
57.Error 和 Exception 有什么區別?
- Error 表示恢復不是不可能但很困難的情況下的一種嚴重問題。比如說內存溢出。不可能指望程序能處理這樣的情況。
- Exception 表示一種設計或實現問題,也就是說,它表示如果程序運行正常,就不會發生的情況。
58.EJB 的生命周期,以及如何管理事務?
- SessionBean:Stateless Session Bean 的生命周期是由容器決定的,當客戶機發出請求要建立一個 Bean 的實例時,EJB 容器不一定要創建一個新的 Bean 的實例供客戶機調用,而是隨便找一個現有的實例提供給客戶機。當客戶機第一次調用一個 Stateful Session Bean 時,容器必須立即在服務器中創建一個新的 Bean 實例,并關聯到客戶機上,以后此客戶機調用 Stateful Session Bean 的方法時容器會把調用分派到與此客戶機相關聯的 Bean 實例。EntityBean:Entity Beans 能存活相對較長的時間,并且狀態是持續的。只要數據庫中的數據存在,Entity beans 就一直存活。而不是按照應用程序或者服務進程來說的。即使 EJB
容器崩潰了,Entity beans 也是存活的。Entity Beans 生命周期能夠被容器或者 Beans 自
己管理。EJB 通過以下技術管理實務:對象管理組織(OMG)的對象實務服務(OTS),Sun Microsystems 的 Transaction Service(JTS)、Java Transaction API(JTA),開發組(X/Open)
的 XA 接口。
59.Comparable 和 Comparator 接口的區別?
- Comparable 接口只包含一個 compareTo() 方法,這個方法可以給兩個對象排序,具體來說,它返回負數,0,正數來分別表明輸入對象小于、等于、大于已經存在的對象。
- Comparator 接口包含 compare() 和 equals() 兩個方法。
60.switch 能否作用在 byte、long、string 上?
- switch 可以作用在 byte、short、int、char,并且switch 可作用于 char、byte、short、int 的包裝類上。
- switch 不可以作用于 long、float、double、boolean,包括他們的包裝類, switch 中可以是字符串類型,String(Java1.7 以后才可以作用在 String 上) switch 可以是枚舉類型(JDK1.5 之后)。
61.jdk 中哪些類是不能繼承的?
- 不能繼承的是類是那些用 final 關鍵字修飾的類。一般比較基本的類型或防止擴展類無意間破壞原來方法的實現的類型都應該是 final 的,在 jdk 中 System,String,StringBuffer 等都是基本類型。
62.Set 里的元素是不能重復的,那么用什么方法來區分重復與否呢?
- Set 里的元素是不能重復的,元素重復與否是使用 equals() 方法進行判斷的。
- equals() 和 == 方法決定引用值是否指向同一對象 equals() 在類中被覆蓋,為的是當兩個分離的對象的內容和類型相配的話,則返回真值。
63.JDK 和 JRE 的區別是什么?
- Java 運行時環境 (JRE) 是將要執行 Java 程序的 Java 虛擬機。它同時也包含了執行 applet 需要的瀏覽器插件。
- Java 開發工具包 (JDK) 是完整的 Java 軟件開發包,它包含了 JRE,編譯器和其他的工具(比如:JavaDoc,Java 調試器),可以讓開發者開發、編譯、執行 Java 應用程序。
64.是否可以在 static 環境中訪問非 static 變量?
- 不可以,static 變量在 Java 中是屬于類的,它在所有的實例中的值是一樣的。當類被 Java 虛擬機載入的時候,會對 static 變量進行初始化。如果你的代碼嘗試不用實例來訪問非 static 的變量,編譯器會報錯,因為這些變量還沒有被創建出來,還沒有跟任何實例關聯上。
65.Java 支持多繼承么?
- 不支持,Java 不支持多繼承,但是支持多層繼承。
- 每個類都只能繼承一個類,但是可以實現多個接口。
66.什么是迭代器(Iterator)?
- Iterator 接口提供了很多對集合元素進行迭代的方法,每一個集合類都包含了可以返回迭代器實例的迭代方法,迭代器可以在迭代的過程中刪除底層集合的元素。
- 克隆(cloning)或者是序列化(serialization)的語義和含義是跟具體的實現相關的,因此,應該由集合類的具體實現來決定如何被克隆或者是序列化。
67.Iterator 和 ListIterator 的區別是什么?
下面列出了它們的區別:
- Iterator 可用來遍歷 List 和 Set 集合,但是 ListIterator 只能用來遍歷 List 集合。
- Iterator 對集合只能是前向遍歷,ListIterator 既可以前向也可以后向遍歷。
- ListIterator 實現了 Iterator 接口,并包含其他的功能,比如:增加元素,替換元素,獲取前一個和后一個元素的索引等。
68.Enumeration 接口和 Iterator 接口的區別有哪些?
- Enumeration 的速度是 Iterator 的 2 倍,同時占用更少的內存。
- Iterator 遠遠比 Enumeration 安全,因為其他線程不能夠修改正在被 iterator 遍歷的集合里面的對象。
- Iterator 允許調用者刪除底層集合里面的元素,這對 Enumeration 來說是不可能的。
69.List, Set, Map 是否繼承自 Collection 接口?
- 只有 List 和 Set 接口繼承于 Collection 接口;
- Map 是與 Collection 并列的接口概念。
70.字符串常量池到底存在于內存空間的哪里?
- jdk 6.0 字符串常量池存在于方法區,方法區的具體體現可以看做是堆中的永久區。
- jdk 7.0 java 虛擬機規范中不再聲明方法區,字符串常量池存放在堆空間中。
- jdk 8.0 java 虛擬機規范中又聲明了元空間,字符串常量池存放在元空間中。
71.Java 中的編譯期常量是什么?使用它又什么風險?
- 公共靜態不可變(public static final )變量也就是我們所說的編譯期常量,這里的 public 可選的。實際上這些變量在編譯時會被替換掉,因為編譯器知道這些變量的值,并且知道這些變量在運行時不能改變。這種方式存在的一個問題是你使用了一個內部的或第三方庫中的公有編譯時常量,這個值后面被其他人改變了,但是你的客戶端仍然在使用老的值,甚至你已經部署了一個新的 jar。為了避免這種情況, 當你在更新依賴 JAR 文件時,確保重新編譯你的程序。
72.用哪兩種方式來實現集合的排序?
- 可以使用有序集合,如 TreeSet 或 TreeMap;
- 可以使用有序集合,如 list,然后通過 Collections.sort() 來排序。
73.說出 JDK 1.7 中的三個新特性?
雖然 JDK 1.7 不像 JDK 5 和 8 一樣的大版本,但是,還是有很多新的特性:
- try-with-resource 語句,在使用流或者資源的時候,不需要手動關閉,Java 會自動關閉。
- Fork-Join 池在某種程度上實現 Java 版的 Map-reduce。
- 允許 Switch 中有 String 變量和文本。
- 菱形操作符 (<>) 用于類型推斷,不再需要在變量聲明的右邊聲明泛型,因此可以寫出可讀寫性更強、更簡潔的代碼。
- 最后一個值得一提的特性是改善異常處理,如允許在同一個 catch 塊中捕獲多個異常。
74.說出 5 個 JDK 1.8 引入的新特性?
Java 8 在 Java 歷史上是一個開創新的版本,下面 JDK 8 中 5 個主要的特性:
- Lambda 表達式;
- 允許像對象一樣、傳遞匿名函數的 Stream API;
- 充分利用現代多核 CPU;
- 可以寫出更簡潔代碼的 Date 和 Time API,最終有一個穩定簡單的日期和時間庫,可供使用并擴展方法,現在接口中有靜態和默認方法;
- 重復注解,可以將相同的注解在同一類型上使用多次。
75.ArrayList 源碼分析?
- ArrayList 是一種變長的集合類,基于定長數組實現,使用默認構造方法初始化出來的容量是 10 ( 1.7 之后都是延遲初始化,即第一次調用 add 方法添加元素的時候才將 elementData 容量初始化為 10)。
- ArrayList 允許空值和重復元素,當往 ArrayList 中添加的元素數量大于其底層數組容量時,其會通過擴容機制重新生成一個更大的數組。ArrayList 擴容的長度是原長度的 1.5 倍。
- 由于 ArrayList 底層基于數組實現,所以其可以保證在 O(1) 復雜度下完成隨機查找操作。
- ArrayList 是非線程安全類,在并發環境下多個線程可同時操作 ArrayList,會引發不可預知的異常或錯誤。
- 順序添加很方便。
- 刪除和插入需要復制數組,性能差(可以使用 LinkindList)。
- Integer.MAX_VALUE - 8:主要是考慮到不同的 JVM,有的 JVM 會在加入一些數據頭,當擴容后的容量大于 MAX_ARRAY_SIZE時,我們會去比較最小需要容量和 MAX_ARRAY_SIZE 做比較,如果比它大,只能取 Integer.MAX_VALUE,否則是 Integer.MAX_VALUE -8,這個是從 jdk1.7 開始才有的。
76.HashMap 源碼分析?
- jdk1.8 之前是 list + 鏈表
- jdk1.8 之后是 list + 鏈表(當鏈表長度到 8 時,轉化為紅黑樹)
- HashMap 的擴容因子
默認 0.75,也就是會浪費 1/4 的空間,達到擴容因子時,會將 list 擴容一倍,0.75 是時間與空間一個平衡值;
77. ConcurrentHashMap 源碼分析?
- ConcurrentHashMap 使用的是鎖分段技術,首先是將數據分成一段一段的存儲,然后給每一段數據配一把鎖,當一個線程占用鎖訪問其中一段數據的時候,其他段的數據也能被其他線程訪問。有些方法需要跨段,比如 size() 和 containsValue(),它們可能需要鎖定整個表而不僅是某個段,這需要按順序鎖定所有段,操作完畢后,又按順序釋放所有段的鎖。這里“按順序”是很重要的,否則極有可能出現死鎖,在ConcurrentHashMap 內部,段數組是 final 的,并且其成員變量實際上也是 final 的,但是僅僅是將數組聲明為 final 的,并不保證數組成員也是 final 的,這需要實現以上的保證。這可以確保不會出現死鎖,因為獲得鎖的順序是固定的。
- ConcurrentHashMap 是由 Segment 數組結構和 HashEntry 數組結構組成。Segment 是一種可重入鎖 ReentrantLock,在 ConcurrentHashMap 里扮演鎖的角色,HashEntry 則用于存儲鍵值對數據。一個 ConcurrentHashMap 里包含一個 Segment 數組,Segment 的結構和 HashMap 的結構類似,是一種數組和鏈表結構, 一個 Segment 里包含一個 HashEntry 數組,每個 HashEntry 是一個鏈表結構的元素,每個 Segment 守護者一個 HashEntry 數組里的元素,當對 HashEntry 數組的數據進行修改時,必須首先獲得它對應的 Segment 鎖。
二、Java IO
1. IO 里面的常見類,字節流、字符流、接口、實現類、方法阻塞?
- 輸入流就是從外部文件輸入到內存,輸出流主要是從內存輸出到文件。
IO 里面常見的類,第一印象就只知道 IO 流中有很多類,IO 流主要分為字符流和字節流。字符流中有抽象類 InputStream 和 OutputStream,它們的子類分別為: FileInputStream,FileOutputStream,BufferedOutputStream 等。字符流 BufferedReader 和 Writer 等。都實現了 Closeable, Flushable, Appendable 這些接口。程序中的輸入輸出都是以流的形式保存的,流中保存的實際上全都是字節文件。 java 中的阻塞式方法是指在程序調用改方法時,必須等待輸入數據可用或者檢測到輸入結束或者拋出異常,否則程序會一直停留在該語句上,不會執行下面的語句。比如 read()和readLine()方法。
2. 談談對 NIO 的認知?
- 對于 NIO,它是非阻塞式,核心類。
- Buffer 為所有的原始類型提供 (Buffer) 緩存支持。
- Charset 字符集編碼解碼解決方案。
- Channel 一個新的原始 I/O 抽象,用于讀寫 Buffer 類型,通道可以認為是一種連接,可以是到特定設備,程序或者是網絡的連接。
3. 字節流和字符流的區別?
- 字符流和字節流的使用非常相似,但是實際上字節流的操作不會經過緩沖區(內存)而是直接操作文本本身的,而字符流的操作會先經過緩沖區(內存)然后通過緩沖區再操作文件以字節為單位輸入輸出數據,字節流按照 8 位傳輸以字符為單位輸入輸出數據,字符流按照 16 位傳輸。
4. NIO 和傳統的 IO 有什么區別?
-
傳統 IO 一般是一個線程等待連接,連接過來之后分配給 processor 線程,processor 線程與通道連接后如果通道沒有數據過來就會阻塞(線程被動掛起)不能做別的事情。NIO 則不同,首先,在 selector 線程輪詢的過程中就已經過濾掉了不感興趣的事件,其次,在 processor 處理感興趣事件的 read 和 write 都是非阻塞操作即直接返回的,線程沒有被掛起。
-
傳統 IO 的管道是單向的,NIO 的管道是雙向的。
-
兩者都是同步的,也就是 java 程序親力親為的去讀寫數據,不管傳統 io 還是 nio 都需要 read 和 write 方法,這些都是 java 程序調用的而不是系統幫我們調用的,nio2.0 里這點得到了改觀,即使用異步非阻塞 AsynchronousXXX 四個類來處理。
5.BIO 和 NIO 和 AIO 的區別以及應用場景?
- 同步:java 自己去處理 io。
- 異步:java 將 io 交給操作系統去處理,告訴緩存區大小,處理完成回調。阻塞:使用阻塞 IO 時,Java 調用會一直阻塞到讀寫完成才返回。
- 非阻塞:使用非阻塞 IO 時,如果不能立馬讀寫,Java 調用會馬上返回,當 IO 事件分發器通知可讀寫時在進行讀寫,不斷循環直到讀寫完成。BIO:同步并阻塞,服務器的實現模式是一個連接一個線程,這樣的模式很明顯的一個缺陷是:由于客戶端連接數與服務器線程數成正比關系,可能造成不必要的線程開銷,嚴重的還將導致服務器內存溢出。當然,這種情況可以通過線程池機制改善,但并不能從本質上消除這個弊端。
- NIO:在 JDK1.4 以前,Java 的 IO 模型一直是 BIO,但從 JDK1.4 開始,JDK 引入的新的 IO 模型 NIO,它是同步非阻塞的。而服務器的實現模式是多個請求一個線程,即請求會注冊到多路復用器 Selector 上,多路復用器輪詢到連接有 IO 請求時才啟動一個線程處理。AIO:JDK1.7 發布了 NIO2.0,這就是真正意義上的異步非阻塞,服務器的實現模式為多個有效請求一個線程,客戶端的 IO 請求都是由 OS 先完成再通知服務器應用去啟動線程處理(回調)。
- 應用場景:并發連接數不多時采用 BIO,因為它編程和調試都非常簡單,但如果涉及到高并發的情況,應選擇 NIO 或 AIO,更好的建議是采用成熟的網絡通信框架 Netty。
6.什么是 Java 序列化,如何實現 Java 序列化?
- 序列化就是一種用來處理對象流的機制,將對象的內容進行流化。可以對流化后的對象進行讀寫操作,可以將流化后的對象傳輸于網絡之間。序列化是為了解決在對象流讀寫操作時所引發的問題序列化的實現:將需要被序列化的類實現 Serialize 接口,沒有需要實現的方法,此接口只是為了標注對象可被序列化的,然后使用一個輸出流(如:FileOutputStream)來構造一個ObjectOutputStream(對象流)對象,再使用 ObjectOutputStream 對象的 write(Object obj)方法就可以將參數 obj 的對象寫出。
7.PrintStream、BufferedWriter、PrintWriter 的比較?
- PrintStream 類的輸出功能非常強大,通常如果需要輸出文本內容,都應該將輸出流包裝成 PrintStream 后進行輸出。它還提供其他兩項功能。與其他輸出流不同,PrintStream 永遠不會拋出 IOException;而是,異常情況僅設置可通過 checkError 方法測試的內部標志。另外,為了自動刷新,可以創建一個 PrintStream
- BufferedWriter:將文本寫入字符輸出流,緩沖各個字符從而提供單個字符,數組和字符串的高效寫入。通過 write()方法可以將獲取到的字符輸出,然后通過 newLine()進行換行操作。
BufferedWriter 中的字符流必須通過調用 flush 方法才能將其刷出去。并且 BufferedWriter 只能對字符流進行操作。如果要對字節流操作,則使用 BufferedInputStream - PrintWriter 的 println 方法自動添加換行,不會拋異常,若關心異常,需要調用 checkError
方法看是否有異常發生,PrintWriter 構造方法可指定參數,實現自動刷新緩存(autoflush)
8. 什么是節點流,什么是處理流,各有什么好處,處理流的創建有什么特征?
- 節點流直接與數據源相連,用于輸入或者輸出處理流,在節點流的基礎上對之進行加工,進行一些功能的擴展處理流的構造器必須要傳入節點流的子類。
9. 什么是 IO 流?
- 它是一種數據的流從源頭流到目的地。比如文件拷貝,輸入流和輸出流都包括了。輸入流從文件中讀取數據存儲到進程(process)中,輸出流從進程中讀取數據然后寫入到目標文件。
10.有哪些可用的 Filter 流?
- 在 java.io 包中主要由 4 個可用的 filter Stream。兩個字節 filter stream,兩個字符 filter stream. 分別是 FilterInputStream, FilterOutputStream, FilterReader and FilterWriter.這些類是抽象類,不能被實例化的。
11. Java 中有幾種類型的流?
- 按照流的方向:輸入流(inputStream)和輸出流(outputStream)。
- 按照實現功能分:節點流(可以從或向一個特定的地方(節點)讀寫數據。如 FileReader)和處理流(是對一個已存在的流的連接和封裝,通過所封裝的流的功能調用實現數據讀寫。如 BufferedReader。處理流的構造方法總是要帶一個其他的流對象做參數,一個流對象經過其他流的多次包裝,稱為流的鏈接)。
- 按照處理數據的單位:字節流和字符流。字節流繼承于 InputStream 和 OutputStream,字符流繼承于 InputStreamReader 和 OutputStreamWriter 。
12.如何實現對象克隆?
- 有兩種方式:
實現 Cloneable 接口并重寫 Object 類中的 clone()方法;
實現 Serializable 接口,通過對象的序列化和反序列化實現克隆,可以實現真正的深度克隆。
13. 什么是緩沖區?有什么作用?
- 緩沖區就是一段特殊的內存區域,很多情況下當程序需要頻繁地操作一個資源(如文件或數據庫)則性能會很低,所以為了提升性能就可以將一部分數據暫時讀寫到緩存區,以后直接從此區域中讀寫數據即可,這樣就可以顯著的提升性能。對于 Java 字符流的操作都是在緩沖區操作的,所以如果我們想在字符流操作中主動將緩沖區刷新到文件則可以使用 flush() 方法操作。
14.什么是阻塞 IO?什么是非阻塞 IO?
-
IO 操作包括:對硬盤的讀寫、對 socket 的讀寫以及外設的讀寫。
當用戶線程發起一個 IO 請求操作(本文以讀請求操作為例),內核會去查看要讀取的數據是否就緒,對于阻塞 IO 來說,如果數據沒有就緒,則會一直在那等待,直到數據就緒;對于非阻塞 IO 來說,如果數據沒有就緒,則會返回一個標志信息告知用戶線程當前要讀的數據沒有就緒。當數據就緒之后,便將數據拷貝到用戶線程,這樣才完成了一個完整的 IO 讀請求操作,也就是說一個完整的 IO 讀請求操作包括兩個階段:- 查看數據是否就緒;
- 進行數據拷貝(內核將數據拷貝到用戶線程)。
-
那么阻塞(blocking IO)和非阻塞(non-blocking IO)的區別就在于第一個階段,如果數據沒有就緒,在查看數據是否就緒的過程中是一直等待,還是直接返回一個標志信息。
-
Java 中傳統的 IO 都是阻塞 IO,比如通過 socket 來讀數據,調用 read()方法之后,如果數據沒有就緒,當前線程就會一直阻塞在 read 方法調用那里,直到有數據才返回;而如果是非阻塞 IO 的話,當數據沒有就緒,read()方法應該返回一個標志信息,告知當前線程數據沒有就緒,而不是一直在那里等待。
三、Java Web
1. session 和 cookie 的區別?
- session 是存儲在服務器端,cookie 是存儲在客戶端的,所以安全來講 session 的安全性要比 cookie 高,然后我們獲取 session 里的信息是通過存放在會話 cookie 里的 sessionid 獲取的。又由于 session 是存放在服務器的內存中,所以 session 里的東西不斷增加會造成服務器的負擔,所以會把很重要的信息存儲在 session 中,而把一些次要東西存儲在客戶端的 cookie 里,然后 cookie 確切的說分為兩大類分為會話 cookie 和持久化 cookie,會話 cookie 確切的說是,存放在客戶端瀏覽器的內存中,所以說他的生命周期和瀏覽器是一致的,瀏覽器關了會話 cookie 也就消失了,然而持久化 cookie 是存放在客戶端硬盤中,而持久化 cookie 的生命周期就是我們在設置 cookie 時候設置的那個保存時間,然后我們考慮一問題當瀏覽器關閉時 session 會不會丟失,從上面敘述分析 session 的信息是通過會話 cookie 的 sessionid 獲取的,當瀏覽器關閉的時候會話 cookie 消失所以我們的 sessionid 也就消失了,但是 session 的信息還存在服務器端,這時我們只是查不到所謂的 session 但它并不是不存在。那么,session 在什么情況下丟失,就是在服務器關閉的時候,或者是 session 過期(默認時間是 30 分鐘),再或 者 調 用 了 invalidate() 的 或 者 是 我 們 想 要 session 中 的 某 一 條 數 據 消 失 調 用session.removeAttribute()方法,然后 session 在什么時候被創建呢,確切的說是通過調用getsession()來創建,這就是 session 與 cookie 的區別.
2. session 和 cookie 聯系?
- session 是通過 cookie 來工作的 session 和 cookie 之間是通過COOKIE[′PHPSESSID′]來聯系的,通過_COOKIE['PHPSESSID']來聯系的,通過C?OOKIE[′PHPSESSID′]來聯系的,通過_COOKIE[‘PHPSESSID’]可以知道 session 的 id,從而獲取到其他的信息,在購物網站中通常將用戶加入購物車的商品聯通 session_id 記錄到數據庫中,當用戶再次訪問是,通過 sessionid 就可以查找到用戶上次加入購物車的商品。因為 sessionid 是唯一的,記錄到數據庫中就可以根據這個查找了。
3. servlet 的生命周期?
-
Servlet 生命周期可以分成四個階段:加載和實例化、初始化、服務、銷毀。
當客戶第一次請求時,首先判斷是否存在 Servlet 對象,若不存在,則由 Web 容器創建對象,而后調用 init()方法對其初始化,此初始化方法在整個 Servlet 生命周期中只調用一次。完成 Servlet 對象的創建和實例化之后,Web 容器會調用 Servlet 對象的 service()方法來處理請求。 -
Web 容器關閉或者 Servlet 對象要從容器中被刪除時,會自動調用 destory()方法。
4. 什么是 webservice?
- 從表面上看,WebService 就是一個應用程序向外界暴露出一個能通過 Web 進行調用的 API,也就是說能用編程的方法通過 Web 來調用這個應用程序。我們把調用這個 WebService 的應用程序叫做客戶端,而把提供這個 WebService 的應用程序叫做服務端。從深層次看, WebService 是建立可互操作的分布式應用程序的新平臺,是一個平臺,是一套標準。它定義了應用程序如何在 Web 上實現互操作性,你可以用任何你喜歡的語言,在任何你喜歡的平臺上寫 Web service ,只要我們可以通過 Web service 標準對這些服務進行查詢和訪問。
5. jsp 和 servlet 的區別、共同點、各自應用的范圍?
- JSP 是 Servlet 技術的擴展,本質上就是 Servlet 的簡易方式。JSP 編譯后是“類 servlet”。Servlet
和 JSP 最主要的不同點在于,Servlet 的應用邏輯是在 Java 文件中,并且完全從表示層中的 HTML 里分離開來。而 JSP 的情況是 Java 和 HTML 可以組合成一個擴展名為.jsp 的文件。JSP側重于視圖,Servlet 主要用于控制邏輯。在 struts 框架中,JSP 位于 MVC 設計模式的視圖層,
而 Servlet 位于控制層.
6.轉發(forward)和重定向(redirect)的區別?
- 從地址欄顯示來說
forward 是服務器請求資源,服務器直接訪問目標地址的 URL,把那個 URL 的響應內容讀取過來,然后把這些內容再發給瀏覽器.瀏覽器根本不知道服務器發送的內容從哪里來的,所以它的地址欄還是原來的地址. redirect 是服務端根據邏輯,發送一個狀態碼,告訴瀏覽器重新去請求那個地址.所以地址欄顯示的是新的 URL. - 從數據共享來說
forward:轉發頁面和轉發到的頁面可以共享 request 里面的數據.
redirect:不能共享數據. 3. 從運用地方來說
forward:一般用于用戶登陸的時候,根據角色轉發到相應的模塊.
redirect:一般用于用戶注銷登陸時返回主頁面和跳轉到其它的網站等 4. 從效率來說forward:高.
redirect:低.
7. request.getAttribute() 和 request.getParameter() 有何區別?
- request.getParameter()取得是通過容器的實現來取得通過類似 post,get 等方式傳入的數
- request.setAttribute()和 getAttribute()只是在 web 容器內部流轉,僅僅是請求處理階段。
- getAttribute 是返回對象,getParameter 返回字符串
- getAttribute()一向是和 setAttribute()一起使用的,只有先用 setAttribute()設置之后,才能夠通過 getAttribute()來獲得值,它們傳遞的是 Object 類型的數據。而且必須在同一個 request 對象中使用才有效。,而 getParameter()是接收表單的 get 或者 post 提交過來的參數
8. jsp 靜態包含和動態包含的區別?
- 兩者格式不同,靜態包含:<%@ include file=“文件” %>,而動態包含:。
- 包含時間不同,靜態包含是先將幾個文件合并,然后再被編譯,缺點就是如果含有相同的標簽,會出錯。動態包含是頁面被請求時編譯,將結果放在一個頁面。
- 生成的文件不同,靜態包含會生成一個包含頁面名字的 servlet 和 class 文件;而動態包含
會各自生成對應的 servlet 和 class 文件 - 傳遞參數不同,動態包含能夠傳遞參數,而靜態包含不能。
9. MVC 的各個部分都有哪些技術來實現?如何實現?
- MVC 是 Model-View-Controller 的簡寫。“Model” 代表的是應用的業務邏輯(通過 JavaBean, EJB 組件實現), “View” 是應用的表示面(由 JSP 頁面產生),“Controller” 是提供應用的處理過程控制(一般是一個 Servlet),通過這種設計模型把應用邏輯,處理過程和顯示邏輯分成不同的組件實現。這些組件可以進行交互和重用
10. jsp 有哪些內置對象?作用分別是什么?
JSP 的 9 大內置對象:
- request:封裝客戶端的請求,其中包含來自 GET 或 POST 請求的參數;
- response:封裝服務器對客戶端的響應;
- pageContext:通過該對象可以獲取其他對象;
- session:封裝用戶會話的對象;
- application:封裝服務器運行環境的對象;
- out:輸出服務器響應的輸出流對象;
- config:Web 應用的配置對象;
- page:JSP 頁面本身(相當于 Java 程序中的 this);
- exception:封裝頁面拋出異常的對象。
11. Http 請求的 get 和 post 方法的區別。
- Get 是向服務器索取數據的一種請求,而 Post 是向服務器提交數據的一種請求。
- Get 是獲取信息,而不是修改信息,類似數據庫查詢功能一樣,數據不會被修改。
- Get 請求的參數會跟在 url 后進行傳遞,請求的數據會附在 URL 之后,以?分割 URL 和傳輸數據,參數之間以& 相連,%XX 中的 XX 為該符號以 16 進制表示的 ASCII,如果數據是英文字母/數字,原樣發送,如果是空格,轉換為 +,如果是中文/其他字符,則直接把字符串用BASE64 加密。
- Get 傳輸的數據有大小限制,因為 GET 是通過 URL 提交數據,那么 GET 可提交的數據量就跟 URL 的長度有直接關系了,不同的瀏覽器對 URL 的長度的限制是不同的。
- GET 請求的數據會被瀏覽器緩存起來,用戶名和密碼將明文出現在 URL 上,其他人可以查到歷史瀏覽記錄,數據不太安全。在服務器端,用 Request.QueryString 來獲取 Get 方式提交來的數據。
- Post 請求則作為 http 消息的實際內容發送給 web 服務器,數據放置在 HTML Header 內提交, Post 沒有限制提交的數據。Post 比 Get 安全,當數據是中文或者不敏感的數據,則用 get,因為使用 get,參數會顯示在地址,對于敏感數據和不是中文字符的數據,則用 post。
- POST 表示可能修改服務器上的資源的請求,在服務器端,用 Post 方式提交的數據只能用 Request.Form 來獲取。
12. tomcat 容器是如何創建 servlet 類實例?用到了什么原理?
- 當容器啟動時,會讀取在 webapps 目錄下所有的 web 應用中的 web.xml 文件,然后對 xml 文件進行解析,并讀取 servlet 注冊信息。然后,將每個應用中注冊的 servlet 類都進行加載,并通過反射的方式實例化。(有時候也是在第一次請求時實例化)
- 在 servlet 注冊時加上1如果為正數,則在一開始就實例化,如果不寫或為負數,則第一次請求實例化。
13.JDBC 訪問數據庫的基本步驟是什么?
? 第一步:Class.forName() 加載數據庫連接驅動。
? 第二步:DriverManager.getConnection() 獲取數據連接對象。
? 第三步:根據 SQL 獲取 sql 會話對象,有 2 種方式:Statement 和 PreparedStatement。
? 第四步:執行 SQL,執行 SQL 前如果有參數值就設置參數值 setXXX()。
? 第五步:處理結果集。
? 第六步:關閉結果集、關閉會話、關閉連接。
14.為什么要使用 PreparedStatement?
? PreparedStatement 接口繼承 Statement,PreparedStatement 實例包含已編譯的 SQL 語句,所以其執行速度要快于 Statement 對象。
? 作為 Statement 的子類, PreparedStatement 繼承了 Statement 的所有功能。三種方法 execute、 executeQuery 和 executeUpdate 已被更改以使之不再需要參數。
? 在 JDBC 應用中,多數情況下使用 PreparedStatement,原因如下:
代碼的可讀性和可維護性。Statement 需要不斷地拼接,而 PreparedStatement 不會。
? PreparedStatement 盡最大可能提高性能。DB 有緩存機制,相同的預編譯語句再次被調用不會再次需要編譯。
最重要的一點是極大地提高了安全性。Statement 容易被 SQL 注入,而 PreparedStatement傳入的內容不會和 sql 語句發生任何匹配關系。
15.數據庫連接池的原理。為什么要使用連接池?
? 1、數據庫連接是一件費時的操作,連接池可以使多個操作共享一個連接。
? 2、數據庫連接池的基本思想就是為數據庫連接建立一個“緩沖池”。預先在緩沖池中放入一定數量的連接,當需要建立數據庫連接時,只需從 “緩沖池” 中取出一個,使用完畢之后再放回去。我們可以通過設定連接池最大連接數來防止系統無盡的與數據庫連接。更為重要的是我們可以通過連接池的管理機制監視數據庫的連接的數量、使用情況,為系統開發,測試及性能調整提供依據。
? 3、使用連接池是為了提高對數據庫連接資源的管理。
16.execute,executeQuery,executeUpdate 的區別是什么?
? 1、Statement 的 execute(String query)方法用來執行任意的 SQL 查詢,如果查詢的結果是一個ResultSet,這個方法就返回 true。如果結果不是 ResultSet,比如 insert 或者 update 查詢,它就會返回 false 。我們可以通過它的 getResultSet 方法來獲取 ResultSet ,或者通過getUpdateCount()方法來獲取更新的記錄條數。
? 2、Statement 的 executeQuery(String query) 接口用來執行 select 查詢,并且返回 ResultSet,即使查詢不到記錄返回的 ResultSet 也不會為 null。我們通常使用 executeQuery 來執行查詢語句,這樣的話如果傳進來的是 insert 或者 update 語句的話,它會拋出錯誤信息為 “executeQuery method can not be used for update” 的 java.util.SQLException。
? 3、Statement 的 executeUpdate(String query) 方法用來執行 insert、update 和 delete(DML)語句,或者什么也不返回,對于 DDL 語句,返回值是 int 類型,如果是 DML 語句的話,它就是更新的條數,如果是 DDL 的話,就返回 0。只有當你不確定是什么語句的時候才應該使用 execute() 方法,否則應該使用 executeQuery 或者 executeUpdate 方法。
17.JDBC 的 ResultSet 是什么?
? 在查詢數據庫后會返回一個 ResultSet,它就像是查詢結果集的一張數據表。ResultSet 對象維護了一個游標,指向當前的數據行。開始的時候這個游標指向的是第一行。如果調用了 ResultSet 的 next()方法游標會下移一行,如果沒有更多的數據了,next()方法會返回 false。可以在 for 循環中用它來遍歷數據集。
? 默認的 ResultSet 是不能更新的,游標也只能往下移。也就是說你只能從第一行到最后一行遍歷一遍。不過也可以創建可以回滾或者可更新的 ResultSet,當生成 ResultSet 的 Statement 對象要關閉或者重新執行或是獲取下一個 ResultSet 的時候,ResultSet 對象也會自動關閉。可以通過 ResultSet 的 getter 方法,傳入列名或者從 1 開始的序號來獲取列數據。
18.什么是 Servlet?
? Servlet 是使用 Java Servlet 應用程序接口(API)及相關類和方法的 Java 程序,所有的 Servlet 都必須要實現的核心接口是 javax.servlet.servlet。每一個 servlet 都必須要直接或者間接實現這個接口,或者繼承javax.servlet.GenericServlet 或 javax.servlet.HTTPServlet。Servlet 主要用于處理客戶端傳來的 HTTP 請求,并返回一個響應。
19.doGet 和 doPost 方法有什么區別?
? doGet:GET 方法會把名值對追加在請求的 URL 后面。因為 URL 對字符數目有限制,進而限制了用在客戶端請求的參數值的數目。并且請求中的參數值是可見的,因此,敏感信息不能用這種方式傳遞。
? doPOST:POST 方法通過把請求參數值放在請求體中來克服 GET 方法的限制,因此,可以發送的參數的數目是沒有限制的。最后,通過 POST 請求傳遞的敏感信息對外部客戶端是不可見的。
20.JSP 有哪些動作?分別是什么?
? JSP 共有以下 6 種基本動作:
? jsp:include:在頁面被請求的時候引入一個文件。
? jsp:useBean:尋找或者實例化一個 JavaBean。
? jsp:setProperty:設置 JavaBean 的屬性。
? jsp:getProperty:輸出某個 JavaBean 的屬性。
? jsp:forward:把請求轉到一個新的頁面。
? jsp:plugin:根據瀏覽器類型為 Java 插件生成 OBJECT 或 EMBED 標記。
21.JSP 常用的指令?
? page:針對當前頁面的指令。
? include:包含另一個頁面。
? taglib:定義和訪問自定義標簽。
22.頁面間對象傳遞的方法?
? request、session、application、cookie
23.JSP 中動態 INCLUDE 與靜態 INCLUDE 的區別?
? 動態 include 用于 jsp:include 動作實現 <jsp:include page = “include.jsp” flush = “true”/> 它總是會檢查所含文件的變化,適用于包含動態頁面,并且可以帶參數。
? 靜態 include 用于 include 偽碼實現,不會檢查所含文件的變化,適用于包含靜態頁面 <%@include file=“include.html”%>。
24.JSP 的四大范圍?
? JSP 中的四種作用域包括 page、request、session 和 application,具體來說:
? page 代表與一個頁面相關的對象和屬性。
? request 代表與 Web 客戶機發出的一個請求相關的對象和屬性。一個請求可能跨越多個頁面,涉及多個 Web 組件;需要在頁面顯示的臨時數據可以置于此作用域。
? session 代表與某個用戶與服務器建立的一次會話相關的對象和屬性。跟某個用戶相關的數據應該放在用戶自己的 session 中。
? application 代表與整個 Web 應用程序相關的對象和屬性,它實質上是跨越整個 Web 應用程序,包括多個頁面、請求和會話的一個全局作用域。
25.BS 與 CS 的聯系與區別?
? 1、硬件環境不同:
C/S 一般建立在專用的網絡上, 小范圍里的網絡環境, 局域網之間再通過專門服務器提供連接和數據交換服務.
B/S 建立在廣域網之上的, 不必是專門的網絡硬件環境,例與電話上網, 租用設備. 信息自己管理. 有比 C/S 更強的適應范圍, 一般只要有操作系統和瀏覽器就行
? 2、對安全要求不同
C/S 一般面向相對固定的用戶群, 對信息安全的控制能力很強. 一般高度機密的信息系統采用 C/S 結構適宜. 可以通過 B/S 發布部分可公開信息.
B/S 建立在廣域網之上, 對安全的控制能力相對弱, 可能面向不可知的用戶。
? 3、對程序架構不同
C/S 程序可以更加注重流程, 可以對權限多層次校驗, 對系統運行速度可以較少考慮. B/S 對安全以及訪問速度的多重的考慮, 建立在需要更加優化的基礎之上. 比 C/S 有更高的要求 B/S 結構的程序架構是發展的趨勢, 從 MS 的.Net 系列的 BizTalk 2000 Exchange 2000 等, 全面支持網絡的構件搭建的系統. SUN 和 IBM 推的 JavaBean 構件技術等,使 B/S 更加成熟.
? 4、軟件重用不同C/S 程序可以不可避免的整體性考慮, 構件的重用性不如在 B/S 要求下的構件的重用性
好.B/S 對的多重結構,要求構件相對獨立的功能. 能夠相對較好的重用.就入買來的餐桌可以再利用,而不是做在墻上的石頭桌子
? 5、系統維護不同
C/S 程序由于整體性, 必須整體考察, 處理出現的問題以及系統升級. 升級難. 可能是再做一個全新的系統
B/S 構件組成,方面構件個別的更換,實現系統的無縫升級. 系統維護開銷減到最小.用戶從網上自己下載安裝就可以實現升級.
? 6、處理問題不同
C/S 程序可以處理用戶面固定, 并且在相同區域, 安全要求高需求, 與操作系統相關.應該都是相同的系統
B/S 建立在廣域網上, 面向不同的用戶群, 分散地域, 這是 C/S 無法作到的. 與操作系統平臺關系最小.
? 7、用戶接口不同
C/S 多是建立的 Window 平臺上,表現方法有限,對程序員普遍要求較高 B/S 建立在瀏覽器上, 有更加豐富和生動的表現方式與用戶交流. 并且大部分難度減低,
減低開發成本.
? 8、信息流不同
C/S 程序一般是典型的中央集權的機械式處理, 交互性相對低
B/S 信息流向可變化, B-B B-C B-G 等信息、流向的變化, 更像交易中心。
26.說出 Servlet 的生命周期,并說出 Servlet 和 CGI 的區別?
? Web 容器加載 Servlet 并將其實例化后,Servlet 生命周期開始,容器運行其 init 方法進行 Servlet 的初始化,請求到達時運行其 service 方法,service 方法自動派遣運行與請求對應的 doXXX 方法(doGet,doPost)等,當服務器決定將實例銷毀的時候調用其 destroy 方法。
? 與 cgi 的區別在于 servlet 處于服務器進程中,它通過多線程方式運行其 service 方法,一個實例可以服務于多個請求,并且其實例一般不會銷毀,而 CGI 對每個請求都產生新的進程,服務完成后就銷毀,所以效率上低于 servlet。
27.如何防止表單重復提交?
針對于重復提交的整體解決方案:
1、用 redirect(重定向)來解決重復提交的問題。
2、點擊一次之后,按鈕失效。
3、通過 loading (Loading 原理是在點擊提交時,生成 Loading 樣式,在提交完成之后隱藏該樣式)。
4、自定義重復提交過濾器。
28.request 作用?
1、獲取請求參數 getParameter()。
2、獲取當前 Web 應用的虛擬路徑 getContextPath。
3、轉發 getRequestDispatcher(路徑).forward(request,response)。
4、它還是一個域對象。
29.get 請求中文亂碼?
1、亂碼的根本原因:
瀏覽器的編碼方式 UTF-8 和 服務器的解碼方式 ISO-859-1 不一樣。
2、解決方法:
(1)第一種方式 使用 URLEncoder 和 URLDecoder 兩個類編解碼。先以 iso-8895-1 進行編碼,然后再以 utf-8 進行解碼。
(2)第二種方式 使用 String 類的方法進行編解碼。
(3)第三種方式 更改 server.xml 配置文件,GET 請求是在 URL 地址欄中傳遞請求參數的,它會被 Tomcat 服務器自動解碼,而 Tomcat 服務器默認的字符集也是 ISO-8859-1,所以我們需要修改 Tomcat 服務器的字符集為 UTF-8。
30.post 請求中文亂碼問題?
1、post 請求方式亂碼的原因是:因為 post 是以二進制流的形式發送到的服務器,服務器收到數據后,默認以 iso-8859-1 進行編碼。
2、post 請求亂碼解決,只需要在獲取請求參數之前調用 request.setCharacterEncoding(“UTF-8”) 方法設置字符集即可。
31.響應亂碼?
1、原因:由服務器編碼,默認使用 ISO-8859-1 進行編碼,由瀏覽器解碼,默認使用 GBK 進行解碼。
2、解決方案:
方法 1:設置響應頭
response.setHeader(“Content-Type”,“text/html;charset=utf-8”)。
方法 2:設置響應的內容類型
response.setContentType(“text/html;charset=utf-8”),通過這種方式可以在響應頭中告訴瀏覽器響應體的編碼方式是 UTF-8,同時服務器也會采用該字符集進行編碼但需要注意的是,兩種方法一定要在 response.getWriter()之前進行。
32.Cookie 對象的缺陷?
1、Cookie 是明文的,不安全。
2、不同的瀏覽器對 Cookie 對象的數量和大小有限制。
3、Cookie 對象攜帶過多費流量。
4、Cookie 對象中的 value 值只能是字符串,不能放對象網絡中傳遞的數據,只能是字符串。
33.Session 的運行機制?
1、在服務器端創建 Session 對象,該對象有一個全球唯一的 ID。
2、在創建 Session 對象的同時創建一個特殊的 Cookie 對象,該 Cookie 對象的名字是 JSESSIONID,該 Cookie 對象的 value 值是 Session 對象的那個全球唯一的 ID,并且會將這個特殊的 Cookie 對象攜帶發送給瀏覽器。
3、以后瀏覽器再發送請求就會攜帶這個特殊的 Cookie 對象。
4、服務器根據這個特殊的 Cookie 對象的 value 值在服務器中尋找對應的 Session 對象,以此區分不同的用戶。
34.鈍化和活化?
1、Session 與 session 域中的對象一起從內存中被序列化到硬盤上的過程我們稱為鈍化,服務器關閉時會發生鈍化。
2、Session 與 session 域中的對象一起從硬盤上反序列化到內存中的過程我們稱為活化,服務器再次開啟時會發生活化。
3、要保證 session 域中的對象能和 Session 一起被鈍化和活化,必須保證對象對應的類實現 Serializable 接口。
35.過濾器Filter 的工作原理?
Filter 接口中有一個 doFilter 方法,當我們編寫好 Filter,并配置對哪個 web 資源進行攔截后, WEB 服務器每次在調用 web 資源的 service 方法之前,都會先調用一下 filter 的 doFilter 方法,因此,在該方法內編寫代碼可以達到如下目的:
調用目標資源之前,讓一段代碼執行。
是否調用目標資源(即是否讓用戶訪問 web 資源),調用目標資源之后,讓一段代碼執行。
web 服務器在調用 doFilter 方法時,會傳遞一個 filterChain 對象進來,filterChain 對象是filter 接口中最重要的一個對象,它也提供了一個 doFilter 方法,開發人員可以根據需求決定是否調用此方法,調用該方法,則 web 服務器就會調用 web 資源的 service 方法,即 web 資源就會被訪問,否則 web 資源不會被訪問。
36.Filter 鏈是什么?
在一個 web 應用中,可以開發編寫多個 Filter,這些 Filter 組合起來稱之為一個 Filter 鏈。web服務器根據 Filter 在 web.xml 文件中的注冊順序,決定先調用哪個 Filter,當第一個 Filter 的 doFilter 方法被調用時,web 服務器會創建一個代表 Filter 鏈的 FilterChain 對象傳遞給該方法,在 doFilter 方法中,開發人員如果調用了 FilterChain 對象的 doFilter 方法,則 web 服務器會檢查 FilterChain 對象中是否還有 filter,如果有,則調用第 2 個 filter,如果沒有,則調用目標資源。
37.監聽器Listener類型?
按監聽的對象劃分:servlet2.4 規范定義的事件有三種:
1、用于監聽應用程序環境對象(ServletContext)的事件監聽器。
2、用于監聽用戶會話對象(HttpSession)的事件監聽器。
3、用于監聽請求消息對象(ServletRequest)的事件監聽器,按監聽的事件類項劃分為:
(1)用于監聽域對象自身的創建和銷毀的事件監聽器;
(2)用于監聽域對象中的屬性的增加和刪除的事件監聽器;
(3)用于監聽綁定到 HttpSession 域中的某個對象的狀態的事件監聽器,在一個 web 應用程序的整個運行周期內, web 容器會創建和銷毀三個重要的對象,即:ServletContext,HttpSession,ServletRequest。
38.Servlet Filter Listener 啟動順序?
啟動的順序為:listener->Filter->servlet,簡單記為:理(Listener)發(Filter)師(servlet),執行的順序不會因為三個標簽在配置文件中的先后順序而改變。
四、JVM
1.Java 的內存劃分?
第一:程序計數器(PC,Program Counter Register)。在 JVM 規范中,每個線程都有它自己的程序計數器,并且任何時間一個線程都只有一個方法在執行,也就是所謂的當前方法。程序計數器會存儲當前線程正在執行的 Java 方法的 JVM 指令地址;或者,如果是在執行本地方法,則是未指定值(undefined)。(唯一不會拋出 OutOfMemoryError)。
第二:Java 虛擬機棧(Java Virtual Machine Stack),早期也叫 Java 棧。每個線程在創建時都會創建一個虛擬機棧,其內部保存一個個的棧幀(Stack Frame),對應著一次次的 Java 方法調用。
前面談程序計數器時,提到了當前方法;同理,在一個時間點,對應的只會有一個活動的棧幀,通常叫作當前幀,方法所在的類叫作當前類。如果在該方法中調用了其他方法,對應的新的棧幀會被創建出來,成為新的當前幀,一直到它返回結果或者執行結束。JVM 直接對 Java 棧的操作只有兩個,就是對棧幀的壓棧和出棧,棧幀中存儲著局部變量表、操作數(operand)棧、動態鏈接、方法正常退出或者異常退出的定義等。
第三:堆(Heap),它是 Java 內存管理的核心區域,用來放置 Java 對象實例,幾乎所有創建的 Java 對象實例都是被直接分配在堆上。堆被所有的線程共享,在虛擬機啟動時,我們指定的“Xmx”之類參數就是用來指定最大堆空間等指標( 編譯器通過逃逸分析,確定對象是在棧上分配還是在堆上分配),理所當然,堆也是垃圾收集器重點照顧的區域,所以堆內空間還會被不同的垃圾收集器進行進一步的細分,最有名的就是新生代、老年代的劃分。
第四:方法區(Method Area)。這也是所有線程共享的一塊內存區域,用于存儲所謂的元(Meta)數據,例如類結構信息,以及對應的運行時常量池、字段、方法代碼等,由于早期的 Hotspot JVM 實現,很多人習慣于將方法區稱為永久代(Permanent Generation),Oracle JDK 8 中將永久代移除,同時增加了元數據區(Metaspace)。
第五:運行時的常量池(Run-Time Constant Pool),這是方法區的一部分。如果仔細分析過反編譯的類文件結構,你能看到版本號、字段、方法、超類、接口等各種信息,還有一項信息就是常量池。Java 的常量池可以存放各種常量信息,不管是編譯期生成的各種字面量,還是需要在運行時決定的符號引用,所以它比一般語言的符號表存儲的信息更加寬泛。
第六:本地方法棧(Native Method Stack),它和 Java 虛擬機棧是非常相似的,支持對本地方法的調用,也是每個線程都會創建一個,在 Oracle Hotspot JVM 中,本地方法棧和 Java 虛擬機棧是在同一塊兒區域,這完全取決于技術實現的決定,并未在規范中強制。
2.什么是 Java 虛擬機?為什么 Java 被稱作是無關平臺的編程語言?
Java 虛擬機是一個可以執行 Java 字節碼的虛擬機進程,Java 源文件被編譯成能被 Java 虛擬機執行的字節碼文件, Java 被設計成允許應用程序可以運行在任意的平臺,而不需要程序員為每一個平臺單獨重寫或者是重新編譯,Java 虛擬機讓這個變為可能,因為它知道底層硬件平臺的指令長度和其他特性。
3.如何判斷一個對象應該被回收?
1、在 Java 中采取了 可達性分析法
通過一系列的“GC Roots”對象作為起點進行搜索,如果在“GC Roots”和一個對象之間沒有可達路徑,則稱該對象是不可達的,不過要注意的是被判定為不可達的對象不一定就會成為可回收對象。被判定為不可達的對象要成為可回收對象必須至少經歷兩次標記過程,如果在這兩次標記過程中仍然沒有逃脫成為可回收對象的可能性,則基本上就真的成為可回收對象了。
2、虛擬機棧中引用的對象、方法區類靜態屬性引用的對象、方法區常量池引用的對象、本地方法棧 JNI 引用的對象。
4.GC 觸發的條件?
1)程序調用 System.gc 時可以觸發;
2)系統自身來決定 GC 觸發的時機。
5.可以作為 GCRoots 的對象有哪些?
1、虛擬機棧中引用的對象;
2、方法區中類靜態屬性引用的對象;
3、方法區中常量引用的對象;
4、本地方法棧中引用的對象。
6.JVM 中一次完整的 GC 流程是怎樣的,對象如何晉升到老年代?
Java 堆 = 老年代 + 新生代 新生代 = Eden + S0 + S1
當 Eden 區的空間滿了,Java 虛擬機會觸發一次 Minor GC,以收集新生代的垃圾,存活下來的對象,則會轉移到 Survivor 區。
大對象(需要大量連續內存空間的 Java 對象,如那種很長的字符串)直接進入老年態;如果對象在 Eden 出生,并經過第一次 Minor GC 后仍然存活,并且被 Survivor 容納的話,年齡設為 1,每熬過一次 Minor GC,年齡+1,若年齡超過一定限制(15),則被晉升到老年態。即長期存活的對象進入老年態。
老年代滿了而無法容納更多的對象,Minor GC 之后通常就會進行 Full GC,Full GC 清理整個內存堆 – 包括年輕代和年老代。
Major GC 發生在老年代的 GC,清理老年區,經常會伴隨至少一次 Minor GC,比 Minor GC 慢 10 倍以上。
7.雙親委派模型?
雙親委派模型工作過程是:
如果一個類加載器收到類加載的請求,它首先不會自己去嘗試加載這個類,而是把這個請求委派給父類加載器完成。每個類加載器都是如此,只有當父加載器在自己的搜索范圍內找不到指定的類時(即 ClassNotFoundException),子類加載器才會嘗試自己去加載。
8.為什么需要雙親委派模型?
因為為了防止內存中出現多份同樣的字節碼。
9.怎么打破雙親委派模型?
打破雙親委派機制不僅要繼承 ClassLoader 類,而且還要重寫 loadClass 和 findClass 方法。
10.導致 Full GC 一般有哪些情況?
(1)新生代設置過小
一是新生代 GC 次數非常頻繁,增大系統消耗
二是導致大對象直接進入舊生代,占據了舊生代剩余空間,誘發 Full GC。
(2)新生代設置過大
一是新生代設置過大會導致舊生代過小(堆總量一定),從而誘發 Full GC
二是新生代 GC 耗時大幅度增加
(3)Survivor 設置過小
導致對象從 eden 直接到達舊生代
(4)Survivor 設置過大
導致 eden 過小,增加了 GC 頻率一般說來新生代占整個堆 1/3 比較合適 GC 策略的設置方式。
1)吞吐量優先 可由-XX:GCTimeRatio=n 來設置
2)暫停時間優先 可由-XX:MaxGCPauseRatio=n 來設置
11.Minor GC,Full GC 觸發條件?
Minor GC 觸發條件:
當 Eden 區滿時,觸發 Minor GC。
Full GC 觸發條件:
(1)調用 System.gc 時,系統建議執行 Full GC,但是不必然執行。
(2)老年代空間不足。
(3)方法區空間不足。
(4)通過 Minor GC 后進入老年代的平均大小大于老年代的可用內存。
(5)由 Eden 區、From Space 區向 To Sp3ace 區復制時,對象大小大于 To Space 可存,則把該對象轉存到老年代,且老年代的可用內存小于該對象大小
12.JVM 性能調優?
1、設定堆內存大小-Xmx:堆內存最大限制。
2、設定新生代大小。 新生代不宜太小,否則會有大量對象涌入老年代
-XX:NewSize:新生代大小 -XX:NewRatio 新生代和老生代占比
-XX:SurvivorRatio:伊甸園空間和幸存者空間的占比3、設定垃圾回收器 年輕代用 -XX:+UseParNewGC 年老代用-XX:+UseConcMarkSweepGC
13.Java 內存模型?
Java 內存模型定義了多線程之間共享變量的可見性以及如何在需要的時候對共享變量進行同步。JMM 內部的實現通常是依賴于所謂的內存屏障,通過禁止某些重排序的方式,提供內存可見性保證,也就是實現了各種 happen-before 規則。
與 JVM 內存模型不同。
Java 內存模型即 Java Memory Model,簡稱 JMM。JMM 定義了 Java 虛擬機(JVM)在計算機內存(RAM)中的工作方式。JVM 是整個計算機虛擬模型,所以 JMM 是隸屬于 JVM 的。
Java 內存模型定義了多線程之間共享變量的可見性以及如何在需要的時候對共享變量進行同步。
Java 線程之間的通信采用的是過共享內存模型,這里提到的共享內存模型指的就是 Java 內存模型(簡稱 JMM),JMM 決定一個線程對共享變量的寫入何時對另一個線程可見。從抽象的角度來看,JMM 定義了線程和主內存之間的抽象關系:線程之間的共享變量存儲在主內存(main memory)中,每個線程都有一個私有的本地內存(local memory),本地內存中存儲了該線程以讀/寫共享變量的副本。
14.Java 中棧和堆有什么區別?
最主要的區別就是棧內存用來存儲局部變量和方法調用。
而堆內存用來存儲 Java 中的對象。無論是成員變量,局部變量,還是類變量,它們指向的對象都存儲在堆內存中。
獨有還是共享
棧內存歸屬于單個線程,每個線程都會有一個棧內存,其存儲的變量只能在其所屬線程中可見,即棧內存可以理解成線程的私有內存。
而堆內存中的對象對所有線程可見。堆內存中的對象可以被所有線程訪問。異常錯誤如果棧內存沒有可用的空間存儲方法調用和局部變量,JVM 會拋出
java.lang.StackOverFlowError。
而如果是堆內存沒有可用的空間存儲生成的對象,JVM 會拋出 java.lang.OutOfMemoryError。
空間大小
棧的內存要遠遠小于堆內存,如果你使用遞歸的話,那么你的棧很快就會充滿。如果遞歸沒有及時跳出,很可能發生 StackOverFlowError 問題。
15.常見的垃圾回收算法有哪些?簡述其原理?
GC 最基礎的算法有三種: 標記 -清除算法、復制算法、標記-壓縮算法,我們常用的垃圾回收器一般都采用分代收集算法。
(1)標記 -清除算法,“標記-清除”(Mark-Sweep)算法,如它的名字一樣,算法分為“標記”和“清除”兩個階段:首先標記出所有需要回收的對象,在標記完成后統一回收掉所有被標記的對象。
(2)復制算法,“復制”(Copying)的收集算法,它將可用內存按容量劃分為大小相等的兩塊,每次只使用其中的一塊。當這一塊的內存用完了,就將還存活著的對象復制到另外一塊上面,然后再把已使用過的內存空間一次清理掉。
(3)標記-壓縮算法,標記過程仍然與“標記-清除”算法一樣,但后續步驟不是直接對可回收對象進行清理,而是讓所有存活的對象都向一端移動,然后直接清理掉端邊界以外的內存分代收集算法,“分代收集”(Generational Collection)算法,把 Java 堆分為新生代和老年代,這樣就可以根據各個年代的特點采用最適當的收集算法。
16.解釋棧(stack)、堆(heap)和方法區(method area)的用法?
通常我們定義一個基本數據類型的變量,一個對象的引用,還有就是函數調用的現場保存都使用 JVM 中的棧空間;而通過 new 關鍵字和構造器創建的對象則放在堆空間,堆是垃圾收集器管理的主要區域,由于現在的垃圾收集器都采用分代收集算法,所以堆空間還可以細分為新生代和老生代,再具體一點可以分為 Eden、Survivor(又可分為 From Survivor 和 To Survivor)、Tenured;方法區和堆都是各個線程共享的內存區域,用于存儲已經被 JVM 加載的類信息、常量、靜態變量、JIT 編譯器編譯后的代碼等數據;程序中的字面量(literal)如直接書寫的 100、”hello”和常量都是放在常量池中,常量池是方法區的一部分,。棧空間操作起來最快但是棧很小,通常大量的對象都是放在堆空間,棧和堆的大小都可以通過 JVM 的啟動參數來進行調整,棧空間用關了的話則會引發 StackOverflowError,而堆和常量池空間不足的話則會引發 OutOfMemoryError。
17.什么是類的加載?
類的加載指的是將類的.class 文件中的二進制數據讀入到內存中,將其放在運行時數據區的方法區內,然后在堆區創建一個 java.lang.Class 對象,用來封裝類在方法區內的數據結構。類的加載的最終產品是位于堆區中的 Class 對象,Class 對象封裝了類在方法區內的數據結構,并且向 Java 程序員提供了訪問方法區內的數據結構的接口。
18.類加載器有哪些?
1、啟動類加載器:Bootstrap ClassLoader,負責加載存放在 JDK\jre\lib(JDK 代表 JDK 的安裝目錄,下同)下,或被-Xbootclasspath 參數指定的路徑中的,并且能被虛擬機識別的類庫。
2、擴展類加載器:Extension ClassLoader,該加載器由 sun.misc.Launcher$ExtClassLoader 實現,它負責加載 DK\jre\lib\ext 目錄中,或者由 java.ext.dirs 系統變量指定的路徑中的所有類庫(如javax.開頭類),開發者可以直接使用擴展類加載器。
3、應用程序類加載器:Application ClassLoader,該類加載器由 sun.misc.Launcher$AppClassLoader來實現,它負責加載用戶類路徑(ClassPath)所指定的類,開發者可以直接使用該類加載器。
19.Java 對象創建過程?
1、JVM 遇到一條新建對象的指令時首先去檢查這個指令的參數是否能在常量池中定義到一個類的符號引用,然后加載這個類(類加載過程在后邊講)。
2、為對象分配內存。一種辦法“指針碰撞”、一種辦法“空閑列表”,最終常用的辦法是“本地線程緩沖分配(TLAB)”。
3、將除對象頭外的對象內存空間初始化為 0。
4、對對象頭進行必要設置。
20.Java 中類的生命周期是什么?
1、加載,查找并加載類的二進制數據,在 Java 堆中也創建一個 java.lang.Class 類的對象。
2、連接,連接又包含三塊內容:驗證、準備、初始化。
(1)驗證,文件格式、元數據、字節碼、符號引用的驗證。
(2)準備,為類的靜態變量分配內存,并將其初始化為默認值。
(3)解析,把類中的符號引用轉換為直接引用。
3、初始化,為類的靜態變量賦予正確的初始值。
4、使用,new 出對象程序中使用。
5、卸載,執行垃圾回收。
21.都有哪些垃圾回收器?
1、Serial 收集器,串行收集器是最古老,最穩定以及效率高的收集器,可能會產生較長的停頓,只使用一個線程去回收。
2、ParNew 收集器,ParNew 收集器其實就是 Serial 收集器的多線程版本。
3、Parallel 收集器,Parallel Scavenge 收集器類似 ParNew 收集器,Parallel 收集器更關注系統的吞吐量。
4、Parallel Old 收集器,Parallel Old 是 Parallel Scavenge 收集器的老年代版本,使用多線程和“標記-整理”算法。
5、CMS 收集器,CMS(Concurrent Mark Sweep)收集器是一種以獲取最短回收停頓時間為目標的收集器。
6、G1 收集器,G1 (Garbage-First)是一款面向服務器的垃圾收集器,主要針對配備多顆處理器及大容量內存的機器,以極高概率滿足 GC 停頓時間要求的同時還具備高吞吐量性能特征。
22.JVM 調優命令?
Sun JDK 監控和故障處理命令主要有 jps、jstat、jmap、jhat、jstack、jinfo。
1、jps,JVM Process Status Tool 用于顯示指定系統內所有的 HotSpot 虛擬機進程。
2、jstat,JVM statistics Monitoring 用于監視虛擬機運行時狀態信息的命令,它可以顯示出虛擬機進程中的類裝載、內存、垃圾收集、JIT 編譯等運行數據。
3、jmap,JVM Memory Map 命令用于生成 heap dump 文件。
4、jhat,JVM Heap Analysis Tool 命令是與 jmap 搭配使用,用來分析 jmap 生成的 dump,jhat 內置了一個微型的 HTTP/HTML 服務器,生成 dump 的分析結果后,可以在瀏覽器中查看。
5、jstack,用于生成 java 虛擬機當前時刻的線程快照。
6、jinfo,JVM Configuration info 這個命令作用是實時查看和調整虛擬機的運行參數。
23.JVM 調優工具?
常用調優工具分為兩類,jdk 自帶監控工具:jconsole 和 jvisualvm,第三方有:MAT(Memory AnalyzerTool)、GChisto。
1、jconsole,Java Monitoring and Management Console 是從 java5 開始,在 JDK 中自帶的 java 監控和管理控制臺,用于對 JVM 中內存,線程和類等的監控。
2、jvisualvm,jdk 自帶全能工具,可以分析內存快照、線程快照,監控內存變化、GC 變化等。
3、MAT,Memory Analyzer Tool,一個基于 Eclipse 的內存分析工具,是一個快速、功能豐富的 Java heap 分析工具,它可以幫助我們查找內存泄漏和減少內存消耗4、GChisto,一款專業分析 gc 日志的工具。
24.描述一下 JVM 加載 class 文件的原理機制?
JVM 中類的加載是由類加載器(ClassLoader)和它的子類來實現的,Java 中的類加載器是一個重要的 Java 運行時系統組件,它負責在運行時查找和裝入類文件中的類。類的加載是指把類的 .class 文件中的數據讀入到內存中,通常是創建一個字節數組讀入到 .class 文件中。
25.GC 是什么?為什么要有 GC?
GC 是垃圾收集的意思(Gabage Collection),內存處理是編程人員容易出現問題的地方,忘記或者錯誤的內存回收會導致程序或系統的不穩定甚至崩潰,Java 提供的 GC 功能可以自動監測對象是否超過作用域,從而達到自動回收內存的目的, Java 語言沒有提供釋放已分配內存的顯示操作方法。
26.垃圾回收器的基本原理是什么?
對于 GC 來說,當程序員創建對象時,GC 就開始監控這個對象的地址、大小以及使用情況。通常,GC 采用有向圖的方式記錄和管理堆(heap)中的所有對象。通過這種方式確定哪些對象是"可達的",哪些對象是"不可達的"。當 GC 確定一些對象為"不可達"時,GC 就有責任回收這些內存空間,程序員可以手動執行 System.gc(),通知 GC 運行,但是 Java 語言規范并不保證 GC 一定會執行。
27.Java 中的引用類型有幾種?
1、強引用
如果一個對象具有強引用,它就不會被垃圾回收器回收。即使當前內存空間不足,JVM也不會回收它,而是拋出 OutOfMemoryError 錯誤,使程序異常終止。如果想中斷強引用和某個對象之間的關聯,可以顯式地將引用賦值為 null,這樣一來的話,JVM 在合適的時間就會回收該對象。
2、軟引用
在使用軟引用時,如果內存的空間足夠,軟引用就能繼續被使用,而不會被垃圾回收器回收;只有在內存空間不足時,軟引用才會被垃圾回收器回收。
3、弱引用
具有弱引用的對象擁有的生命周期更短暫。因為當 JVM 進行垃圾回收,一旦發現弱引用對象,無論當前內存空間是否充足,都會將弱引用回收。不過由于垃圾回收器是一個優先級較低的線程,所以并不一定能迅速發現弱引用對象。
4、虛引用
顧名思義,就是形同虛設,如果一個對象僅持有虛引用,那么它相當于沒有引用,在任何時候都可能被垃圾回收器回收。
虛引用必須和引用隊列關聯使用,當垃圾回收器準備回收一個對象時,如果發現它還有虛引用,就會把這個虛引用加入到與之關聯的引用隊列中。程序可以通過判斷引用隊列中是否已經加入了虛引用,來了解被引用的對象是否將要被垃圾回收。如果程序發現某個虛引用已經被加入到引用隊列,那么就可以在所引用的對象的內存被回收之前采取必要的行動
五、多線程
1.Java 創建線程之后,直接調用 start()方法和 run()的區別?
啟動一個線程是調用 start()方法,使線程所代表的虛擬處理機處于可運行狀態,這意味著它可以由 JVM 調度并執行。這并不意味著線程就會立即運行。run()方法可以產生必須退出的標志來停止一個線程。
2.線程 B 怎么知道線程 A 修改了變量?
volatile 修飾變量;
synchronized 修飾修改變量的方法;
wait/notify;
while 輪詢
3.synchronized 和 Volatile、CAS 比較?
synchronized 是悲觀鎖,屬于搶占式,會引起其他線程阻塞。
volatile 提供多線程共享變量可見性和禁止指令重排序優化。
CAS 是基于沖突檢測的樂觀鎖(非阻塞)。
4.線程間通信,wait 和 notify 的理解和使用?
wait 和 notify 必須配合 synchronized 關鍵字使用。
wait 方法釋放鎖,notify 方法不釋放鎖。
還要注意一點就是涉及到線程之間的通信,就肯定會用到 validate 修飾。
5.定時線程的使用?
1、普通線程死循環
2、使用定時器 timer
3、使用定時調度線程池 ScheduledExecutorService
6. 線程同步的方法?
wait():使一個線程處于等待狀態,并且釋放所持有的對象的 lock。sleep():使一個正在運行的線程處于睡眠狀態,是一個靜態方法,調用此方法要捕捉InterruptedException 異常。notify():喚醒一個處于等待狀態的線程,注意的是在調用此方法的時候,并不能確切的喚醒某一個等待狀態的線程,而是由 JVM 確定喚醒哪個線程,而且不是按優先級。notityAll():喚醒所有處入等待狀態的線程,注意并不是給所有喚醒線程一個對象的鎖,而是讓它們競爭。
7.進程和線程的區別?
1、調度:線程作為調度和分配的基本單位,進程作為擁有資源的基本單位。2、并發性:不僅進程之間可以并發執行,同一個進程的多個線程之間也可以并發執行。3、擁有資源:進程是擁有資源的一個獨立單位,線程不擁有系統資源,但可以訪問隸屬于進程的資源。4、系統開銷:在創建或撤銷進程的時候,由于系統都要為之分配和回收資源,導致系統的明顯大于創建或撤銷線程時的開銷。但進程有獨立的地址空間,進程崩潰后,在保護模式下不會對其他的進程產生影響,而線程只是一個進程中的不同的執行路徑。線程有自己的堆棧和局部變量,但線程之間沒有單獨的地址空間,一個線程死掉就等于整個進程死掉,所以多進程的程序要比多線程的程序健壯,但是在進程切換時,耗費的資源較大,效率要差些。
8.什么叫線程安全?
如果你的代碼所在的進程中有多個線程在同時運行,而這些線程可能會同時運行這段代碼。如果每次運行結果和單線程運行的結果是一樣的,而且其他的變量 的值也和預期的是一樣的,就是線程安全的。一個線程安全的計數器類的同一個實例對象在被多個線程使用的情況下也不會出現計算失誤。很顯然你可以將集合類分 成兩組,線程安全和非線程安全的。
9.線程的幾種狀態?
1、新建狀態(New):新創建了一個線程對象。2、就緒狀態(Runnable):線程對象創建后,其他線程調用了該對象的 start()方法。該狀態的線程位于“可運行線程池”中,變得可運行,只等待獲取 [CPU ](http://product.it168.com/list/b/0217_1.shtml)的使用權。即在就緒狀態的進程除 CPU 之外,其它的運行所需資源都已全部獲得。3、運行狀態(Running):就緒狀態的線程獲取了 CPU,執行程序代碼。4、阻塞狀態(Blocked):阻塞狀態是線程因為某種原因放棄 CPU 使用權,暫時停止運行。直到線程進入就緒狀態,才有機會轉到運行狀態。阻塞的情況分三種:(1)、等待阻塞:運行的線程執行 wait()方法,該線程會釋放占用的所有資源,JVM 會把該線程放入“等待池”中。進入這個狀態后,是不能自動喚醒的,必須依靠其他線程調用 notify()或 notifyAll()方法才能被喚醒,(2)、同步阻塞:運行的線程在獲取對象的同步鎖時,若該同步鎖被別的線程占用,則 JVM 會把該線程放入“鎖池”中。(3)、其他阻塞:運行的線程執行 sleep()或 join()方法,或者發出了 I/O 請求時,JVM 會把該線程置為阻塞狀態。當 sleep()狀態超時、join()等待線程終止或者超時、或者 I/O 處理完畢時,線程重新轉入就緒狀態。5、死亡狀態(Dead):線程執行完了或者因異常退出了 run()方法,該線程結束生命周期。
10.volatile 變量和 atomic 變量有什么不同?
volatile 變量和 atomic 變量看起來很像,但功能卻不一樣。Volatile 變量可以確保先行關系,即寫操作會發生在后續的讀操作之前, 但它并不能保證原子性。例如用 volatile 修飾 count 變量那么 count++ 操作就不是原子性的。而 AtomicInteger 類提供的 atomic 方法可以讓這種操作具有原子性如 getAndIncrement()方法會原子性 的進行增量操作把當前值加一,其它數據類型和引用變量也可以進行相似操作。
11.Java 中什么是靜態條件?
競態條件會導致程序在并發情況下出現一些 bugs。多線程對一些資源的競爭的時候就會產生競態條件,如果首先要執行的程序競爭失敗排到后面執行了, 那么整個程序就會出現一些不確定的 bugs。這種 bugs 很難發現而且會重復出現,因為線程間的隨機競爭。
12.Java 中如何停止一個線程?
Java 提供了很豐富的 API 但沒有為停止線程提供 API。JDK 1.0 本來有一些像 stop(), suspend()和 resume()的控制方法但是由于潛在的死鎖威脅因此在后續的 JDK 版本中他們被棄用了,之后 Java API 的設計者就沒有提供一個兼容且線程安全的方法來停止一個線程。當 run() 或者 call() 方法執行完的時候線程會自動結束,如果要手動結束一個線程,你可以用 volatile 布爾變量來退出 run()方法的循環或者是取消任務來中斷線程。
13.線程池的優點?
1)重用存在的線程,減少對象創建銷毀的開銷。
2)可有效的控制最大并發線程數,提高系統資源的使用率,同時避免過多資源競爭,避免堵塞。
3)提供定時執行、定期執行、單線程、并發數控制等功能。
14.volatile 的理解?
volatile 關鍵字的兩層語義
一旦一個共享變量(類的成員變量、類的靜態成員變量)被 volatile 修飾之后,那么就具備了兩層語義:1)保證了不同線程對這個變量進行操作時的可見性,即一個線程修改了某個變量的值,這新值對其他線程來說是立即可見的。
2)禁止進行指令重排序。
用 volatile 修飾之后,變量的操作:
第一:使用 volatile 關鍵字會強制將修改的值立即寫入主存;第二:使用 volatile 關鍵字的話,當線程 2 進行修改時,會導致線程 1 的工作內存中緩存變
量 stop 的緩存行無效(反映到硬件層的話,就是 CPU 的 L1 或者 L2 緩存中對應的緩存行無效);第三:由于線程 1 的工作內存中緩存變量 stop 的緩存行無效,所以線程 1 再次讀取變量 stop
的值時會去主存讀取。
15. 實現多線程有幾種方式?
在語言層面有兩種方式。 java.lang.Thread 類的實例就是一個線程但是它需要調用 java.lang.Runnable 接口來執行,由于線程類本身就是調用的 Runnable 接口所以你可以繼承 java.lang.Thread 類或者直接調用 Runnable 接口來重寫 run()方法實現線程。
16.Java 中 notify 和 notifyAll 有什么區別?
notify()方法不能喚醒某個具體的線程,所以只有一個線程在等待的時候它才有用武之地。而 notifyAll()喚醒所有線程并允許他們爭奪鎖確保了至少有一個線程能繼續運行。
17.什么是樂觀鎖和悲觀鎖?
1)樂觀鎖:就像它的名字一樣,對于并發間操作產生的線程安全問題持樂觀狀態,樂觀鎖認為競爭不總是會發生,因此它不需要持有鎖,將比較-替換這兩個動作作為一個原子操作嘗試去修改內存中的變量,如果失敗則表示發生沖突,那么就應該有相應的重試邏輯。
2)悲觀鎖:還是像它的名字一樣,對于并發間操作產生的線程安全問題持悲觀狀態,悲觀鎖認為競爭總是會發生,因此每次對某資源進行操作時,都會持有一個獨占的鎖,就像synchronized,不管三七二十一,直接上了鎖就操作資源了。
18.線程的創建方式?
方式一:繼承 Thread 類
方式二:實現 Runnable 接口
方式三:實現 Callable 接口
方式四:使用線程池的方式
19.線程池的作用?
創建線程要花費昂貴的資源和時間,如果任務來了才創建線程那么響應時間會變長,而且一個進程能創建的線程數有限。為了避免這些問題,在程序啟動的時 候就創建若干線程來響應處理,它們被稱為線程池,里面的線程叫工作線程。從 JDK1.5 開始,Java API 提供了 Executor 框架讓你可以創建不同的線程池。比如單線程池,每次處理一個任務;數目固定的線程池或者是緩存線程池(一個適合很多生存期短 的任務的程序的可擴展線程池)。
20.wait 和 sleep 的區別?
sleep 是線程類(Thread)的方法,導致此線程暫停執行指定時間,給執行機會給其他線程,但是監控狀態依然保持,到時后會自動恢復。調用 sleep 不會釋放對象鎖。
wait 是 Object 類的方法,對此對象調用 wait 方法導致本線程放棄對象鎖,進入等待此對象的等待鎖定池,只有針對此對象發出 notify 方法(或 notifyAll)后本線程才進入對象鎖定池準備獲得對象鎖進入運行狀態。
21.產生死鎖的條件?
1、互斥條件:一個資源每次只能被一個進程使用。
2、請求與保持條件:一個進程因請求資源而阻塞時,對已獲得的資源保持不放。
3、不剝奪條件:進程已獲得的資源,在末使用完之前,不能強行剝奪。
4、循環等待條件:若干進程之間形成一種頭尾相接的循環等待資源關系。
22.請寫出實現線程安全的幾種方式?
方式一:使用同步代碼塊
方式二:使用同步方法
方式三:使用 ReentrantLock
23.守護線程是什么?它和非守護線程的區別?
程序運行完畢,jvm 會等待非守護線程完成后關閉,但是 jvm 不會等待守護線程.守護線程最典型的例子就是 GC 線程.
24.什么是多線程的上下文切換?
多線程的上下文切換是指 CPU 控制權由一個已經正在運行的線程切換到另外一個就緒并等待獲取 CPU 執行權的線程的過程.
25.Callable 和 Runnable 的區別是什么?
兩者都能用來編寫多線程,但實現 Callable 接口的任務線程能返回執行結果,而實現 Runnable 接口的任務線程不能返回結果.Callable 通常需要和 Future/FutureTask 結合使用,用于獲取異步計算結果.
26.線程阻塞有哪些原因?
1、sleep() 允許 指定以毫秒為單位的一段時間作為參數,它使得線程在指定的時間內進入阻塞狀態,不能得到 CPU 時間,指定的時間一過,線程重新進入可執行狀態。典型地,sleep()被用在等待某個資源就緒的情形:測試發現條件不滿足后,讓線程阻塞一段時間后重新測試,直到條件滿足為止
2、suspend() 和 resume() 兩個方法配套使用,suspend()使得線程進入阻塞狀態,并且不會
自動恢復,必須其對應的 resume() 被調用,才能使得線程重新進入可執行狀態。典型地, suspend() 和 resume() 被用在等待另一個線程產生的結果的情形:測試發現結果還沒有產生后,讓線程阻塞,另一個線程產生了結果后,調用 resume() 使其恢復。
3、yield() 使當前線程放棄當前已經分得的 CPU 時間,但不使當前線程阻塞,即線程仍處于可執行狀態,隨時可能再次分得 CPU 時間。調用 yield() 的效果等價于調度程序認為該線程已執行了足夠的時間從而轉到另一個線程4、wait() 和 notify() 兩個方法配套使用,wait() 使得線程進入阻塞狀態,它有兩種形式,
一種允許 指定以毫秒為單位的一段時間作為參數,另一種沒有參數,前者當對應的 notify() 被調用或者超出指定時間時線程重新進入可執行狀態,后者則必須對應的 notify() 被調用.
27.synchronized 和 Lock 的區別?
主要相同點:Lock 能完成 synchronized 所實現的所有功能
主要不同點:Lock 有比 synchronized 更精確的線程語義和更好的性能。synchronized 會自動釋放鎖,而 Lock 一定要求程序員手工釋放,并且必須在 finally 從句中釋放。
28.ThreadLocal 是什么?有什么作用?
ThreadLocal 是一個本地線程副本變量工具類。主要用于將私有線程和該線程存放的副本對象做一個映射,各個線程之間的變量互不干擾,在高并發場景下,可以實現無狀態的調用,特別適用于各個線程依賴不通的變量值完成操作的場景。
簡單說 ThreadLocal 就是一種以空間換時間的做法,在每個 Thread 里面維護了一個以開地址法實現的 ThreadLocal.ThreadLocalMap,把數據進行隔離,數據不共享,自然就沒有線程安全方面的問題了。
29.交互方式分為同步和異步兩種?
同步交互:指發送一個請求,需要等待返回,然后才能夠發送下一個請求,有個等待過程;異步交互:指發送一個請求,不需要等待返回,隨時可以再發送下一個請求,即不需要等待。區別:一個需要等待,一個不需要等待,在部分情況下,我們的項目開發中都會優先選擇不需要等待的異步交互方式。
30.什么是線程?
線程是操作系統能夠進行運算調度的最小單位,它被包含在進程之中,是進程中的實際運作單位。程序員可以通過它進行多處理器編程,你可以使用多線程對 運算密集型任務提速
31.什么是 FutureTask?
在 Java 并發程序中 FutureTask 表示一個可以取消的異步運算。它有啟動和取消運算、查詢運算是否完成和取回運算結果等方法。只有當運算完成的時候結果才能取回,如果運算尚未完成 get 方法將會阻塞。一個 FutureTask 對象可以對調用了 Callable 和 Runnable 的對象進行包 裝,由于 FutureTask 也是調用了 Runnable 接口所以它可以提交給 Executor 來執行。
32.Java 中 interrupted 和 isInterruptedd 方法的區別?
interrupted() 和 isInterrupted()的主要區別是前者會將中斷狀態清除而后者不會。Java 多線程的中斷機制是用內部標識來實現的,調用 Thread.interrupt()來中斷一個線程就會設置中斷標識為 true。當中斷線程調用靜態方法 Thread.interrupted()來 檢查中斷狀態時,中斷狀態會被清零。而非靜態方法 isInterrupted()用來查詢其它線程的中斷狀態且不會改變中斷狀態標識。簡單的說就是任何拋 出 InterruptedException 異常的方法都會將中斷狀態清零。無論如何,一個線程的中斷狀態有有可能被其它線程調用中斷來改變。
33.死鎖的原因?
1)是多個線程涉及到多個鎖,這些鎖存在著交叉,所以可能會導致了一個鎖依賴的閉環。例如:線程在獲得了鎖 A 并且沒有釋放的情況下去申請鎖 B,這時,另一個線程已經獲得了鎖 B,在釋放鎖 B 之前又要先獲得鎖 A,因此閉環發生,陷入死鎖循環。
2)默認的鎖申請操作是阻塞的。
所以要避免死鎖,就要在一遇到多個對象鎖交叉的情況,就要仔細審查這幾個對象的類中的所有方法,是否存在著導致鎖依賴的環路的可能性。總之是盡量避免在一個同步方法中調用其它對象的延時方法和同步方法。
34.什么是自旋
很多 synchronized 里面的代碼只是一些很簡單的代碼,執行時間非常快,此時等待的線程都加鎖可能是一種不太值得的操作,因為線程阻塞涉及到用戶態和內核態切換的問題。既然 synchronized 里面的代碼執行得非常快,不妨讓等待鎖的線程不要被阻塞,而是在 synchronized 的邊界做忙循環,這就是自旋。如果做了多次忙循環發現還沒有獲得鎖,再阻塞,這樣可能是一種更好的策略。
35.怎么喚醒一個阻塞的線程?
如果線程是因為調用了 wait()、sleep()或者 join()方法而導致的阻塞,可以中斷線程,并且通過拋出 InterruptedException 來喚醒它;如果線程遇到了 IO 阻塞,無能為力,因為 IO 是操作系統實現的,Java 代碼并沒有辦法直接接觸到操作系統。
36.如果提交任務時,線程池隊列已滿,這時會發生什么?
許多程序員會認為該任務會阻塞直到線程池隊列有空位。事實上如果一個任務不能被調度執行,那么ThreadPoolExecutor’s submit()方法將會拋出一個 RejectedExecutionException 異常。
37.什么是線程局部變量?
線程局部變量是局限于線程內部的變量,屬于線程自身所有,不在多個線程間共享。Java 提供 ThreadLocal 類來支持線程局部變量,是一種實現線程安全的方式。但是在管理環境下(如 web 服務器)使用線程局部變量的時候要特別小心,在這種情況下,工作線程的生命周期比任何應用變量的生命周期都要長。任何線程局部變量一旦在工作完成后沒有釋放,Java 應用就存在內存泄露的風險。
38.使用 volatile 關鍵字的場景?
synchronized 關鍵字是防止多個線程同時執行一段代碼,那么就會很影響程序執行效率,而 volatile 關鍵字在某些情況下性能要優于 synchronized,但是要注意 volatile 關鍵字是無法替代 synchronized 關鍵字的,因為 volatile 關鍵字無法保證操作的原子性。通常來說,使用 volatile 必須具備以下 2 個條件:
1)對變量的寫操作不依賴于當前值
2)該變量沒有包含在具有其他變量的不變式中
39.線程池的工作原理,幾個重要參數?
ThreadPoolExecutor(intcorePoolSize,intmaximumPoolSize,longkeepAliveTi me,TimeUnit unit,BlockingQueue workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler)
參數說明: corePoolSize 核心線程數
maximumPoolSize 最大線程數,一般大于等于核心線程數 keepAliveTime 線程存活時間(針對最大線程數大于核心線程數時,非核心線程) unit 存活時間單位,和線程存活時間配套使用 workQueue 任務隊列 threadFactory 創建線程的工程 handler 拒絕策略
40.線程池的類型?
五種線程池:
ExecutorService threadPool = null;
threadPool = Executors.newCachedThreadPool();//有緩沖的線程池,線程數 JVM 控制 threadPool = Executors.newFixedThreadPool(3);//固定大小的線程池
threadPool = Executors.newScheduledThreadPool(2);
threadPool = Executors.newSingleThreadExecutor();//單線程的線程池,只有一個線程在工
作
threadPool = new ThreadPoolExecutor();//默認線程池,可控制參數比較多
41.線程池的阻塞隊列有哪些?
三種阻塞隊列:
BlockingQueue workQueue = null;
workQueue = new ArrayBlockingQueue<>(5);//基于數組的先進先出隊列,有界 workQueue = new LinkedBlockingQueue<>();//基于鏈表的先進先出隊列,無界 workQueue = new SynchronousQueue<>();//無緩沖的等待隊列,無界
42.線程池的拒絕策略都有哪些?
四種拒絕策略
等待隊列已經排滿了,再也塞不下新任務,同時線程池中線程也已經達到 maximumPoolSize 數量,無法繼續為新任務服務,這個時候就需要使用拒絕策略來處理。
RejectedExecutionHandler rejected = null;
rejected = new ThreadPoolExecutor.AbortPolicy();//默認,隊列滿了丟任務拋出異常,直接拋出 RejectedExecutionException 異常阻止系統正常運行。
rejected = new ThreadPoolExecutor.DiscardPolicy();//隊列滿了丟任務不異常,直接丟棄任
務,不予任何處理也不拋出異常。如果允許任務丟失,這是最好的一種方案。
rejected = new ThreadPoolExecutor.DiscardOldestPolicy();//將最早進入隊列的任務刪,之后再嘗試加入隊列, 拋棄隊列中等待最久的任務,然后把當前任務加入隊列中嘗試再次提交當前任務。
rejected = new ThreadPoolExecutor.CallerRunsPolicy();//如果添加到線程池失敗,那么主線程會自己去執行該任務,調用者運行”一種調節機制,該策略既不會丟棄任務,也不會拋出異常,而是將某些任務回退給調用者,從而降低新任務的流量。
六、設計模式
1.說一下你熟悉的設計模式?
單例模式:保證被創建一次,節省系統開銷。
工廠模式(簡單工廠、抽象工廠):解耦代碼。
觀察者模式:定義了對象之間的一對多的依賴,這樣一來,當一個對象改變時,它的所有的依賴者都會收到通知并自動更新。
外觀模式:提供一個統一的接口,用來訪問子系統中的一群接口,外觀定義了一個高層的接口,讓子系統更容易使用。
模版方法模式:定義了一個算法的骨架,而將一些步驟延遲到子類中,模版方法使得子類可以在不改變算法結構的情況下,重新定義算法的步驟。
狀態模式:允許對象在內部狀態改變時改變它的行為,對象看起來好像修改了它的類。
2.簡單工廠和抽象工廠的區別
簡單工廠:用來生產同一等級結構中的任意產品,對于增加新的產品,無能為力。工廠方法:用來生產同一等級結構中的固定產品,支持增加任意產品。
抽象工廠:用來生產不同產品族的全部產品,對于增加新的產品,無能為力;支持增加產品族。
3.設計模式的優點?
設計模式可在多個項目中重用。
設計模式提供了一個幫助定義系統架構的解決方案。設計模式吸收了軟件工程的經驗。
設計模式為應用程序的設計提供了透明性。
設計模式是被實踐證明切實有效的,由于它們是建立在專家軟件開發人員的知識和經驗之上的。
4.設計模式的六大基本原則?
(1)單一原則(Single Responsibility Principle):一個類只負責一項職責,盡量做到類的只有一個
行為原因引起變化;
(2)里氏替換原則(LSP liskov substitution principle):子類可以擴展父類的功能,但不能改變
原有父類的功能;
(3)依賴倒置原則(dependence inversion principle):面向接口編程;
(4)接口隔離(interface segregation principle):建立單一接口;
(5)迪米特原則(law of demeter LOD):最少知道原則,盡量降低類與類之間的耦合;
(6)開閉原則(open closed principle):用抽象構建架構,用實現擴展原則;
5.單例模式?
單例就是該類只能返回一個實例。單例所具備的特點:1.私有化的構造函數2.私有的靜態的全局變量3.公有的靜態的方法
單例分為懶漢式、餓漢式和雙層鎖式餓漢式:
public class Singleton1 { private Singleton1() {};
private static Singleton1 single = new Singleton1(); public static Singleton1 getInstance() {
return single;
}
}
懶漢式:
public class Singleton2 { private Singleton2() {}
private static Singleton2 single=null; public tatic Singleton2 getInstance() {
if (single == null) {
single = new Singleton2();
}
return single;
}
}
線程安全: public class Singleton3 {
private Singleton3() {}
private static Singleton3 single ; public static Singleton3 getInstance() {
if(null == single){ synchronized(single ){
if(null == single){
single = new Singleton3();
}
}
}
return single;
}
}
6.設計模式的分類?
\1. 根據目的來分根據模式是用來完成什么工作來劃分,這種方式可分為創建型模式、結構型模式和行為型模式 3 種。
創建型模式:用于描述“怎樣創建對象”,它的主要特點是“將對象的創建與使用分離”。 GoF 中提供了單例、原型、工廠方法、抽象工廠、建造者等 5 種創建型模式。
結構型模式:用于描述如何將類或對象按某種布局組成更大的結構,GoF 中提供了代理、適配器、橋接、裝飾、外觀、享元、組合等 7 種結構型模式。
行為型模式:用于描述類或對象之間怎樣相互協作共同完成單個對象都無法單獨完成的任務,以及怎樣分配職責。GoF 中提供了模板方法、策略、命令、職責鏈、狀態、觀察者、中介者、迭代器、訪問者、備忘錄、解釋器等 11 種行為型模式。
\2. 根據作用范圍來分
根據模式是主要用于類上還是主要用于對象上來分,這種方式可分為類模式和對象模式兩種。
類模式:用于處理類與子類之間的關系,這些關系通過繼承來建立,是靜態的,在編譯時刻便確定下來了。GoF 中的工廠方法、(類)適配器、模板方法、解釋器屬于該模式。
對象模式:用于處理對象之間的關系,這些關系可以通過組合或聚合來實現,在運行時刻是可以變化的,更具動態性。GoF 中除了以上 4 種,其他的都是對象模式。
7.23 種設計模式的具體的每種模式的功能?
單例(Singleton)模式:某個類只能生成一個實例,該類提供了一個全局訪問點供外部獲取該實例,其拓展是有限多例模式。
原型(Prototype)模式:將一個對象作為原型,通過對其進行復制而克隆出多個和原型類似的新實例。
工廠方法(Factory Method)模式:定義一個用于創建產品的接口,由子類決定生產什么產品。
抽象工廠(AbstractFactory)模式:提供一個創建產品族的接口,其每個子類可以生產一系列相關的產品。
建造者(Builder)模式:將一個復雜對象分解成多個相對簡單的部分,然后根據不同需要分別創建它們,最后構建成該復雜對象。
代理(Proxy)模式:為某對象提供一種代理以控制對該對象的訪問。即客戶端通過代理間接地訪問該對象,從而限制、增強或修改該對象的一些特性。
適配器(Adapter)模式:將一個類的接口轉換成客戶希望的另外一個接口,使得原本由于接口不兼容而不能一起工作的那些類能一起工作。
橋接(Bridge)模式:將抽象與實現分離,使它們可以獨立變化。它是用組合關系代替繼承關系來實現,從而降低了抽象和實現這兩個可變維度的耦合度。
裝飾(Decorator)模式:動態的給對象增加一些職責,即增加其額外的功能。
外觀(Facade)模式:為多個復雜的子系統提供一個一致的接口,使這些子系統更加容易被訪問。
享元(Flyweight)模式:運用共享技術來有效地支持大量細粒度對象的復用。
組合(Composite)模式:將對象組合成樹狀層次結構,使用戶對單個對象和組合對象具有一致的訪問性。
模板方法(TemplateMethod)模式:定義一個操作中的算法骨架,而將算法的一些步驟延遲到子類中,使得子類可以不改變該算法結構的情況下重定義該算法的某些特定步驟。
策略(Strategy)模式:定義了一系列算法,并將每個算法封裝起來,使它們可以相互替換,且算法的改變不會影響使用算法的客戶。
命令(Command)模式:將一個請求封裝為一個對象,使發出請求的責任和執行請求的責任分割開。
職責鏈(Chain of Responsibility)模式:把請求從鏈中的一個對象傳到下一個對象,直到請求被響應為止。通過這種方式去除對象之間的耦合。
狀態(State)模式:允許一個對象在其內部狀態發生改變時改變其行為能力。
觀察者(Observer)模式:多個對象間存在一對多關系,當一個對象發生改變時,把這種改變通知給其他多個對象,從而影響其他對象的行為。
中介者(Mediator)模式:定義一個中介對象來簡化原有對象之間的交互關系,降低系統中對象間的耦合度,使原有對象之間不必相互了解。
迭代器(Iterator)模式:提供一種方法來順序訪問聚合對象中的一系列數據,而不暴露聚合對象的內部表示。
訪問者(Visitor)模式:在不改變集合元素的前提下,為一個集合中的每個元素提供多種訪問方式,即每個元素有多個訪問者對象訪問。
備忘錄(Memento)模式:在不破壞封裝性的前提下,獲取并保存一個對象的內部狀態,以便以后恢復它。
解釋器(Interpreter)模式:提供如何定義語言的文法,以及對語言句子的解釋方法,即解釋器。
8.UML 是什么?
統一建模語言(Unified Modeling Language,UML)是用來設計軟件藍圖的可視化建模語言, 1997 年被國際對象管理組織(OMG)采納為面向對象的建模語言的國際標準。它的特點是簡單、統一、圖形化、能表達軟件設計中的動態與靜態信息。
統一建模語言能為軟件開發的所有階段提供模型化和可視化支持。而且融入了軟件工程領域的新思想、新方法和新技術,使軟件設計人員溝通更簡明,進一步縮短了設計時間,減少開發成本。它的應用領域很寬,不僅適合于一般系統的開發,而且適合于并行與分布式系統的建模。
UML 從目標系統的不同角度出發,定義了用例圖、類圖、對象圖、狀態圖、活動圖、時序圖、協作圖、構件圖、部署圖等 9 種圖。
9.橋接模式是什么?
橋接(Bridge)模式的定義如下:將抽象與實現分離,使它們可以獨立變化。它是用組合關系代替繼承關系來實現,從而降低了抽象和實現這兩個可變維度的耦合度。
橋接(Bridge)模式的優點是:
由于抽象與實現分離,所以擴展能力強;其實現細節對客戶透明。
缺點是:由于聚合關系建立在抽象層,要求開發者針對抽象化進行設計與編程,這增加了系統的理解與設計難度類適配器:
10. 策略模式是什么?
策略(Strategy)模式的定義:該模式定義了一系列算法,并將每個算法封裝起來,使它們可以相互替換,且算法的變化不會影響使用算法的客戶。策略模式屬于對象行為模式,它通過對算法進行封裝,把使用算法的責任和算法的實現分割開來,并委派給不同的對象對這些算法進行管理。
策略模式的主要優點如下多重條件語句不易維護,而使用策略模式可以避免使用多重條件語句。
策略模式提供了一系列的可供重用的算法族,恰當使用繼承可以把算法族的公共代碼轉移到父類里面,從而避免重復的代碼。策略模式可以提供相同行為的不同實現,客戶可以根據不同時間或空間要求選擇不同的。策略模式提供了對開閉原則的完美支持,可以在不修改原代碼的情況下,靈活增加新算法。策略模式把算法的使用放到環境類中,而算法的實現移到具體策略類中,實現了二者的分離。其主要缺點如下客戶端必須理解所有策略算法的區別,以便適時選擇恰當的算法類。策略模式造成很多的策略類
七、開源框架
相同點:
1)都屬于 ORM 框架
2)都是對 jdbc 的包裝
3)都屬于持久層的框架
不同點:
1)hibernate 是面向對象的,mybatis 是面向 sql 的;
2)hibernate 全自動的 orm,mybatis 是半自動的 orm;
3)hibernate 查詢映射實體對象必須全字段查詢,mybatis 可以不用;
4)hibernate 級聯操作,mybatis 則沒有;
5)hibernate 編寫 hql 查詢數據庫大大降低了對象和數據庫的耦合性,mybatis 提供動態 sql,需要手寫 sql,與數據庫之間的耦合度取決于程序員所寫的 sql 的方法,所以 hibernate 的移植性要遠大于 mybatis。
6)hibernate 有方言夸數據庫,mybatis 依賴于具體的數據庫。
7)hibernate 擁有完整的日志系統,mybatis 則相對比較欠缺。
1、基于 SQL 語句編程,相當靈活,不會對應用程序或者數據庫的現有設計造成任何影響, SQL 寫在 XML 里,解除 sql 與程序代碼的耦合,便于統一管理;提供 XML 標簽,支持編寫動態 SQL 語句,并可重用。
2、與 JDBC 相比,減少了 50%以上的代碼量,消除了 JDBC 大量冗余的代碼,不需要手動開關連接;3、很好的與各種數據庫兼容(因為 MyBatis 使用 JDBC 來連接數據庫,所以只要 JDBC 支持的數據庫 MyBatis 都支持)。
4、能夠與 Spring 很好的集成;
5、提供映射標簽,支持對象與數據庫的 ORM 字段關系映射;提供對象關系映射標簽,支持對象關系組件維護。
3.MyBatis 框架的缺點?
(1)SQL 語句的編寫工作量較大,尤其當字段多、關聯表多時,對開發人員編寫 SQL 語句的功底有一定要求。
(2)SQL 語句依賴于數據庫,導致數據庫移植性差,不能隨意更換數據庫。
1、用戶發送請求至前端控制器 DispatcherServlet。
2、DispatcherServlet 收到請求調用 HandlerMapping 處理器映射器。
3、處理器映射器根據請求 url 找到具體的處理器,生成處理器對象及處理器攔截器(如果有則生成)一并返回給 DispatcherServlet。
4、DispatcherServlet 通過 HandlerAdapter 處理器適配器調用處理器。
5、執行處理器(Controller,也叫后端控制器)。
6、Controller 執行完成返回 ModelAndView
7、HandlerAdapter 將 controller 執行結果 ModelAndView 返回給 DispatcherServlet
8、DispatcherServlet 將 ModelAndView 傳給 ViewReslover 視圖解析器
9、ViewReslover 解析后返回具體 View
10、DispatcherServlet 對 View 進行渲染視圖(即將模型數據填充至視圖中)。
11、DispatcherServlet 響應用戶。
4.什么是SpringMVC?
SpringMVC是 Spring 提供的一個 mvc 模式的框架。
5.MyBatis 框架使用的場合?
(1)MyBatis 專注于 SQL 本身,是一個足夠靈活的 DAO 層解決方案。
(2)對性能的要求很高,或者需求變化較多的項目,如互聯網項目,MyBatis 將是不錯的選擇。
6.Spring 中 beanFactory 和 ApplicationContext 的聯系和區別?
BeanFactory 是 spring 中較為原始的 Factory,無法支持 spring 的許多插件,如 AOP 功能、
Web 應用等。
ApplicationContext 接口是通過 BeanFactory 接口派生而來的,除了具備 BeanFactory 接口的
功能外,還具備資源訪問、事件傳播、國際化消息訪問等功能。總體區別如下:
1)使用 ApplicationContext,配置 bean 默認配置是 singleton,無論是否使用,都會被實例化。
優點是預先加載,缺點是浪費內存;2)使用 BeanFactory 實例化對象時,配置的 bean 等到使用的時候才會被實例化。優點是節
約內存,缺點是速度比較慢,多用于移動設備的開發;
3)沒有特殊要求的情況下,應該使用 ApplicationContext 完成,ApplicationContext 可以實現
BeanFactory 所有可實現的功能,還具備其他更多的功能。
7.SpringIOC 注入的幾種方式?
構造器注入
set 方法注入
接口注入
8.攔截器與過濾器的區別?
? 1、攔截器是基于 java 的反射機制的,而過濾器是基于函數回調
? 2、攔截器不依賴與 servlet 容器,過濾器依賴與 servlet 容器。
? 3、攔截器只能對 action 請求起作用,而過濾器則可以對幾乎所有的請求起作用。
? 4、攔截器可以訪問 action 上下文、值棧里的對象,而過濾器不能訪問。
? 5、在 action 的生命周期中,攔截器可以多次被調用,而過濾器只能在容器初始化時被調用一次
9.SpringIOC 是什么?
Spring IOC 負責創建對象,管理對象(通過依賴注入(DI),裝配對象,配置對象,并且管理這些對象的整個生命周期。
10.AOP 有哪些實現方式?
實現 AOP 的技術,主要分為兩大類:
靜態代理 - 指使用 AOP 框架提供的命令進行編譯,從而在編譯階段就可生成 AOP 代理類,因此也稱為編譯時增強;編譯時編織(特殊編譯器實現)
類加載時編織(特殊的類加載器實現)。
動態代理 - 在運行時在內存中“臨時”生成 AOP 動態代理類,因此也被稱為運行時增強。
-
解釋一下代理模式?
1、代理模式: 代理模式就是本該我做的事,我不做,我交給代理人去完成。就比如,我生產了一些產品,我自己不賣,我委托代理商幫我賣,讓代理商和顧客打交道,我自己負責主要產品的生產就可以了。 代理模式的使用,需要有本類,和代理類,本類和代理類共同實現統一的接口。然后在 main 中調用就可以了。本類中的業務邏輯一般是不會變動的,在我們需要的時候可以不斷的添加代理對象,或者修改代理類來實現業務的變更。
2、代理模式可以分為: 靜態代理 優點:可以做到在不修改目標對象功能的前提下,對目標功能擴展 缺點:因為本來和代理類要實現統一的接口,所以會產生很多的代理類,類太多,一旦接口增加方法,目標對象和代理對象都要維護。 動態代理(JDK 代理/接口代理)代理對象,不需要實現接口,代理對象的生成,是利用 JDK 的 API,動態的在內存中構建代理對象,需要我們指定代理對象/目標對象實現的接口的類型。 Cglib 代理 特點: 在內存中構建一個子類對象,從而實現對目標對象功能的擴展。
3、使用場景: 修改代碼的時候。不用隨便去修改別人已經寫好的代碼,如果需要修改的話,可以通過代理的方式來擴展該方法。 隱藏某個類的時候,可以為其提供代理類 當我們要擴展某個類功能的時候,可以使用代理類 當一個類需要對不同的調用者提供不同的調用權限的時候,可以使用代理類來實現。 減少本類代碼量的時候。 需要提升處理速度的時候。就比如我們在訪問某個大型系統的時候,一次生成實例會耗費大量的時間,我們可以采用代理模式,當用來需要的時候才生成實例,這樣就能提高訪問的速度。
12.Mybatis 是如何把 sql 執行結果封裝為目標對象?都有哪些映射形式?
第一種是使用<resultMap>標簽,逐一定義數據庫列名和對象屬性名之間的映射關系。第二種是使用 sql 列的別名功能,將列的別名書寫為對象屬性名。
有了列名與屬性名的映射關系后,Mybatis 通過反射創建對象,同時使用反射給對象的屬性逐一賦值并返回,那些找不到映射關系的屬性,是無法完成賦值的。
13.Spring bean 的生命周期?
1、Spring 容器根據配置中的 bean 定義中實例化 bean。
2、Spring 使用依賴注入填充所有屬性,如 bean 中所定義的配置。
3 、如果 bean | 實現 BeanNameAware 接口,則工廠通過傳遞 bean 的 ID 來調用 |
---|---|
setBeanName()。 | |
4 、如果 bean | 實現 BeanFactoryAware 接口,工廠通過傳遞自身的實例來調用 |
setBeanFactory()。 |
5、如果存在與 bean 關聯的任何 BeanPostProcessors,則調用 preProcessBeforeInitialization()
方法。
6、如果為 bean 指定了 init 方法( 的 init-method 屬性),那么將調用它。
7 、 最 后 , 如 果 存 在 與 bean 關 聯 的 任 何 BeanPostProcessors , 則 將 調 用
postProcessAfterInitialization() 方法。
8、如果 bean 實現 DisposableBean 接口,當 spring 容器關閉時,會調用 destory()。9、如果為 bean 指定了 destroy 方法( 的 destroy-method 屬性),那么將調用它。
51
14.Spring 框架中都用到了哪些設計模式?
代理模式,在 AOP 中被使用最多。
單例模式,在 Spring 配置文件中定義 bean 的時候默認的是單例模式。
工廠模式, BeanFactory 用來創建對象的實例。
模板方法, 用來解決重復性代碼。
前端控制器,Spring 提供了 DispatcherSerclet 來對請求進行分發。視圖幫助,Spring 提供了一系列的 JSP 標簽。
依賴注入,它是慣穿于 BeanFactory/ApplicationContext 接口的核心理念。
15.Spring 中的事件處理?
1、Spring 的核心是 ApplicatonContext,它負責管理 bean 的完整的生命周期。Spring 提供了以下 內 置 事 件 : ContextRefreshedEvent ContextStartedEvent ContextStoppedEvent ContextClosedEvent RequestHandleEvent 2、由于 Spring 的事件處理是單線程的,所以如果一個事件被發布,直至并且除非所有的
接收者得到的該消息,該進程被阻塞并且流程將不會繼續。因此,如果事件處理被使用,在設計應用程序時應注意。
3、監聽上下文事件
4、自定義事件
16.使用 Sping 框架的好處是什么?
1、簡化開發,解耦,集成其它框架。
2、低侵入式設計,代碼污染級別低。
3、Spring 的 DI 機制降低了業務對象替換的復雜性,提高了軟件之間的解耦。
4、Spring AOP 支持將一些通用的任務進行集中式的管理,例如:安全,事務,日志等,從而使代碼能更好的復用。
17.解釋 Spring 支持的幾種 bean 的作用域?
當通過 Spring 容器創建一個 Bean 實例的時候,不僅可以完成 bean 實例的實力化,還可以為 bean 指定作用域。Spring bean 元素的支持以下 5 種作用域:
Singleton:單例模式,在整個 spring IOC 容器中,使用 singleton 定義的 bean 將只有一個實
例。
Prototype:多例模式,每次通過容器中的 getBean 方法獲取 prototype 定義的 beans 時,都會產生一個新的 bean 的實例。
Request:對于每次 Http 請求,使用 request 定義的 bean 都會產生一個新的實例,只有在 web 應用時候,該作用域才會有效。
Session:對于每次 Http Session,使用 session 定義的 Bean 都將產生一個新的實例。Globalsession:每個全局的 Http Sesisonn,使用 session 定義的本都將產生一個新的實例
18.在 Spring 中如何注入一個 java 集合?
Spring 提供理論四種集合類的配置元素:
lt;List&: 該標簽用來裝配 有重復值的 list 值
lt;set&: 該標簽用來裝配沒有重復值的 set 值lt;map&:該標簽科以用來注入鍵值對 lt;props&: 該標簽用來支持注入鍵值對和字符串類型鍵值對。
19.什么是 Spring bean?
它們是構成用戶應用程序主干的對象。
Bean 由 Spring IoC 容器管理。
它們由 Spring IoC 容器實例化,配置,裝配和管理。 Bean 是基于用戶提供給容器的配置元數據創建。
20.什么是 spring 自動裝配?
就是將一個 Bean 注入到其它的 Bean 的 Property 中,默認情況下,容器不會自動裝配,需要我們手動設定。Spring 可以通過向 Bean Factory 中注入的方式來搞定 bean 之間的依賴關系,達到自動裝配的目的。
自動裝配建議少用,如果要使用,建議使用 ByName
21.自動裝配有哪些方式?
1、no - 這是默認設置,表示沒有自動裝配。應使用顯式 bean 引用進行裝配。
2、byName - 它根據 bean 的名稱注入對象依賴項。它匹配并裝配其屬性與 XML 文件中由相同名稱定義的 bean。
3、byType - 它根據類型注入對象依賴項。如果屬性的類型與 XML 文件中的一個 bean 名稱匹配,則匹配并裝配屬性。4、構造函數 - 它通過調用類的構造函數來注入依賴項。它有大量的參數。
5、autodetect - 首先容器嘗試通過構造函數使用 autowire 裝配,如果不能,則嘗試通過 byType 自動裝配。
22.自動裝配有什么局限?
1、覆蓋的可能性 - 您始終可以使用 和 設置指定依賴項,這將
覆蓋自動裝配。
2、基本元數據類型 - 簡單屬性(如原數據類型,字符串和類)無法自動裝配。
3、令人困惑的性質 - 總是喜歡使用明確的裝配,因為自動裝配不太精確。
23.Spring 的重要注解?
@Controller - 用于 Spring MVC 項目中的控制器類。 @Service - 用于服務類。
@RequestMapping - 用于在控制器處理程序方法中配置 URI 映射。
@ResponseBody - 用于發送 Object 作為響應,通常用于發送 XML 或 JSON 數據作為響應。
@PathVariable - 用于將動態值從 URI 映射到處理程序方法參數。
@Autowired - 用于在 spring bean 中自動裝配依賴項。
@Qualifier - 使用 @Autowired 注解,以避免在存在多個 bean 類型實例時出現混淆。 @Scope - 用于配置 spring bean 的范圍。
@Configuration,@ComponentScan 和 @Bean - 用于基于 java 的配置。@Aspect,@Before,@After,@Around,@Pointcut - 用于切面編程(AOP)。
24.@Component, @Controller, @Repository, @Service 有何區別?
1、 @Component:這將 java 類標記為 bean。它是任何 Spring 管理組件的通用構造型。 spring 的組件掃描機制現在可以將其拾取并將其拉入應用程序環境中。
2、@Controller:這將一個類標記為 Spring Web MVC 控制器。標有它的 Bean 會自動導入到 IoC 容器中。
3、@Service:此注解是組件注解的特化。它不會對 @Component 注解提供任何其他行為。您可以在服務層類中使用 @Service 而不是 @Component,因為它以更好的方式指定了意圖。
4、@Repository:這個注解是具有類似用途和功能的 @Component 注解的特化。它為 DAO
提供了額外的好處。它將 DAO 導入 IoC 容器,并使未經檢查的異常有資格轉換為 Spring DataAccessException。
25.列舉 spring 支持的事務管理類型?
Spring 支持兩種類型的事務管理:
程序化事務管理:在此過程中,在編程的幫助下管理事務。它為您提供極大的靈活性,但維護起來非常困難。
聲明式事務管理:在此,事務管理與業務代碼分離。僅使用注解或基于 XML 的配置來管理事務。
26.Spring 框架的事物管理有哪些優點?
1、它為不同的事務 API(如 JTA, JDBC, Hibernate, JPA, 和 JDO)提供了統一的編程模型。
2、它為編程式事務管理提供了一個簡單的 API 而非一系列復雜的事務 API(如 JTA).
3、它支持聲明式事務管理。
4、它可以和 Spring 的多種數據訪問技術很好的融合。
27.Spring AOP(面向切面)編程的原理?
1、AOP 面向切面編程,它是一種思想。它就是針對業務處理過程中的切面進行提取,以達到優化代碼的目的,減少重復代碼的目的。 就比如,在編寫業務邏輯代碼的時候,我們習慣性的都要寫:日志記錄,事物控制,以及權限控制等,每一個子模塊都要寫這些代碼,代碼明顯存在重復。這時候,我們運用面向切面的編程思想,采用橫切技術,將代碼中重復的部分,不影響主業務邏輯的部分抽取出來,放在某個地方進行集中式的管理,調用。 形成日志切面,事物控制切面,權限控制切面。 這樣,我們就只需要關系業務的邏輯處理,即提高了工作的效率,又使得代碼變的簡潔優雅。這就是面向切面的編程思想,它是面向對象編程思想的一種擴展。
2、AOP 的使用場景: 緩存、權限管理、內容傳遞、錯誤處理、懶加載、記錄跟蹤、優化、
校準、調試、持久化、資源池、同步管理、事物控制等。 AOP 的相關概念: 切面(Aspect)
連接點(JoinPoint) 通知(Advice) 切入點(Pointcut) 代理(Proxy): 織入(WeaVing)3、Spring AOP 的編程原理? 代理機制 JDK 的動態代理:只能用于實現了接口的類產生代理。 Cglib 代理:針對沒有實現接口的類產生代理,應用的是底層的字節碼增強技術,生成當前類的子類對象。
28.Spring MVC 框架有什么用?
Spring Web MVC 框架提供 模型-視圖-控制器 架構和隨時可用的組件,用于開發靈活且松散耦合的 Web 應用程序。 MVC 模式有助于分離應用程序的不同方面,如輸入邏輯,業務邏輯和 UI 邏輯,同時在所有這些元素之間提供松散耦合。
29.介紹一下 WebApplicationContext?
WebApplicationContext 是 ApplicationContext 的擴展。它具有 Web 應用程序所需的一些額外功能。它與普通的 ApplicationContext 在解析主題和決定與哪個 servlet 關聯的能力方面有所不同。
30.SpringMVC 和 struts2 的區別有哪些?
一、攔截機制的不同
Struts2 是類級別的攔截,每次請求就會創建一個 Action,和 Spring 整合時 Struts2 的 ActionBean 注入作用域是原型模式 prototype,然后通過 setter,getter 吧 request 數據注入到屬性。Struts2 中,一個 Action 對應一個 request,response 上下文,在接收參數時,可以通過屬性接收,這說明屬性參數是讓多個方法共享的。Struts2 中 Action 的一個方法可以對
應一個 url,而其類屬性卻被所有方法共享,這也就無法用注解或其他方式標識其所屬方法了,只能設計為多例。
SpringMVC 是方法級別的攔截,一個方法對應一個 Request 上下文,所以方法直接基本上是獨立的,獨享 request,response 數據。而每個方法同時又何一個 url 對應,參數的傳遞是直接注入到方法中的,是方法所獨有的。處理結果通過 ModeMap 返回給框架。在 Spring
整合時,SpringMVC 的 Controller Bean 默認單例模式 Singleton,所以默認對所有的請求,只會創建一個 Controller,有應為沒有共享的屬性,所以是線程安全的,如果要改變默認的作用域,需要添加@Scope 注解修改。
Struts2 有自己的攔截 Interceptor 機制,SpringMVC 這是用的是獨立的 Aop 方式,這樣導致 Struts2 的配置文件量還是比 SpringMVC 大。
二、底層框架的不同
Struts2 采用 Filter(StrutsPrepareAndExecuteFilter)實現,SpringMVC(DispatcherServlet)
則采用 Servlet 實現。Filter 在容器啟動之后即初始化;服務停止以后墜毀,晚于 Servlet。Servlet在是在調用時初始化,先于 Filter 調用,服務停止后銷毀。
三、性能方面
Struts2 是類級別的攔截,每次請求對應實例一個新的 Action,需要加載所有的屬性值注入,SpringMVC 實現了零配置,由于 SpringMVC 基于方法的攔截,有加載一次單例模式 bean 注入。所以,SpringMVC 開發效率和性能高于 Struts2。
四、配置方面
spring MVC 和 Spring 是無縫的。從這個項目的管理和安全上也比 Struts2 高。
31.Mybatis 中#{}和${}的區別是什么?
#{}是預編譯處理,KaTeX parse error: Expected 'EOF', got '#' at position 22: …替換。 Mybatis 在處理#?{}時,會將 sql 中的#{…{}時,就是把${}替換成變量的值。使用#{}可以有效的防止 SQL 注入,提高系統安全性。
32.Spring 中@Autowire 與@Resource 的區別?
@Autowire 默認按照類型裝配,默認情況下它要求依賴對象必須存在如果允許為 null,可以設置它 required 屬性為 false,如果我們想使用按照名稱裝配,可以結合@Qualifier 注解一起使用;
@Resource 默認按照名稱裝配,當找不到與名稱匹配的 bean 才會按照類型裝配,可以通過 name 屬性指定,如果沒有指定 name 屬性,當注解標注在字段上,即默認取字段的名稱作為 bean 名稱尋找依賴對象,當注解標注在屬性的 setter 方法上,即默認取屬性名作為 bean 名稱尋找依賴對象
33.什么是控制反轉(IOC),什么是依賴注入(DI)?
IOC:就是對象之間的依賴關系由容器來創建,對象之間的關系本來是由我們開發者自己創建和維護的,在我們使用 Spring 框架后,對象之間的關系由容器來創建和維護,將開發者做的事讓容器做,這就是控制反轉。BeanFactory 接口是 Spring Ioc 容器的核心接口。
DI:我們在使用 Spring 容器的時候,容器通過調用 set 方法或者是構造器來建立對象之間的依賴關系。
控制反轉是目標,依賴注入是我們實現控制反轉的一種手段。
34.Spring 運行原理?
1、內部最核心的就是 IOC 了,之前是 new 對象,現在可以直接從容器中獲取,動態注入,這其實就是利用 java 里的反射。反射其實就是在運行時動態的去創建、調用對象,Spring就是在運行時,根據 xml Spring 的配置文件來動態的創建對象,和調用對象里的方法的。
2、Spring 另一個核心就是 AOP 面向切面編程,可以為某一類對象 進行監督和控制(也就是在調用這類對象的具體方法的前后去調用你指定的 模塊)從而達到對一個模塊擴充的功能。這些都是通過配置類達到的。(日志、事務等)
3、Spring 目的:就是讓對象與對象(模塊與模塊)之間的關系沒有通過代碼來關聯,都是通過配置類說明管理的(Spring 根據這些配置 內部通過反射去動態的組裝對象)要記住: Spring 是一個容器,凡是在容器里的對象才會有 Spring 所提供的這些服務和功能。
4、Spring 里用的最經典設計模式:模板方法模式。(有興趣同學可以了解一下)、核心容器組件是 BeanFactory,它是工廠模式的實現。BeanFactory 使用控制反轉(IOC)模式將應用程序的配置和依賴性規范與實際的應用程序代碼分開。
35.Spring 的事務傳播行為?
PROPAGATION(蔓延、傳播、傳輸)
事務傳播行為類型 說明
事務傳播行為類型 說明
如果當前沒有事務,就新建一個事務,如果已經存在一個事務
PROPAGATION_REQUIRED
中,加入到這個事務中。這是默認的事務傳播行為
PROPAGATION_SUPPORTS 支持當前事務,如果當前沒有事務,就以非事務方式執行。
PROPAGATION_MANDATORY 使用當前的事務,如果當前沒有事務,就拋出異常。
新建事務,如果當前存在事務,把當前事務掛起。(一個新的 PROPAGATION_REQUIRES_NEW 事務將啟動,而且如果有一個現有的事務在運行的話,則這個方法將在運行期被掛起,直到新的事務提交或者回滾才恢復執
行。)
以非事務方式執行操作,如果當前存在事務,就把當前事務掛
PROPAGATION_NOT_SUPPORTED 起。
PROPAGATION_NEVER 以非事務方式執行,如果當前存在事務,則拋出異常。
如果當前存在事務,則在嵌套事務內執行。如果當前沒有事務,
則執行與 PROPAGATION_REQUIRED 類似的操作。(外層事務拋
PROPAGATION_NESTED
出異常回滾,那么內層事務必須回滾,反之內層事務并不影響外層事務)
36. SpringMVC 的運行流程?
DispatcherServlet 前置控制器 |HandlerMapping 請求映射(到 Controller)
|HandlerAdapter 請求映射(到 Controller 類的方法上)|Controller 控制器| HandlerIntercepter 攔截器|ViewResolver 視圖映射|View 視圖處理
八、分布式相關
1.Redis 和 Memcache 的區別?
1、存儲方式 Memecache 把數據全部存在內存之中,斷電后會掛掉,數據不能超過內存大小。 Redis 有部份存在硬盤上,redis 可以持久化其數據
2、數據支持類型 memcached 所有的值均是簡單的字符串,redis 作為其替代者,支持更為豐富的數據類型 ,提供 list,set,zset,hash 等數據結構的存儲
3、使用底層模型不同 它們之間底層實現方式 以及與客戶端之間通信的應用協議不一樣。 Redis 直接自己構建了 VM 機制 ,因為一般的系統調用系統函數的話,會浪費一定的時間去移動和請求。
4、value 值大小不同:Redis 最大可以達到 1gb;memcache 只有 1mb。5、redis 的速度比 memcached 快很多6、Redis 支持數據的備份,即 master-slave 模式的數據備份。
2.使用 Redis 有哪些好處?
- 速度快,因為數據存在內存中,類似于 HashMap,HashMap 的優勢就是查找和操作的時間復雜度都是 O(1)
- 支持豐富數據類型,支持 string,list,set,sorted set,hash
- 支持事務,操作都是原子性,所謂的原子性就是對數據的更改要么全部執行,要么全部不執行
- 豐富的特性:可用于緩存,消息,按 key 設置過期時間,過期后將會自動刪除
3.什么是 redis 持久化?rdb 和 aof 的比較?
持久化就是把內存的數據寫到磁盤中去,防止服務宕機了內存數據丟失。比較:
1、aof 文件比 rdb 更新頻率高,優先使用 aof 還原數據。
2、aof 比 rdb 更安全也更大
3、rdb 性能比 aof 好
4、如果兩個都配了優先加載 AOF
4.Redis 最適合的場景?
(1)、會話緩存(Session Cache)
最常用的一種使用 Redis 的情景是會話緩存(session cache)。用 Redis 緩存會話比其他存儲(如 Memcached)的優勢在于:Redis 提供持久化。
(2)、全頁緩存(FPC)
除基本的會話 token 之外,Redis 還提供很簡便的 FPC 平臺。回到一致性問題,即使重啟了 Redis 實例,因為有磁盤的持久化,用戶也不會看到頁面加載速度的下降,這是一個極大改進,類似 PHP 本地 FPC。
再次以 Magento 為例,Magento 提供一個插件來使用 Redis 作為全頁緩存后端。
此外,對 WordPress 的用戶來說,Pantheon 有一個非常好的插件 wp-redis,這個插件能幫助你以最快速度加載你曾瀏覽過的頁面。
(3)、隊列
Reids 在內存存儲引擎領域的一大優點是提供 list 和 set 操作,這使得 Redis 能作為一個很好的消息隊列平臺來使用。Redis 作為隊列使用的操作,就類似于本地程序語言(如 Python)
對 list 的 push/pop 操作。
(4),排行榜/計數器
Redis 在內存中對數字進行遞增或遞減的操作實現的非常好。集合(Set)和有序集合(Sorted Set)也使得我們在執行這些操作的時候變的非常簡單,Redis 只是正好提供了這兩種數據結構。所以,我們要從排序集合中獲取到排名最靠前的 10 個用戶–我們稱之為“user_scores”,我們只需要像下面一樣執行即可:
(5)、發布/訂閱
最后(但肯定不是最不重要的)是 Redis 的發布/訂閱功能。發布/訂閱的使用場景確實非常多。我已看見人們在社交網絡連接中使用,還可作為基于發布/訂閱的腳本觸發器,甚至用 Redis 的發布/訂閱功能來建立聊天系統!
5.redis 哈希槽的概念?
Redis 集群沒有使用一致性 hash,而是引入了哈希槽的概念,Redis 集群有 16384 個哈希槽,每個 key 通過 CRC16 校驗后對 16384 取模來決定放置哪個槽,集群的每個節點負責一部分 hash 槽。
6.怎么理解 Redis 事務?
事務是一個單獨的隔離操作:事務中的所有命令都會序列化、按順序地執行,事務在執行的過程中,不會被其他客戶端發送來的命令請求所打斷。事務是一個原子操作:事務中的命令要么全部被執行,要么全部都不執行。
7.redis 的淘汰策略有哪些?
noeviction:返回錯誤當內存限制達到并且客戶端嘗試執行會讓更多內存被使用的命令(大部分的寫入指令,但 DEL 和幾個例外) allkeys-lru: 嘗試回收最少使用的鍵(LRU),使得新添加的數據有空間存放。
volatile-lru: 嘗試回收最少使用的鍵(LRU),但僅限于在過期集合的鍵,使得新添加的數據有空間存放。 allkeys-random: 回收隨機的鍵使得新添加的數據有空間存放。
volatile-random: 回收隨機的鍵使得新添加的數據有空間存放,但僅限于在過期集合的鍵。
volatile-ttl: 回收在過期集合的鍵,并且優先回收存活時間(TTL)較短的鍵,使得新添加的數據有空間存放。
8.redis 有哪些數據結構?
String、List、Set、Zset(Sorted Set)、hash
9.redis 緩存穿透、緩存雪崩、緩存擊穿?
緩存穿透:無效 ID,在 redis 緩存中查不到,去查詢 DB,造成 DB 壓力增大。解決方法:
1、解決方法 1:布隆過濾器,提供一個很大的 Bit-Map,提供多個 hash 函數,分別對查詢參數值【比如 UUID】,進行求 hash,然后分別對多個 hash 結果,在對應位置對比是否全為 1 或者某個位置為 0,一旦有一個位置標識為 0,表示本次查詢 UUID,不存在于緩存,再去查詢 DB.起到一個再過濾的效果。
2、解決方法 2:把無效的 ID,也在 redis 緩存起來,并設置一個很短的超時時間。緩存雪崩:緩存同一時間批量失效,導致大量的訪問直接訪問 DB 解決方法:
在做緩存時候,就做固定失效時間+隨機時間段,保證所有的緩存不會同一時間失效緩存擊穿:在緩存失效的時候,會有高并發訪問失效的緩存【熱點數據】解決方法:
最簡單的解決方法,就是將熱點數據設置永不超時!
第二個解決方法:對訪問的 Key 加上互斥鎖,請求的 Key 如果不存在,則加鎖,去數據庫取,新請求過來,如果相同 KEy,則暫停 10s 再去緩存取值;如果 Key 不同,則直接去緩存取!
10.redis 如何實現高并發?
redis 通過一主多從,主節點負責寫,從節點負責讀,讀寫分離,從而實現高并發。
11.redis 如何實現高可用?
主備切換,哨兵集群,主節點宕機的情況下,自動選舉出一個從節點變成主節點,從而保證了 redis 集群的高可用。
12.redis 單線程還能處理速度那么快?
首先,redis 是單進程單線程的 k-v 內存型可持久化數據庫。單線程還能處理速度很快的原因:
1、redis 操作是基于內存的,內存的讀寫速度非常快
2、正是由于 redis 的單線程模式,避免了線程上下文切換的損耗
3、redis 采用的 IO 多路復用技術,可以很好的解決多請求并發的問題。 多路代表多請求,復用代表多個請求重復使用同一個線程。
13.為什么 Redis 的操作是原子性的,怎么保證原子性?
對于 Redis 而言,命令的原子性指的是:一個操作的不可以再分,操作要么執行,要么不執行。
Redis 的操作之所以是原子性的,是因為 Redis 是單線程的。
Redis 本身提供的所有 API 都是原子操作,Redis 中的事務其實是要保證批量操作的原子性。多個命令在并發中也是原子性的嗎?
不一定, 將 get 和 set 改成單命令操作,incr 。使用 Redis 的事務,或者使用 Redis+Lua==
的方式實現.
14.redis 的主從復制的實現過程?
1、從服務發送一個 sync 同步命令給主服務要求全量同步
2、主服務接收到從服務的 sync 同步命令時,會 fork 一個子進程后臺執行 bgsave 命令(非阻塞)快照保存,生成 RDB 文件,并將 RDB 文件發送給從服務
3、從服務再將接收到的 RDB 文件載入自己的 redis 內存
4、待從服務將 RDB 載入完成后,主服務再將緩沖區所有寫命令發送給從服務
5、從服務在將主服務所有的寫命令載入內存從而實現數據的完整同步
6、從服務下次在需要同步數據時只需要發送自己的 offset 位置(相當于 mysql binlog 的位置)即可,只同步新增加的數據,再不需要全量同步
15.redis 的哨兵機制的作用?
1、監控:Sentinel 會不斷的檢查主服務器和從服務器是否正常運行。
2、通知:當被監控的某個 redis 服務器出現問題,Sentinel 通過 API 腳本向管理員或者其他的應用程序發送通知。
3、自動故障轉移:當主節點不能正常工作時,Sentinel 會開始一次自動的故障轉移操作,它會將與失效主節點是主從關系 的其中一個從節點升級為新的主節點,并且將其他的從節點指向新的主節點。
16.redis 常見的性能問題和解決方案?
- Master 最好不要做任何持久化工作,如 RDB 內存快照和 AOF 日志文件
- 如果數據比較重要,某個 Slave 開啟 AOF 備份數據,策略設置為每秒同步一次
- 為了主從復制的速度和連接的穩定性, Master 和 Slave 最好在同一個局域網內
- 盡量避免在壓力很大的主庫上增加從庫
- 主從復制不要用圖狀結構,用單向鏈表結構更為穩定,即: Master <- Slave1 <- Slave2 <-Slave3…
17.分布式緩存?
硬盤上的數據,緩存在別的計算機上(非程序運行的計算機)的內存上,而且可以緩存的計算機的個數不止一個,可以使用 n 個用戶通過訪問 http 服務器,然后訪問應用服務器資源,應用服務器調用后端的數據庫,在第一次訪問的時候,直接訪問數據庫,然后將要緩存的內容放入到 memcached 集群,集群規模根據緩存文件的大小而定。在第二次訪問的時候就直接進入緩存讀取,不需要進行數據庫的操作。這個適合數據變化不頻繁的場景,比如:互聯網站顯示的榜單,閱讀排行等。
18.什么是 Nginx?
Nginx 是一個高性能的 HTTP 和反向代理服務器,及電子郵件代理服務器,同時也是一個非常高效的反向代理、負載平衡。
19.nginx 相對于 apache 的優點?
輕量級,同樣起 web 服務,比 apache 占用更少的內存及資源抗并發,nginx 處理請求是異步非阻塞的,而 apache 則是阻塞型的,在高并發下 nginx 能保持低資源低消耗高性能高度模塊化的設計,編寫模塊相對簡單
社區活躍,各種高性能模塊出品迅速啊 rewrite ,比 nginx 的 rewrite 強大模塊超多,基本想到的都可以找到
少 bug ,nginx 的 bug 相對較多
20. Nginx 優化的方式?
Nginx 運行工作進程數量 Nginx 運行工作進程個數一般設置 CPU 的核心或者核心數 x2 Nginx 運行 CPU 親和力比如 4 核配置: worker_processes 4;
worker_cpu_affinity 0001 0010 0100 1000 Nginx 最大打開文件數 worker_rlimit_nofile 65535; Nginx 事件處理模型 events {
use epoll; worker_connections 65535; multi_accept on;
}
nginx 采用 epoll 事件模型,處理效率高。開啟高效傳輸模式連接超時時間
主要目的是保護服務器資源,CPU,內存,控制連接數,因為建立連接也是需要消耗資源的
21.Nginx 如何處理一個請求的?
首先,nginx 在啟動時,會解析配置文件,得到需要監聽的端口與 ip 地址,然后在 nginx 的 master 進程里面先初始化好這個監控的 socket,再進行 listen,然后再 fork 出多個子進程出來, 子進程會競爭 accept 新的連接。此時,客戶端就可以向 nginx 發起連接了。當客戶端與 nginx 進行三次握手,與 nginx 建立好一個連接后,此時,某一個子進程會 accept 成功,然后創建 nginx 對連接的封裝,即 ngx_connection_t 結構體,接著,根據事件調用相應的事件處理模塊,如 http 模塊與客戶端進行數據的交換。最后,nginx 或客戶端來主動關掉連接,到此,一個連接就壽終正寢了
22.Nginx 是如何實現高并發的?
nginx 之所以可以實現高并發,與它采用的 epoll 模型有很大的關系。epoll 模型采用異步非阻塞的事件處理機制。這種機制可讓 nginx 進程同時監控多個事件。
簡單來說,就是異步非阻塞,使用了 epoll 模型和大量的底層代碼優化。如果深入一點的話,
就是 nginx 的特殊進程模型和事件模型的設計,才使其可以實現高并發。
23.Nginx 的進程模型?
它是采用一個 master 進程和多個 worker 進程的工作模式。
1、master 進程主要負責收集、分發請求。當一個請求過來時,master 拉起一個 worker 進程負責處理這個請求。;2、master 進程也要負責監控 worker 的狀態,保證高可靠性;
3、worker 進程議案設置為和 CPU 核心數一致或者其二倍。nginx 的 worker 進程和 Apache
的不一樣。apache 的進程在同一時間只能處理一個請求,所以它會開啟很多個進程,幾百甚至幾千個。而 nginx 的 worker 進程在同一時間可以處理的請求數只受內存限制,因此可以處理更多請求。
24.Nginx 負載均衡的 4 種分配方式?
1、輪詢(默認)
每個請求按時間順序逐一分配到不同的后端服務器,如果后端服務器 down 掉,能自動剔除。
2、weight
指定輪詢幾率,weight 和訪問比率成正比,用于后端服務器性能不均的情況。
3、ip_hash
每個請求按訪問 ip 的 hash 結果分配,這樣每個訪客固定訪問一個后端服務器,可以解決的問題。
4、fair(第三方)
按后端服務器的響應時間來分配請求,響應時間短的優先分配。
、url_hash(第三方)
按訪問 url 的 hash 結果來分配請求,使同樣的 url 定向到同一個后端服務器,后端服務器為緩存時比較有效
25.為什么要用 Nginx?
跨平臺、配置簡單,非阻塞、高并發連接:處理 2-3 萬并發連接數,官方監測能支持 5 萬并發,內存消耗小:開啟 10 個 nginx 才占 150M 內存,nginx 處理靜態文件好,耗費內存少,
內置的健康檢查功能:如果有一個服務器宕機,會做一個健康檢查,再發送的請求就不會發送到宕機的服務器了。重新將請求提交到其他的節點上。
節省寬帶:支持 GZIP 壓縮,可以添加瀏覽器本地緩存穩定性高:宕機的概率非常小
接收用戶請求是異步的:瀏覽器將請求發送到 nginx 服務器,它先將用戶請求全部接收下來,再一次性發送給后端 web 服務器,極大減輕了 web 服務器的壓力,一邊接收 web 服務器的返回數據,一邊發送給瀏覽器客戶端, 網絡依賴性比較低,只要 ping 通就可以負載均衡,可以有多臺 nginx 服務器使用 dns 做負載均衡,事件驅動:通信機制采用 epoll 模型(nio2 異步非阻塞)
26.什么是正向代理?
一個位于客戶端和原始服務器之間的服務器,為了從原始服務器取得內容,客戶端向代理發送一個請求并指定目標(原始服務器),然后代理向原始服務器轉交請求并將獲得的內容返回給客戶端。客戶端才能使用正向代理正向代理總結就一句話:代理端代理的是客戶端
27.什么是反向代理?
反向代理是指以代理服務器來接受 internet 上的連接請求,然后將請求,發給內部網絡上的服務器,并將從服務器上得到的結果返回給 internet 上請求連接的客戶端,此時代理服務器對外就表現為一個反向代理服務器反向代理總結就一句話:代理端代理的是服務端
28.什么是負載均衡?
負載均衡即是代理服務器將接收的請求均衡的分發到各服務器中,負載均衡主要解決網絡擁塞問題,提高服務器響應速度,服務就近提供,達到更好的訪問質量,減少后臺服務器大并發壓力
29.Nginx 的調度算法有哪些?
1、sticky:通過 nginx-sticky 模塊,來實現 cookie 黏貼的方式將來自同一個客戶端的請求發送到同一個后端服務器上處理,這樣一定程度上可以解決多個后端服務器的 session 會話同步的問題;2、round-robin(RR):輪詢,每個請求按時間順序依次分配到不同的后端服務器,如果后端某臺服務器死機,自動剔除故障系統,使用戶訪問不受影響;3、weight:輪詢權重,weight 的值越大分配到的訪問概率就越高,主要用于后端每臺服務器性能不均衡的情況下,或者僅僅為在主從的情況下設置不同的權重,達到合理有效的利用主機資源。
4、least_conn:請求被發送到當前活躍連接最少的 realserver 上,會考慮到 weight 的值;
5、ip_hash:每個請求按照 IP 的哈希結果分配,使來自同一個 IP 的訪客固定訪問后端服務器,可以有效的解決動態網頁存在的 session 共享問題。
6、fair:比 weight、ip_hash 更加智能的負載均衡算法,fair 算法可以根據頁面的大小和加載時間長短智能地進行負載均衡,也就是根據后端服務器的響應時間來分配請求,相應時間短的優先分配。nginx 本身不支持 fair,如果需要使用這種調度算法,則必須安裝 upstream_fair 模塊。
7、url_hash:按訪問的 URL 的哈希結果來分配請求,使每個 URL 定向到后端服務器,可以進一步提高后端緩存服務器的效率。同樣,nginx 本身不支持 url_hash,如果需要這種調度算法,則必須安裝 nginx 的 hash 軟件包。
30.Nginx 負載均衡調度狀態?
常用的狀態有:
1、down:表示當前的 server 暫時不參與負載均衡;
2、backup:預留的備份機器。當其他所有的非 backup 機器出現故障或者繁忙的時候,才會請求 backup 機器,因此這臺機器的訪問壓力最低;
3、max_fails:允許請求失敗的次數,默認為 1,當超過最大次數時,返回 proxy_next_upstraem
模塊定義的錯誤;
4、fail_timeout:請求失敗超時時間,在經歷了 max_fails 次失敗后,暫停服務的時間。max_fails和 fail_timeout 可以一起使用。
31.可以從哪些方面來優化 nginx 服務?
1、配置 nginx 的 proxy 緩存;
2、對靜態頁面開啟壓縮功能,如 br 壓縮或者 gzip 壓縮;
3、調整 nginx 運行工作進程個數,最多開啟 8 個,8 個以上話性能就不會再提升了,而且穩定性變得更低,所以 8 個足夠用了;4、調整 nginx 運行 CPU 的親和力;
5、修改 nginx 最多可打開的文件數,若超過系統限制的最多打開文件數(ulimit -n 命令查看系統的最多打開文件數),還需要修改系統默認的文件數;6、修改單個 worker 的最大連接數;7、開啟高效傳輸;
8、設置連接超時時間,以便保護服務器資源,因為建立連接也是需要消耗資源的;
9、優化 fastCGI 的一個超時時間,也可以根據實際情況對其配置緩存動態頁面;10、expires 緩存調優,主要針對圖片、css、js 等元素更改較少的情況下使用。11、配置防盜鏈;12、優化內核參數,如進程可以同時打開的最大句柄數;開啟 tcp 重用機制,以便允許
TIME_WAIT sockets 重新用于新的 TCP 連接
32.為什么要用 MQ?
1、解耦:如果多個模塊或者系統中,互相調用很復雜,維護起來比較麻煩,但是這個調用又不是同步調用,就可以運用 MQ 到這個業務中。2、異步:這個很好理解,比如用戶的操作日志的維護,可以不用同步處理,節約響應時間。3、削峰:在高峰期的時候,系統每秒的請求量達到 5000,那么調用 MySQL 的請求也是5000,一般情況下 MySQL 的請求大概在 2000 左右,那么在高峰期的時候,數據庫就被打垮了,那系統就不可用了。此時引入 MQ,在系統 A 前面加個 MQ,用戶請求先到 MQ,系統 A 從 MQ 中每秒消費 2000 條數據,這樣就把本來 5000 的請求變為 MySQL 可以接受的請求數量了,可以保證系統不掛掉,可以繼續提供服務。MQ 里的數據可以慢慢的把它消費掉。
33.使用 MQ 會有什么問題?
(1)降低了系統可用性 (2)增加了系統的復雜性
34.怎么保證 MQ 的高可用?
RabbitMQ 是比較有代表性的,因為是基于主從做高可用性的。以他為例,自行查閱以下模式。 rabbitmq 有三種模式:單機模式、普通集群模式、鏡像集群模式。
35.MQ 的優缺點?
在特殊場景下有其對應的好處,解耦、異步、削峰。缺點有以下幾個:
系統可用性降低系統引入的外部依賴越多,越容易掛掉。萬一 MQ 掛了,MQ 一掛,整套系統崩潰,你不就完了?
系統復雜度提高硬生生加個 MQ 進來,你怎么保證消息沒有重復消費?怎么處理消息丟失的情況?怎么保證消息傳遞的順序性?問題一大堆。
一致性問題
A 系統處理完了直接返回成功了,人都以為你這個請求就成功了;但是問題是,要是 BCD 三個系統那里,BD 兩個系統寫庫成功了,結果 C 系統寫庫失敗了,咋整?你這數據就不一致了。
36.Kafka、ActiveMQ、RabbitMQ、RocketMQ 都有什么區別?
對于吞吐量來說 kafka 和 RocketMQ 支撐高吞吐,ActiveMQ 和 RabbitMQ 比他們低一個數量級。對于延遲量來說 RabbitMQ 是最低的。
1.從社區活躍度
按照目前網絡上的資料,RabbitMQ、activeM 、ZeroMQ 三者中,綜合來看,RabbitMQ 是首
選。
2.持久化消息比較
ActiveMq 和 RabbitMq 都支持。持久化消息主要是指我們機器在不可抗力因素等情況下掛掉了,消息不會丟失的機制。
3.綜合技術實現
可靠性、靈活的路由、集群、事務、高可用的隊列、消息排序、問題追蹤、可視化管理工具、插件系統等等。
RabbitMq/Kafka 最好,ActiveMq 次之,ZeroMq 最差。當然 ZeroMq 也可以做到,不過自己
必須手動寫代碼實現,代碼量不小。尤其是可靠性中的:持久性、投遞確認、發布者證實和高可用性。
4.高并發
毋庸置疑,RabbitMQ 最高,原因是它的實現語言是天生具備高并發高可用的 erlang 語言。
5.比較關注的比較,RabbitMQ 和 Kafka RabbitMq 比 Kafka 成熟,在可用性上,穩定性上,可靠性上,RabbitMq 勝于 Kafka(理論上)。
另外,Kafka 的定位主要在日志等方面, 因為 Kafka 設計的初衷就是處理日志的,可以看做是一個日志(消息)系統一個重要組件,針對性很強,所以 如果業務方面還是建議選擇 RabbitMq 。
還有就是,Kafka 的性能(吞吐量、TPS)比 RabbitMq 要高出來很多。
37.如何設置消息的過期時間?
設置隊列屬性,隊列中所有消息都有相同的過期時間對消息本身進行單獨設置,每條消息的 TTL 可以不同
如果兩種方法一起使用,則消息的 TTL 以兩者之間較小的那個數值為準
38.消息的持久化是如何實現的?
RabbitMQ 的持久化分為:交換器的持久化、隊列的持久化和消息的持久化交換器和隊列的持久化都是通過在聲明時將 durable 參數置為 true 實現的消息的持久化是在發送消息指定 deliveryMode 為 2 實現的
39.Zookeeper 是什么?
ZooKeeper 是一個開放源碼的分布式協調服務,它是集群的管理者,監視著集群中各個節點的狀態根據節點提交的反饋進行下一步合理操作。最終,將簡單易用的接口和性能高效、功能穩定的系統提供給用戶。
40.Zookeeper 的應用場景?
數據發布/訂閱負載均衡命名服務
分布式協調/通知集群管理
Master 選舉
分布式鎖分布式隊列
41.四種類型的數據節點 Znode?
PERSISTENT-持久節點
除非手動刪除,否則節點一直存在于 Zookeeper 上
EPHEMERAL-臨時節點
臨時節點的生命周期與客戶端會話綁定,一旦客戶端會話失效(客戶端與 zookeeper 連接斷開不一定會話失效),那么這個客戶端創建的所有臨時節點都會被移除。
PERSISTENT_SEQUENTIAL-持久順序節點
基本特性同持久節點,只是增加了順序屬性,節點名后邊會追加一個由父節點維護的自增整型數字。
EPHEMERAL_SEQUENTIAL-臨時順序節點
基本特性同臨時節點,增加了順序屬性,節點名后邊會追加一個由父節點維護的自增整型數字。
42.Zookeeper Watcher 機制?
1、一次性
無論是服務端還是客戶端,一旦一個 Watcher 被觸發,Zookeeper 都會將其從相應的存儲中移除。這樣的設計有效的減輕了服務端的壓力,不然對于更新非常頻繁的節點,服務端會不斷的向客戶端發送事件通知,無論對于網絡還是服務端的壓力都非常大。
2、客戶端串行執行
客戶端 Watcher 回調的過程是一個串行同步的過程。3、輕量
3.1Watcher 通知非常簡單,只會告訴客戶端發生了事件,而不會說明事件的具體內容。 3.2 客戶端向服務端注冊 Watcher 的時候,并不會把客戶端真實的 Watcher 對象實體傳遞到服務端,僅僅是在客戶端請求中使用 boolean 類型屬性進行了標記。
4、watcher event 異步發送 watcher 的通知事件從 server 發送到 client 是異步的,這就存在一個問題,不同的客戶端和服務器之間通過 socket 進行通信,由于網絡延遲或其他因素導致客戶端在不通的時刻監聽到事件,由于 Zookeeper 本身提供了 ordering guarantee,即客戶端監聽事件后,才會感知它所監視 znode 發生了變化。所以我們使用 Zookeeper 不能期望能夠監控到節點每次的變化。Zookeeper 只能保證最終的一致性,而無法保證強一致性。
5、注冊 watcher getData、exists、getChildren
6、觸發 watcher create、delete、setData
7、當一個客戶端連接到一個新的服務器上時,watch 將會被以任意會話事件觸發。當與一個服務器失去連接的時候,是無法接收到 watch 的。而當 client 重新連接時,如果需要的話,所有先前注冊過的 watch,都會被重新注冊。通常這是完全透明的。只有在一個特殊情況下, watch 可能會丟失:對于一個未創建的 znode 的 exist watch,如果在客戶端斷開連接期間被創建了,并且隨后在客戶端連接上之前又刪除了,這種情況下,這個 watch 事件可能會被丟失。
43.Zookeeper 下 Server 工作狀態?
服務器具有四種狀態,分別是 LOOKING、FOLLOWING、LEADING、OBSERVING。LOOKING:尋找 Leader 狀態。當服務器處于該狀態時,它會認為當前集群中沒有 Leader,因此需要進入 Leader 選舉狀態。
FOLLOWING:跟隨者狀態。表明當前服務器角色是 Follower。LEADING:領導者狀態。表明當前服務器角色是 Leader。OBSERVING:觀察者狀態。表明當前服務器角色是 Observer。
44.Zookeeper 是如何保證事務的順序一直性的?
zookeeper 采用了全局遞增的事務 Id 來標識,所有的 proposal(提議)都在被提出的時候加上了 zxid,zxid 實際上是一個 64 位的數字,高 32 位是 epoch(時期; 紀元; 世; 新時代)用來標識 leader 周期,如果有新的 leader 產生出來,epoch 會自增,低 32 位用來遞增計數。當新產生 proposal 的時候,會依據數據庫的兩階段過程,首先會向其他的 server 發出事務執行請求,如果超過半數的機器都能執行并且能夠成功,那么就會開始執行。
45.ZK 節點宕機如何處理?
Zookeeper 本身也是集群,推薦配置不少于 3 個服務器。Zookeeper 自身也要保證當一個節點宕機時,其他節點會繼續提供服務。
如果是一個 Follower 宕機,還有 2 臺服務器提供訪問,因為 Zookeeper 上的數據是有多個副本的,數據并不會丟失;如果是一個 Leader 宕機,Zookeeper 會選舉出新的 Leader。
ZK 集群的機制是只要超過半數的節點正常,集群就能正常提供服務。只有在 ZK 節點掛得太多,只剩一半或不到一半節點能工作,集群才失效。所以3 個節點的 cluster 可以掛掉 1 個節點(leader 可以得到 2 票>1.5)2 個節點的 cluster 就不能掛掉任何 1 個節點了(leader 可以得到 1 票<=1)
46.Zookeeper 有哪幾種部署模式?
部署模式:單機模式、偽集群模式、集群模式。
47.Dubbo 內置了哪幾種容器?
Spring Container
Jetty Container
Log4j Container
48.Dubbo 里面有哪幾種角色?
Provider:暴露服務的服務提供方
Consumer:調用遠程服務的服務消費方
Registry:服務注冊與發現的注冊中心
Monitor:統計服務的調用次數和調用時間的監控中心
Container:服務運行容器
49.Dubbo 有哪幾種集群容錯方案,默認是那種?
Failover Cluster:失敗自動切換,自動重試其他服務器(默認)
Failfast Cluster:快速失敗,立即報錯,只發起一次調用
Failsafe Cluster:失敗安全,出現異常時,直接忽略
Failback Cluster:失敗自動恢復,記錄失敗請求,定時重發
Forking Cluster:并行調用多個服務器,只要一個成功即返回
Broadcast Cluster:廣播逐個調用所有提供者,任意一個報錯則報錯
50.Dubbo 有哪幾種負載均衡策略,默認是哪種?
Random LoadBalance:隨機,按權重設置隨機概率(默認)
RoundRobin LoadBalance:輪詢,按公約后的權重設置輪詢比率
LeastActive LoadBalance:最少活躍調用次數,相同活躍數的隨機
ConsistentHash LoadBalance:一直性 Hash,相同參數的請求總是發到同一提供者
51.Dubbo 的管理控制臺能做什么?
管理控制臺主要包含:路由規則,動態配置,服務降級,訪問控制,權重調整,負載均衡,等管理功能。
52.什么是 Spring Boot?
多年來,隨著新功能的增加,spring 變得越來越復雜。只需訪問 https://spring.io/projects 頁
面,我們就會看到可以在我們的應用程序中使用的所有 Spring 項目的不同功能。如果必須啟動一個新的 Spring 項目,我們必須添加構建路徑或添加 Maven 依賴關系,配置應用程序服務器,添加 spring 配置。因此,開始一個新的 spring 項目需要很多努力,因為我們現在必須從頭開始做所有事情。
Spring Boot 是解決這個問題的方法。Spring Boot 已經建立在現有 spring 框架之上。使用 spring
啟動,我們避免了之前我們必須做的所有樣板代碼和配置。因此,Spring Boot 可以幫助我們以最少的工作量,更加健壯地使用現有的 Spring 功能。
53.Spring Boot 有哪些優點?
減少開發,測試時間和努力。
使用 JavaConfig 有助于避免使用 XML。
避免大量的 Maven 導入和各種版本沖突。提供意見發展方法。
通過提供默認值快速開始開發。
沒有單獨的 Web 服務器需要。這意味著你不再需要啟動 Tomcat,Glassfish 或其他任何東西。需要更少的配置 因為沒有 web.xml 文件。只需添加用@ Configuration 注釋的類,然后添加用@Bean 注釋的方法,Spring 將自動加載對象并像以前一樣對其進行管理。您甚至可以將 @Autowired 添加到 bean 方法中,以使 Spring 自動裝入需要的依賴關系中。
基于環境的配置 使用這些屬性,您可以將您正在使用的環境傳遞到應用程序:
-Dspring.profiles.active = {enviornment} 。在加載主應用程序屬性文件后, Spring 將在(application{environment} .properties)中加載后續的應用程序屬性文件。
54.什么是 JavaConfig?
Spring JavaConfig 是 Spring 社區的產品,它提供了配置 Spring IoC 容器的純 Java 方法。因此它有助于避免使用 XML 配置。使用 JavaConfig 的優點在于:
面向對象的配置。由于配置被定義為 JavaConfig 中的類,因此用戶可以充分利用 Java 中的面向對象功能。一個配置類可以繼承另一個,重寫它的@Bean 方法等。
減少或消除 XML 配置。基于依賴注入原則的外化配置的好處已被證明。但是,許多開發人員不希望在 XML 和 Java 之間來回切換。JavaConfig 為開發人員提供了一種純 Java 方法來配置與 XML 配置概念相似的 Spring 容器。從技術角度來講,只使用 JavaConfig 配置類來配置容器是可行的,但實際上很多人認為將 JavaConfig 與 XML 混合匹配是理想的。
類型安全和重構友好。JavaConfig 提供了一種類型安全的方法來配置 Spring 容器。由于 Java 5.0 對泛型的支持,現在可以按類型而不是按名稱檢索 bean,不需要任何強制轉換或基于字符串的查找。
55.如何重新加載 Spring Boot 上的更改,而無需重新啟動服務器?
這可以使用 DEV 工具來實現。通過這種依賴關系,您可以節省任何更改,嵌入式 tomcat 將重新啟動。Spring Boot 有一個開發工具(DevTools)模塊,它有助于提高開發人員的生產力。 Java 開發人員面臨的一個主要挑戰是將文件更改自動部署到服務器并自動重啟服務器。開發人員可以重新加載 Spring Boot 上的更改,而無需重新啟動服務器。這將消除每次手動部署更改的需要。Spring Boot 在發布它的第一個版本時沒有這個功能。這是開發人員最需要的功能。DevTools 模塊完全滿足開發人員的需求。該模塊將在生產環境中被禁用。它還提供 H2 數據庫控制臺以更好地測試應用程序。
56.Spring Boot 中的監視器是什么?
Spring boot actuator 是 spring 啟動框架中的重要功能之一。Spring boot 監視器可幫助您訪問
生產環境中正在運行的應用程序的當前狀態。有幾個指標必須在生產環境中進行檢查和監控。即使一些外部應用程序可能正在使用這些服務來向相關人員觸發警報消息。監視器模塊公開了一組可直接作為 HTTP URL 訪問的 REST 端點來檢查狀態。
57.如何在 Spring Boot 中禁用 Actuator 端點安全性?
默認情況下,所有敏感的 HTTP 端點都是安全的,只有具有 ACTUATOR 角色的用戶才能訪問它們。安全性是使用標準的 HttpServletRequest.isUserInRole 方法實施的。 我們可以使用 management.security.enabled = false 來禁用安全性。只有在執行機構端點在防火墻后訪問時,才建議禁用安全性。
58.什么是Spring的注解
59.什么是 WebSocket?
WebSocket 是一種計算機通信協議,通過單個 TCP 連接提供全雙工通信信道。 WebSocket 是雙向的 -使用 WebSocket 客戶端或服務器可以發起消息發送。 WebSocket 是全雙工的 -客戶端和服務器通信是相互獨立的。
單個 TCP 連接 -初始連接使用 HTTP,然后將此連接升級到基于套接字的連接。然后這個單一連接用于所有未來的通信Light -與 http 相比,WebSocket 消息數據交換要輕得多。
60.什么是 Swagger?你用 Spring Boot 實現了它嗎?
Swagger 廣泛用于可視化 API,使用 Swagger UI 為前端開發人員提供在線沙箱。Swagger 是用于生成 RESTful Web 服務的可視化表示的工具,規范和完整框架實現。它使文檔能夠以與服務器相同的速度更新。當通過 Swagger 正確定義時,消費者可以使用最少量的實現邏輯來理解遠程服務并與其進行交互。因此,Swagger 消除了調用服務時的猜測。
61.什么是 Apache Kafka?
Apache Kafka 是一個分布式發布 - 訂閱消息系統。它是一個可擴展的,容錯的發布 - 訂閱消息系統,它使我們能夠構建分布式應用程序。這是一個 Apache 頂級項目。Kafka 適合離線和在線消息消費。
62.什么是 Spring Cloud?
Spring cloud 流應用程序啟動器是基于 Spring Boot 的 Spring 集成應用程序,提供與外部系統的集成。 Spring cloud Task,一個生命周期短暫的微服務框架,用于快速構建執行有限數據處理的應用程序。
63.使用 Spring Cloud 有什么優勢?
使用 Spring Boot 開發分布式微服務時,我們面臨以下問題
1、與分布式系統相關的復雜性-這種開銷包括網絡問題,延遲開銷,帶寬問題,安全問題。
2、服務發現-服務發現工具管理群集中的流程和服務如何查找和互相交談。它涉及一個服務目錄,在該目錄中注冊服務,然后能夠查找并連接到該目錄中的服務。
3、冗余-分布式系統中的冗余問題。
4、負載平衡 --負載平衡改善跨多個計算資源的工作負荷,諸如計算機,計算機集群,網絡鏈路,中央處理單元,或磁盤驅動器的分布。
5、性能-問題 由于各種運營開銷導致的性能問題。
6、部署復雜性-Devops 技能的要求。
64.服務注冊和發現是什么意思?Spring Cloud 如何實現?
當我們開始一個項目時,我們通常在屬性文件中進行所有的配置。隨著越來越多的服務開發和部署,添加和修改這些屬性變得更加復雜。有些服務可能會下降,而某些位置可能會發生變化。手動更改屬性可能會產生問題。 Eureka 服務注冊和發現可以在這種情況下提供幫助。由于所有服務都在 Eureka 服務器上注冊并通過調用 Eureka 服務器完成查找,因此無需處理服務地點的任何更改和處理。
65.什么是 Netflix Feign?它的優點是什么?
Feign 是受到 Retrofit,JAXRS-2.0 和 WebSocket 啟發的 java 客戶端聯編程序。Feign 的第一個目標是將約束分母的復雜性統一到 http apis,而不考慮其穩定性。在 employee-consumer 的例子中,我們使用了 employee-producer 使用 REST 模板公開的 REST 服務。
但是我們必須編寫大量代碼才能執行以下步驟
- 使用功能區進行負載平衡。
- 獲取服務實例,然后獲取基本 URL。
- 利用 REST 模板來使用服務。 前面的代碼如
之前的代碼,有像 NullPointer 這樣的例外的機會,并不是最優的。我們將看到如何使用 Netflix Feign 使呼叫變得更加輕松和清潔。如果 Netflix Ribbon 依賴關系也在類路徑中,那么 Feign 默認也會負責負載平衡。
66.什么是 Spring Cloud Bus?我們需要它嗎?
考慮以下情況:我們有多個應用程序使用 Spring Cloud Config 讀取屬性,而 Spring Cloud Config 從 GIT 讀取這些屬性。
下面的例子中多個員工生產者模塊從 Employee Config Module 獲取 Eureka 注冊的財產。
如果假設 GIT 中的 Eureka 注冊屬性更改為指向另一臺 Eureka 服務器,會發生什么情況。在這種情況下,我們將不得不重新啟動服務以獲取更新的屬性。
還有另一種使用執行器端點/刷新的方式。但是我們將不得不為每個模塊單獨調用這個 url。例如,如果
Employee Producer1 部署在端口 8080 上,則調用 http:// localhost:8080 / refresh。同樣對于
Employee Producer2 http:// localhost:8081 / refresh 等等。這又很麻煩。這就是 Spring Cloud Bus
發揮作用的地方。
Spring Cloud Bus 提供了跨多個實例刷新配置的功能。因此,在上面的示例中,如果我們刷新 Employee Producer1,則會自動刷新所有其他必需的模塊。如果我們有多個微服務啟動并運行,這特別有用。這是通過將所有微服務連接到單個消息代理來實現的。無論何時刷新實例,此事件都會訂閱到偵聽此代理的所有微服務,并且它們也會刷新。可以通過使用端點/總線/刷新來實現對任何單個實例的刷新。
67.什么是 Hystrix 斷路器?我們需要它嗎?
由于某些原因,employee-consumer 公開服務會引發異常。在這種情況下使用 Hystrix 我們定義了一個回退方法。如果在公開服務中發生異常,則回退方法返回一些默認值。
68. Elasticsearch是什么
Elasticsearch是一個基于Lucene的搜索引擎。它提供了具有HTTP Web界面和無架構JSON文檔的分布式,多租戶能力的全文搜索引擎。Elasticsearch是用Java開發的,根據Apache許可條款作為開源發布
69. Elasticsearch每一個索引默認的分片數和副本數
每個索引的主分片數,默認值是 5
。這個配置在索引創建后不能修改 , 每個主分片的副本數,默認值是 1
。對于活動的索引庫,這個配置可以隨時修改
70. Elasticsearch中的倒排索引是什么
倒排索引是搜索引擎的核心。搜索引擎的主要目標是在查找發生搜索條件的文檔時提供快速搜索。倒排索引是一種像數據結構一樣的散列圖,可將用戶從單詞導向文檔或網頁。它是搜索引擎的核心。其主要目標是快速搜索從數百萬文件中查找數據
71. Elasticsearch是如何實現Master選舉的
Elasticsearch的選主是ZenDiscovery模塊負責的,主要包含Ping(節點之間通過這個RPC來發現彼此)和Unicast(單播模塊包含一個主機列表以控制哪些節點需要ping通)這兩部分 ; 對所有可以成為master的節點(node.master: true)根據nodeId字典排序,每次選舉每個節點都把自己所知道節點排一次序,然后選出第一個(第0位)節點,暫且認為它是master節點 ; 如果對某個節點的投票數達到一定的值(可以成為master節點數n/2+1)并且該節點自己也選舉自己,那這個節點就是master。否則重新選舉一直到滿足上述條件
72. Elasticsearch在部署時,對Linux的設置有哪些優化方法
關閉緩存 swap; 堆內存設置為:Min(節點內存/2, 32GB); 設置最大文件句柄數; 線程池+隊列大小根據業務需要做調整; 磁盤存儲 raid 方式——存儲有條件使用 RAID10,增加單節點性能以及避免單 節點存儲故障
SpringCloud面試題
1、什么是微服務架構
微服務架構就是將單體的應用程序分成多個應用程序,這多個應用程序就成為微服務,每個微服務運行在自己的進程中,并使用輕量級的機制通信。這些服務圍繞業務能力來劃分,并通過自動化部署機制來獨立部署。這些服務可以使用不同的編程語言,不同數據庫,以保證最低限度的集中式管理。
2、Spring Cloud 是什么
- SpringCloud是分布式微服務治理解決方案。提供了一系列框架技術的有序集合。
- 利用Spring Boot的開發便利性巧妙地簡化了分布式系統基礎設施的開發,如服務發現注冊、配置中心、智能路由、消息總線、負載均衡、斷路器、數據監控等,都可以用Spring Boot的開發風格做到一鍵啟動和部署。
- Spring Cloud是集大成者,它只是將各家公司開發的比較成熟、經得起實際考驗的服務框架組合起來,通過Spring Boot風格進行再封裝屏蔽掉了復雜的配置和實現原理,最終給開發者留出了一套簡單易懂、易部署和易維護的分布式系統開發工具包。
3、SpringCloud的優缺點
優點:
1.耦合度比較低。不會影響其他模塊的開發。
2.配置比較簡單,基本用注解就能實現,不用使用過多的配置文件。
3.微服務跨平臺的,可以用任何一種語言開發。
4.每個微服務可以有自己的獨立的數據庫也有用公共的數據庫。
5.直接寫后端的代碼,不用關注前端怎么開發,直接寫自己的后端代碼即可,然后暴露接口,通過組件進行服務通信。6.組件化(模塊化)、隨時開箱拆箱。
缺點:
1.微服務落地部署
2.微服務監控
3.微服務日志管理
4.微服務集成測試與質量管理
4、SpringBoot和SpringCloud的區別?
- SpringBoot專注于快速方便的開發單個個體微服務。
- SpringCloud是全局的微服務協調整理治理框架,它將SpringBoot開發的一個個單體微服務整合并管理起來,為各個微服務之間提供,配置管理、服務發現、斷路器、路由、微代理、事件總線、全局鎖、決策競選、分布式會話等等集成服務
- SpringBoot可以離開SpringCloud獨立使用開發項目, SpringCloud基于SpringBoot ,不能脫離SpringBoot。
- SpringBoot專注于快速、方便的開發單個微服務個體,SpringCloud關注全局的服務治理框架。
5、SpringCloud有哪些組件?
- Spring Cloud Eureka:服務注冊與發現
- Spring Cloud Ribbon:客戶端負載均衡
- Spring Cloud Feign:聲明性的Web服務客戶端
- Spring Cloud Hystrix:斷路器
- Spring Cloud Zuul:服務網關
- Spring Cloud Config:分布式統一配置管理
其它組件詳細地址:
- https://www.processon.com/view/link/5ef6e8a96376891e81e8009d
6、為什么使用服務發現?
如果你在寫代碼調用一個有REST API或Thrift API的服務,你的代碼需要知道一個服務實例的網絡地址(IP地址和端口)。運行在物理硬件上的傳統應用中,服務實例的網絡地址是相對靜態的,你的代碼可以從一個很少更新的配置文件中讀取網絡地址。
在一個現代的,基于云的微服務應用中,
服務實例的網絡地址是動態分配的。而且,由于自動擴展,失敗和更新,服務實例的配置也經常變化。此時,需要服務注冊與發現的組件來解決動態IP的變化等問題。
7、服務注冊與發現需要具備哪些功能?
注冊中心應具備以下功能:
- 服務注冊表
服務注冊表是注冊中心的核心,它用來記錄各個微服務的信息,例如微服務的名稱、IP、端口等。服務注冊表提供查詢API和管理API,查詢API用于查詢可用的微服務實例,管理API用于服務的注冊與注銷。- 服務注冊與發現
服務注冊是指微服務在啟動時,將自己的信息注冊到注冊中心的過程。服務發現是指查詢可用的微服務列表及網絡地址的機制。- 服務檢查
注冊中心使用一定的機制定時檢測已注冊的服務,如發現某實例長時間無法訪問,就會從服務注冊表移除該實例。
8、Eureka組件
什么是Eureka
? Eureka是作為SpringCloud的服務注冊與發現組件功能服務器,是服務注冊中心,系統中的其他服務使用Eureka的客戶端將其連接到Eureka Service中,并且保持心跳,這樣開發人員可以通過Eureka Service來監控各個微服務是否運行正常。
什么是Eureka的自我保護模式,
默認情況下,如果Eureka Service在一定時間內沒有接收到某個微服務的心跳,Eureka Service會進入自我保護模式,在該模式下Eureka Service會保護服務注冊表中的信息,不在刪除注冊表中的數據,當網絡故障恢復后,Eureka Servic 節點會自動退出自我保護模式
Renew服務租約
Eureka Client 在默認的情況下會每隔 30 秒發送一次心跳來進行服務續約。通過服務續約 來告知 Eureka Server 該 Eureka Client 仍然可用,沒有出現故障。正常情況下,如果 Eureka Server 在 90 秒內沒有收到 Eureka Client 的心跳, Eureka Server 會將 Eureka Client 實例從注冊列表中 刪除。
Eviction什么時候會對服務進行剔除
在默認情況下,當 Eureka Client 連續 90 秒沒有向 Eureka Server 發送服務續約(即心跳〉 時, Eureka Server 會將該服務實例從服務注冊列表刪除,即服務剔除。
Eureka怎么實現高可用
注冊多臺Eureka,SpringCloud服務間互相注冊,客戶端從Eureka獲取信息時,按照Eureka的順序依次來訪問。
9、Ribbon組件
Ribbon是什么?
- Ribbon是Netflix發布的開源項目,主要功能是提供客戶端的服務間調用和服務的負載均衡。
- Ribbon客戶端組件提供一系列完善的配置項,如連接超時,重試等。簡單的說,就是在配置文件中列出后面所有的機器,Ribbon會自動的幫助你基于某種規則(如簡單輪詢,隨即連接等)去連接這些機器。我們也很容易使用Ribbon實現自定義的負載均衡算法。(有點類似Nginx)
Nginx與Ribbon的區別
Nginx是反向代理同時可以實現負載均衡,nginx攔截客戶端請求采用負載均衡策略根據upstream配置進行轉發,相當于請求通過nginx服務器進行轉發。
Ribbon是客戶端負載均衡,從注冊中心讀取目標服務器信息,然后客戶端采用輪詢策略對服務直接訪問,全程在客戶端操作。(默認是輪循操作)
Ribbon底層實現原理
- Ribbon使用discoveryClient從注冊中心讀取目標服務信息,對同一接口請求進行計數,使用%取余算法獲取目標服務集群索引,返回獲取到的目標服務信息。
10、Feign組件
什么是Feign?
- Feign 是一個聲明web服務客戶端,這使得編寫web服務客戶端更容易
- Feign可幫助我們更加便捷,優雅的調用HTTP API。
在SpringCloud中,使用Feign非常簡單——創建一個接口,并在接口上添加一些注解,代碼就完 成了。 Feign支持多種注解,例如Feign自帶的注解或者JAX-RS注解等。
SpringCloud對Feign進行了增強,使Feign支持了SpringMVC注解,并整合了Ribbon和Eureka, 從而讓Feign的使用更加方便。
- Spring Cloud 集成 Ribbon 和 Eureka 提供的負載均衡的HTTP客戶端Feign。(Feign默認集成了Ribbon,并和Eureka結合)。默認實現了負載均衡的效果。
Ribbon和Feign調用服務的區別
- 調用方式同:Ribbon需要我們自己構建Http請求,模擬Http請求然后通過RestTemplate發給其他服務,步驟相當繁瑣
- 而Feign則是在Ribbon的基礎上進行了一次改進,采用接口的形式,將我們需要調用的服務方法定義成抽象方法保存在本地就可以了,不需要自己構建Http請求了,直接調用接口就行了,不過要注意,調用方法要和本地抽象方法的簽名完全一致。
11、Hystrix組件
什么是 Hystrix?
- 在分布式系統,我們一定會依賴各種服務,那么這些個服務一定會出現失敗的情況,就會導致雪崩,
- Hystrix是由Netflix開源的一個針對分布式系統容錯處理的開源組件。能夠提供斷路,降級,監控等多種服務。它具有服務降級,服務熔斷,服務隔離,監控等一些防止雪崩的技術。
Hystrix提供了哪些功能?
Hystrix有四種防雪崩方式:
- 服務降級:接口調用失敗就調用本地的方法返回一個空
- 服務熔斷:接口調用失敗就會進入調用接口提前定義好的一個熔斷的方法,返回錯誤信息
- 服務隔離:隔離服務之間相互影響
- 服務監控:在服務發生調用時,會將每秒請求數、成功請求數等運行指標記錄下來。
服務雪崩效應產生的原因
因為Tomcat默認情況下只有一個線程池來維護客戶端發送的所有的請求,這時候某一接口在某一時刻被大量訪問就會占據tomcat線程池中的所有線程,其他請求處于等待狀態,無法連接到服務接口。
在微服務中,如何保護服務?
一般使用使用Hystrix框架,實現服務隔離來避免出現服務的雪崩效應,從而達到保護服務的效果。當微服務中,高并發的數據庫訪問量導致服務線程阻塞,使單個服務宕機,服務的不可用會蔓延到其他服務,引起整體服務災難性后果,使用服務降級能有效為不同的服務分配資源,一旦服務不可用則返回友好提示,不占用其他服務資源,從而避免單個服務崩潰引發整體服務的不可用.
服務容錯的相關知識概念
服務容錯的核心知識
雪崩效應
? 在微服務架構中,一個請求需要調用多個服務是非常常見的。如客戶端訪問A服務,而A服務需要調用B 服務,B服務需要調用C服務,由于網絡原因或者自身的原因,如果B服務或者C服務不能及時響應,A服 務將處于阻塞狀態,直到B服務C服務響應。此時若有大量的請求涌入,容器的線程資源會被消耗完畢, 導致服務癱瘓。服務與服務之間的依賴性,故障會傳播,造成連鎖反應,會對整個微服務系統造成災難 性的嚴重后果,這就是服務故障的“雪崩”效應。(服務熔斷和服務降級就可以視為解決服務雪崩的手段之一)
服務隔離
? 顧名思義,它是指將系統按照一定的原則劃分為若干個服務模塊,各個模塊之間相對獨立,無強依賴。 當有故障發生時,能將問題和影響隔離在某個模塊內部,而不擴散風險,不波及其它模塊,不影響整體 的系統服務。
服務熔斷
? 熔斷這一概念來源于電子工程中的斷路器(Circuit Breaker)。
在互聯網系統中,當下游服務因訪問壓力過大而響應變慢或失敗,上游服務為了保護系統整體的可用性,可以暫時切斷對下游服務的調用。這種犧牲局部,保全整體的措施就叫做熔斷。
服務降級
? 降級,就是當某個服務熔斷之后,服務器將不再被調用,此時客戶端可以自己準備一個本地的 fallback回調,返回一個缺省值。
Hystrix實現兩個核心注解
@HystrixCommand 注解
@FeignClient注解:fallback 如果接口服務不能訪問或延遲,則運行對應其接口實現類。
12、網關組件
什么是網關?
網關相當于一個網絡服務架構的入口,所有網絡請求必須通過網關轉發到具體的服務。
? API網關,顧名思義,是統一管理API的一個網絡關口、通道,是整個微服務平臺所有請求的唯一入口,所有的客戶端和消費端都通過統一的網關接入微服務,在網關層處理所有的非業務功能。
網關的作用是什么
統一管理微服務請求,權限控制、負載均衡、路由轉發、監控、安全控制黑名單和白名單等
什么是Spring Cloud Zuul網關)
Zuul是對SpringCloud提供的成熟對的路由方案,他會根據請求的路徑不同,網關會定位到指定的微服務,并代理請求到不同的微服務接口,他對外隱蔽了微服務的真正接口地址。
三個重要概念:動態路由表,路由定位,反向代理:
- 動態路由表:Zuul支持Eureka路由,手動配置路由,這倆種都支持自動更新
- 路由定位:根據請求路徑,Zuul有自己的一套定位服務規則以及路由表達式匹配
- 反向代理:客戶端請求到路由網關,網關受理之后,在對目標發送請求,拿到響應之后在 給客戶端
它可以和Eureka,Ribbon,Hystrix等組件配合使用,
Zuul的應用場景:
- 對外暴露,權限校驗,服務聚合,日志審計等
網關與過濾器有什么區別
網關是對所有服務的請求進行分析過濾,過濾器是對單個服務而言。
ZuulFilter常用有那些方法
- Run():過濾器的具體業務邏輯
- shouldFilter():判斷過濾器是否有效
- filterOrder():過濾器執行順序
- filterType():過濾器攔截位置
Zuul與Nginx有什么區別?
Zuul是java語言實現的,主要為java服務提供網關服務,尤其在微服務架構中可以更加靈活的對網關進行操作。Nginx是使用C語言實現,性能高于Zuul,但是實現自定義操作需要熟悉lua語言,對程序員要求較高,可以使用Nginx做Zuul集群
Nginx可以實現網關?為什么還需要使用Zuul框架
Zuul是SpringCloud集成的網關,使用Java語言編寫,可以對SpringCloud架構提供更靈活的服務。
如何實現動態Zuul網關路由轉發
通過path配置攔截請求,通過ServiceId到配置中心獲取轉發的服務列表,Zuul內部使用Ribbon實現本地負載均衡和轉發。
Zuul網關如何搭建集群
使用Nginx的upstream設置Zuul服務集群,通過location攔截請求并轉發到upstream,默認使用輪詢機制對Zuul集群發送請求。
13、配置中心組件
分布式配置中心有那些框架?
SpringCloud Config、Apollo、Zookeeper。
什么是Spring Cloud Config?
Spring Cloud Config為分布式系統中的外部配置提供服務器和客戶端支持,可以方便的對微服務各個環境下的配置進行集中式管理。Spring Cloud Config分為Config Server和Config Client兩部分。Config Server負責讀取配置文件,并且暴露Http API接口,Config Client通過調用Config Server的接口來讀取配置文件。
Spring Cloud Config
- Config能夠管理所有微服務的配置文件
- 集中配置管理工具,分布式系統中統一的外部配置管理,默認使用Git來存儲配置,可以支持客戶端配置的刷新及加密、解密操作。
分布式配置中心的作用?
動態變更項目配置信息而不必重新部署項目。
SpringCloud Config 可以實現實時刷新嗎?
SpringCloud Config實時刷新采用SpringCloud Bus消息總線。
http://assets.processon.com/chart_image/5e46afc1e4b00aefb7e063aa.png
14、Spring Cloud Bus
- 用于傳播集群狀態變化的消息總線,使用輕量級消息代理鏈接分布式系統中的節點,可以用來動態刷新集群中的服務配置信息。
- 簡單來說就是修改了配置文件,發送一次請求,所有客戶端便會重新讀取配置文件。需要利用中間插件MQ
15、Spring Cloud Sleuth組件
在微服務中,通常根據業務模塊分服務,項目中前端發起一個請求,后端可能跨幾個服務調用才能完成這個請求。如果系統越來越龐大,服務之間的調用與被調用關系就會變得很復雜,假如一個請求中需要跨幾個服務調用,其中一個服務由于網絡延遲等原因掛掉了,那么這時候我們需要分析具體哪一個服務出問題了就會顯得很困難。
Spring Cloud Sleuth服務鏈路跟蹤功能就可以幫助我們快速的發現錯誤根源以及監控分析每條請求鏈路上的性能等等。
16、Spring Cloud Stream組件
- 輕量級事件驅動微服務框架,可以使用簡單的聲明式模型來發送及接收消息,主要實現為Apache Kafka及RabbitMQ。
17、Spring Cloud Task組件
Spring Cloud Task的目標是為Spring Boot應用程序提供創建短運行期微服務的功能。在Spring Cloud Task中,我們可以靈活地動態運行任何任務,按需分配資源并在任務完成后檢索結果。Tasks是Spring Cloud Data Flow中的一個基礎項目,允許用戶將幾乎任何Spring Boot應用程序作為一個短期任務執行。
18、Spring Cloud Sidecar組件
在我們的分布式項目中,經常會出現一部分服務是 Java 語言寫的,一部分服務是非 Java 語言寫的,Java 語言寫的服務可以通過我們的 SpringCloud 組件來進行服務發現,網關路由等操作,但是非 Java語言的程序 則無法實現這個功能,為了解決這個問題,Netfilix 提供了 Sidecar 來解決,其基本思想就是 sidecar 是一個 Java 語言的程序,然后內容通過配置訪問非 Java語言的程序,然后注冊到我們的 SpringCloud組件中,實現我們的功能,本質上其就是一個代理
人力面試題
一、人力面
-
你為什么離開之前的公司
-
你為什么要進我們公司
-
說說職業規劃
-
你如何看待加班問題
-
談一談你的一次失敗經歷
-
你覺得你最大的優點是什么
-
你覺得你最大的缺點是什么
-
你在工作之余做什么事情
-
你為什么認為你適合這個 職位
-
你覺得自己那方面能力最急需提高
-
你來我們公司最希望得到什么
-
你希望從這份工作中獲得什么
-
你對現在應聘的職位有什么了解
-
您還有什么想問的
-
你怎么看待自己的職業生涯
-
談談你的家庭情況
-
你有什么業余愛好
-
你計劃在公司工作多久
-
你如何看待目前程序員頻繁跳槽這件事?
二、軟實力
-
說說你的亮點
-
說說你最近在看什么書
-
說說你覺得最有意義的技術書籍
-
說說個人發展方向方面的思考
-
說說你認為的服務端開發工程師應該具備哪些能力
-
說說你認為的架構師是什么樣的,架構師主要做什么
-
說說你所理解的技術專家