設計模式常見面試題彙總

面試整理

一、數據結構

常見的數據結構:棧(又稱爲堆棧)、隊列、數組、鏈表和紅黑樹html

  • 棧:先進後出,是一種運算受限的線性表限制是僅容許在標的一端進行插入和刪除操做前端

  • 隊列:簡稱隊,先進先出,隊列的入口、出口各佔一側。也是一種運算受限的線性表,僅容許在表的一端進行插入,而在表的另外一端進行刪除java

  • 數組:Array,是有序的元素序列,數組是在內存中開闢一段連續的空間,並在此空間存放元素。查找元素快、增刪元素慢 經過for循環遍歷 arr[i]mysql

  • 鏈表:linked list,由一系列結點(鏈表中每個元素稱爲結點)組成,結點能夠在運行時i動態生成。每一個結點包括兩個部分:一個是存儲數據元素的數據域,另外一個是存儲下一個結點地址的指針域。查找元素慢、增刪元素快git

  • 紅黑樹:自己就是二叉樹binary tree ,每一個節點最多有兩個子樹的樹結構

Java數據結構面試題及答案

  1、線性表(重點)程序員

  線性表是由N個元素組成的有序序列,也是最多見的一種數據結構。重點有兩個數組和鏈表。web

  一、數組面試

  數組是一種存儲單元連續,用來存儲固定大小元素的線性表。java中對應的集合實現,好比ArrayList。ajax

  二、鏈表算法

  鏈表又分單鏈表和雙鏈表,是在物理存儲單元上非連續、非順序的存儲結構,數據元素的邏輯順序是經過鏈表中指針的連接次序實現的。java中對應的集合實現,好比LinkedList。

  2、棧與隊列

  一、棧

  

Java數據結構面試題及答案

  棧,是一種運算受限的線性表,重點掌握其後進先出的特色。表的末端叫棧頂,基本操做有push(進棧)和pop(出棧)。java中stack就是簡單的棧實現。

  二、隊列

  

Java數據結構面試題及答案

  隊列也是一種操做受限制的線性表,重點掌握其先進先出的特色。表的前端只容許進行刪除操做,表的後端進行插入操做。進行插入操做的端稱爲隊尾,進行刪除操做的端稱爲隊頭。java中不少Queue的實現,消息中間件的隊列本質也是基於此的。

  3、樹(重點)

  在非線性結構裏面,樹是很是很是重要的一種數據結構。基於其自己的結構優點,尤爲在查找領域,應用普遍,其中又以二叉樹最爲重要。樹的話咱們這裏只重點說一下二叉樹。

  一、二叉搜索樹

  二叉搜索樹又叫二叉查找樹,又叫二叉排序樹。性質以下:(1) 若左子樹不空,則左子樹上全部結點的值均小於它的根結點的值;(2) 若右子樹不空,則右子樹上全部結點的值均大於它的根結點的值;(3) 左、右子樹也分別爲二叉排序樹;(4) 沒有鍵值相等的結點。

  二、平衡二叉樹

  

Java數據結構面試題及答案

  平衡二叉樹又叫AVL樹。性質以下:它的左子樹和右子樹都是平衡二叉樹,且左子樹和右子樹的深度之差的絕對值不超過1。

  三、紅黑樹

  

Java數據結構面試題及答案

  紅黑樹是一種特殊的平衡二叉樹,它保證在最壞狀況下基本動態集合操做的事件複雜度爲O(log n)。

  紅黑樹放棄了追求徹底平衡,追求大體平衡,在與平衡二叉樹的時間複雜度相差不大的狀況下,保證每次插入最多隻須要三次旋轉就能達到平衡,實現起來也更爲簡單。平衡二叉樹追求絕對平衡,條件比較苛刻,實現起來比較麻煩,每次插入新節點以後須要旋轉的次數不能預知。

二、Java中有幾種類型的流?

1.字符流和字節流。

在這裏插入圖片描述

  1. 字節流繼承inputStream和OutputStream
  2. 字符流繼承自InputSteamReader和OutputStreamWriter
  3. 整體結構圖
    在這裏插入圖片描述

2.字節流和字符流哪一個好?怎麼選擇?

  1. 大多數狀況下使用字節流會更好,由於大多數時候 IO 操做都是直接操做磁盤文件,因此這些流在傳輸時都是以字節的方式進行的(圖片等都是按字節存儲的)
  2. 若是對於操做須要經過 IO 在內存中頻繁處理字符串的狀況使用字符流會好些,由於字符流具有緩衝區,提升了性能

3. 什麼是緩衝區?有什麼做用?

  1. 緩衝區就是一段特殊的內存區域,不少狀況下當程序須要頻繁地操做一個資源(如文件或數據庫)則性能會很低,因此爲了提高性能就能夠將一部分數據暫時讀寫到緩存區,之後直接今後區域中讀寫數據便可,這樣就顯著提高了性。
  2. 對於 Java 字符流的操做都是在緩衝區操做的,因此若是咱們想在字符流操做中主動將緩衝區刷新到文件則能夠使用 flush() 方法操做。

4. 字符流和字節流有什麼區別?

字符流和字節流的使用很是類似,可是實際上字節流的操做不會通過緩衝區(內存)而是直接操做文本自己的,而字符流的操做會先通過緩衝區(內存)而後經過緩衝區再操做文件

5. 什麼是Java序列化,如何實現Java序列化?

  1. 序列化就是一種用來處理對象流的機制,將對象的內容進行流化。能夠對流化後的對象進行讀寫操做,能夠將流化後的對象傳輸於網絡之間。序列化是爲了解決在對象流讀寫操做時所引起的問題
  2. 序列化的實現:將須要被序列化的類實現Serialize接口,沒有須要實現的方法,此接口只是爲了標註對象可被序列化的,而後使用一個輸出流(如:FileOutputStream)來構造一個ObjectOutputStream(對象流)對象,再使用ObjectOutputStream對象的write(Object obj)方法就能夠將參數obj的對象寫出

6. PrintStream、BufferedWriter、PrintWriter的比較?

  1. PrintStream類的輸出功能很是強大,一般若是須要輸出文本內容,都應該將輸出流包裝成PrintStream後進行輸出。它還提供其餘兩項功能。與其餘輸出流不一樣,PrintStream 永遠不會拋出 IOException;而是,異常狀況僅設置可經過 checkError 方法測試的內部標誌。另外,爲了自動刷新,能夠建立一個 PrintStream
  2. BufferedWriter:將文本寫入字符輸出流,緩衝各個字符從而提供單個字符,數組和字符串的高效寫入。經過write()方法能夠將獲取到的字符輸出,而後經過newLine()進行換行操做。BufferedWriter中的字符流必須經過調用flush方法才能將其刷出去。而且BufferedWriter只能對字符流進行操做。若是要對字節流操做,則使用BufferedInputStream
  3. PrintWriter的println方法自動添加換行,不會拋異常,若關心異常,須要調用checkError方法看是否有異常發生,PrintWriter構造方法可指定參數,實現自動刷新緩存(autoflush)

7. BufferedReader屬於哪一種流,它主要是用來作什麼的,它裏面有那些經典的方法?

屬於處理流中的緩衝流,能夠將讀取的內容存在內存裏面,有readLine()方法,它,用來讀取一行

8. 什麼是節點流,什麼是處理流,它們各有什麼用處,處理流的建立有什麼特徵?

  1. 節點流 直接與數據源相連,用於輸入或者輸出
  2. 處理流:在節點流的基礎上對之進行加工,進行一些功能的擴展
  3. 處理流的構造器必需要 傳入節點流的子類

9.流通常須要不須要關閉,若是關閉的話在用什麼方法,通常要在那個代碼塊裏面關閉比較好,處理流是怎麼關閉的,若是有多個流互相調用傳入是怎麼關閉的?

  1. 流一旦打開就必須關閉,使用close方法
  2. 放入finally語句塊中(finally 語句必定會執行)
  3. 調用的處理流就關閉處理流
  4. 多個流互相調用只關閉最外層的流

10. InputStream裏的read()返回的是什麼,read(byte[] data)是什麼意思,返回的是什麼值?

  1. 返回的是所讀取的字節的int型(範圍0-255)
  2. read(byte [ ] data)將讀取的字節儲存在這個數組。返回的就是傳入數組參數個數

11. OutputStream裏面的write()是什麼意思,write(byte b[], int off, int len)這個方法裏面的三個參數分別是什麼意思?

  1. write將指定字節傳入數據源
  2. Byte b[ ]是byte數組
  3. b[off]是傳入的第一個字符、b[off+len-1]是傳入的最後的一個字符 、len是實際長度

三、多線程 線程與進程 併發 並行

  • 併發:指兩個或多個事件在同一個時間段內發生。

    例子:一我的吃兩個饅頭

  • 並行:指兩個或多個事件在同一時刻發生(同時發生)。

    例子:兩我的吃兩個饅頭

  • 進程:是指一個內存中運行的應用程序,每一個進程都有一個獨立的內存空間,一個應用程序能夠同時運行多個進程;進程也是程序的一次執行過程,是系統運行程序的基本單位;系統運行一個程序便是一個進程從建立、運行到消亡的過程。

    就是進入到內存中的程序

  • 線程:線程是進程中的一個執行單元,負責當前進程中程序的執行,一個進程中至少有一個線程。一個進程中是能夠有多個線程的,這個應用程序也能夠稱之爲多線程程序。

    簡而言之:一個程序運行後至少有一個進程,一個進程中能夠包含多個線程

    主線程

    舉例

    1. JVM執行main方法,main方法就會進入到棧內存
    2. JVM棧會操做系統開闢出一條main方法通向CPU的執行路徑
    3. cup就能夠經過這個路徑來執行main方法
    4. 這個路徑就叫作main(主)線程
  1. 多線程使用的優缺點?

優勢:

(1)多線程技術使程序的響應速度更快

(2)當前沒有進行處理的任務能夠將處理器時間讓給其它任務

(3)佔用大量處理時間的任務能夠按期將處理器時間讓給其它任務

(4)能夠隨時中止任務

(5)能夠分別設置各個任務的優先級以及優化性能

缺點:

(1)等候使用共享資源時形成程序的運行速度變慢

(2)對線程進行管理要求額外的cpu開銷

(3)可能出現線程死鎖狀況。即較長時間的等待或資源競爭以及死鎖等症狀。

  1. start()方法和run()方法簡介和區別?

start()方法:

1)用start方法來啓動線程,真正實現了多線程運行,這時無需等待run方法體代碼執行完畢而直接繼續執行下面的代碼。

2)經過調用Thread類的start()方法來啓動一個線程,這時此線程處於就緒(可運行)狀態,並無運行,一旦獲得CPU時間片,就開始執行run()方法。

run()方法:

1)run()方法只是類的一個普通方法而已,若是直接調用Run方法,程序中依然只有主線程這一個線程,其程序執行路徑仍是隻有一條。

總結:

1)調用start方法方可啓動線程,

2)而run方法只是thread的一個普通方法調用,仍是在主線程裏執行。

3)把須要並行處理的代碼放在run()方法中,start()方法啓動線程將自動調用run()方法,這是由jvm的內存機制規定的。

4)而且run()方法必須是public訪問權限,返回值類型爲void.。

  1. Runnable接口和Callable接口的相同點和不一樣點?

    img

  2. volatile關鍵字的做用是什麼?

(1)多線程使用volatile關鍵字修飾的變量,保證了其在多線程之間的可見性,即每次讀取到volatile變量,必定是最新的數據

(2)Java代碼執行中,爲了獲取更好的性能JVM可能會對指令進行重排序,多線程下可能會出現一些意想不到的問題。使用volatile則會對禁止語義重排序,固然這也必定程度上下降了代碼執行效率

  1. CyclicBarrier和CountDownLatch的區別是什麼?

    img

  2. volatile和synchronized對比?

1)volatile本質是在告訴jvm當前變量在寄存器中的值是不肯定的,須要從主存中讀取,synchronized則是鎖定當前變量,只有當前線程能夠訪問該變量,其餘線程被阻塞住.

2)volatile僅能使用在變量級別,synchronized則能夠使用在變量,方法.

3)volatile僅能實現變量的修改可見性,而synchronized則能夠保證變量的修改可見性和原子性.  

4)volatile不會形成線程的阻塞,而synchronized可能會形成線程的阻塞.

  1. 怎麼喚醒一個阻塞的線程?

若是線程是由於調用了wait()、sleep()或者join()方法而致使的阻塞,能夠中斷線程,而且經過拋出InterruptedException來喚醒它;若是線程遇到了IO阻塞,無能爲力,由於IO是操做系統實現的,Java代碼並無辦法直接接觸到操做系統。

  1. Java中如何獲取到線程dump文件?

dump文件的做用:

死循環、死鎖、阻塞、頁面打開慢等問題,打線程dump是最好的解決問題的途徑。所以,線程dump也就是線程堆棧。

獲取到線程堆棧dump文件內容分兩步:

(1)第一步:獲取到線程的pid,Linux環境下能夠使用ps -ef | grep java

(2)第二步:打印線程堆棧,能夠經過使用jstack pid命令

  1. sleep方法和wait方法的相同點和不一樣點?

相同點:

兩者均可以讓線程處於凍結狀態。

不一樣點:

1)首先應該明確sleep方法是Thread類中定義的方法,而wait方法是Object類中定義的方法。

2)sleep方法必須人爲地爲其指定時間。

wait方法既能夠指定時間,也能夠不指定時間。

3)sleep方法時間到,線程處於臨時阻塞狀態或者運行狀態。

wait方法若是沒有被設置時間,就必需要經過notify或者notifyAll來喚醒。

4)sleep方法不必定非要定義在同步中。

wait方法必須定義在同步中。

5)當兩者都定義在同步中時,

線程執行到sleep,不會釋放鎖。

線程執行到wait,會釋放鎖。

  1. 生產者和消費者模型的做用是什麼?

1)經過平衡生產者的生產能力和消費者的消費能力來提高整個系統的運行效率,這是生產者消費者模型最重要的做用

2)解耦,這是生產者消費者模型附帶的做用,解耦意味着生產者和消費者之間的聯繫少,聯繫越少越能夠獨自發展而不須要收到相互的制約

  1. ThreadLocal的做用是什麼?

1)ThreadLocal用來解決多線程程序的併發問題

2)ThreadLocal並非一個Thread,而是Thread的局部變量,當使用ThreadLocal維護變量時,ThreadLocal爲每一個使用該變量的線程提供獨立的變量副本,因此每一個線程均可以獨立地改變本身的副本,而不會影響其它線程所對應的副本.

3)從線程的角度看,目標變量就象是線程的本地變量,這也是類名中「Local」所要表達的意思。

4)線程局部變量並非Java的新發明,Java沒有提供在語言級支持(語法上),而是變相地經過ThreadLocal的類提供支持.

  1. wait方法和notify/notifyAll方法在放棄對象監視器時有什麼區別?

wait()方法當即釋放對象監視器;

notify()/notifyAll()方法則會等待線程剩餘代碼執行完畢纔會放棄對象監視器。

  1. Lock和synchronized對比?

1)Lock是一個接口,而synchronized是Java中的關鍵字,synchronized是內置的語言實現;

2)synchronized在發生異常時,會自動釋放線程佔有的鎖,所以不會致使死鎖現象發生;而Lock在發生異常時,若是沒有主動經過unLock()去釋放鎖,則極可能形成死鎖現象,所以使用Lock時須要在finally塊中釋放鎖;

3)Lock可讓等待鎖的線程響應中斷,而synchronized卻不行,使用synchronized時,等待的線程會一直等待下去,不可以響應中斷;

4)經過Lock能夠知道有沒有成功獲取鎖,而synchronized卻沒法辦到。

5)Lock能夠提升多個線程進行讀操做的效率。

6)在JDK1.5中,synchronized是性能低效的。由於這是一個重量級操做,它對性能最大的影響是阻塞式的實現,掛起線程和恢復線程的操做都須要轉入內核態中完成,這些操做給系統的併發性帶來了很大的壓力。相比之下使用Java提供的Lock對象,性能更高一些。

可是,JDK1.6,發生了變化,對synchronize加入了不少優化措施,有自適應自旋,鎖消除,鎖粗化,輕量級鎖,偏向鎖等等。致使在JDK1.6上synchronize的性能並不比Lock差。所以。提倡優先考慮使用synchronized來進行同步。

一、具備1-5工做經驗的,面對目前流行的技術不知從何下手,

須要突破技術瓶頸的。二、在公司待久了,過得很安逸,

但跳槽時面試碰壁。須要在短期內進修、跳槽拿高薪的。

三、若是沒有工做經驗,但基礎很是紮實,對java工做機制,

經常使用設計思想,經常使用java開發框架掌握熟練的。

四、以爲本身很牛B,通常需求都能搞定。

可是所學的知識點沒有系統化,很難在技術領域繼續突破的。

\5. 羣號:高級架構羣 468897908備註好信息!

6.阿里Java高級大牛直播講解知識點,分享知識,

多年工做經驗的梳理和總結,帶着你們全面、

科學地創建本身的技術體系和技術認知!

1四、ConcurrentHashMap的併發度是什麼?

ConcurrentHashMap的併發度就是segment的大小,默認爲16,這意味着最多同時能夠有16條線程操做ConcurrentHashMap,這也是ConcurrentHashMap對Hashtable的最大優點,任何狀況下,Hashtable能同時有兩條線程獲取Hashtable中的數據∂

1五、ReadWriteLock是什麼?

ReadWriteLock是一個讀寫鎖接口,ReentrantReadWriteLock是ReadWriteLock接口的一個具體實現,實現了讀寫的分離,讀鎖是共享的,寫鎖是獨佔的,讀和讀之間不會互斥,讀和寫、寫和讀、寫和寫之間纔會互斥,提高了讀寫的性能。

1六、FutureTask是什麼?

FutureTask表示一個異步運算的任務。FutureTask裏面能夠傳入一個Callable的具體實現類,能夠對這個異步運算的任務的結果進行等待獲取、判斷是否已經完成、取消任務等操做。因爲FutureTask也是Runnable接口的實現類,因此FutureTask也能夠放入線程池中。

1七、Java中用到的線程調度算法是什麼?

搶佔式。一個線程用完CPU以後,操做系統會根據線程優先級、線程飢餓狀況等數據算出一個總的優先級並分配下一個時間片給某個線程執行。

1八、單例模式的線程安全性?

單例模式的線程安全意味着:某個類的實例在多線程環境下只會被建立一次出來。單例模式有不少種的寫法,具體分析以下:

(1)餓漢式單例模式的寫法:線程安全

(2)懶漢式單例模式的寫法:非線程安全

(3)雙檢鎖單例模式的寫法:線程安全

1九、什麼是樂觀鎖和悲觀鎖?

(1)樂觀鎖:對於併發間操做產生的線程安全問題持樂觀狀態,樂觀鎖認爲競爭不老是會發生,所以它不須要持有鎖,將比較-設置這兩個動做做爲一個原子操做嘗試去修改內存中的變量,若是失敗則表示發生衝突,那麼就應該有相應的重試邏輯。

(2)悲觀鎖:對於併發間操做產生的線程安全問題持悲觀狀態,悲觀鎖認爲競爭老是會發生,所以每次對某資源進行操做時,都會持有一個獨佔的鎖,就像synchronized,直接對操做資源上了鎖。

  1. Java編寫一個會致使死鎖的程序?

死鎖現象描述:

線程A和線程B相互等待對方持有的鎖致使程序無限死循環下去。

死鎖的實現步驟:

(1)兩個線程裏面分別持有兩個Object對象:lock1和lock2。這兩個lock做爲同步代碼塊的鎖;

(2)線程1的run()方法中同步代碼塊先獲取lock1的對象鎖,Thread.sleep(xxx),時間不須要太多,100毫秒差很少了,而後接着獲取lock2的對象鎖。這麼作主要是爲了防止線程1啓動一會兒就連續得到了lock1和lock2兩個對象的對象鎖

(3)線程2的run)(方法中同步代碼塊先獲取lock2的對象鎖,接着獲取lock1的對象鎖,固然這時lock1的對象鎖已經被線程1鎖持有,線程2確定是要等待線程1釋放lock1的對象鎖的

這樣,線程1″睡覺」睡完,線程2已經獲取了lock2的對象鎖了,線程1此時嘗試獲取lock2的對象鎖,便被阻塞,此時一個死鎖就造成了。

死鎖的實現代碼:

img

輸出結果是:

線程A 鎖住資源O1,等待O2

線程B 鎖住資源O2,等待O1

四、序列化

把對象以流的方式,寫入到文件中保存,叫寫對象,也叫序列化

  • 序列化:java.io.ObjectOutputStream 類的 writeObject() 方法能夠實現序列化;
  • 反序列化:java.io.ObjectInputStream 類的 readObject() 方法用於實現反序列化。

把文件中保存的對象,以流的方式讀取出來,叫作讀取數據,也叫對象的反序列化

image-20200913202946493

五、jvm原理

摘要

GC 垃圾收集器

Java 堆內存被劃分爲新生代和年老代兩部分,新生代主要使用複製和標記-清除垃圾回收算法;年老代主要使用標記-整理垃圾回收算法

永久代

指內存的永久保存區域,主要存放 Class 和 Meta(元數據)的信息,Class 在被加載的時候被放入永久區域, 它和和存放實例的區域不一樣,GC不會在主程序運行期對永久區域進行清理。因此這也致使了永久代的區域會隨着加載的 Class 的增多而脹滿,最終拋出 OOM 異常。

老年代

主要存放應用程序中生命週期長的內存對象。

JVM 運行時內存

Java 堆從 GC 的角度還能夠細分爲: 新生代(Eden 區、 From Survivor 區和 To Survivor 區)和老年代。

![img](file://C:/Users/Jared/Desktop/img/%E9%9D%A2%E8%AF%95%E5%B0%8F%E7%BB%93.assets/172d0f3d61d605de?lastModify=1600007113)

2三、新生代

是用來存放新生的對象。通常佔據堆的 1/3 空間。因爲頻繁建立對象,因此新生代會頻繁觸發MinorGC 進行垃圾回收。新生代又分爲 Eden區、 ServivorFrom、 ServivorTo 三個區。

5七、JVM內存模型

線程獨佔:棧,本地方法棧,程序計數器

線程共享:堆,方法區

5八、棧

又稱方法棧,線程私有的,線程執行方法是都會建立一個棧陣,用來存儲局部變量表,操做棧,動態連接,方法出口等信息.調用方法時執行入棧,方法返回式執行出棧.

5九、本地方法棧

與棧相似,也是用來保存執行方法的信息.執行Java方法是使用棧,執行Native方法時使用本地方法棧.

60、程序計數器

保存着當前線程執行的字節碼位置,每一個線程工做時都有獨立的計數器,只爲執行Java方法服務,執行Native方法時,程序計數器爲空.

6一、堆

JVM內存管理最大的一塊,對被線程共享,目的是存放對象的實例,幾乎所欲的對象實例都會放在這裏,當堆沒有可用空間時,會拋出OOM異常.根據對象的存活週期不一樣,JVM把對象進行分代管理,由垃圾回收器進行垃圾的回收管理

6二、方法區

又稱非堆區,用於存儲已被虛擬機加載的類信息,常量,靜態變量,即時編譯器優化後的代碼等數據.1.7的永久代和1.8的元空間都是方法區的一種實現。

前言

關於JVM系列面試知識點總結了一個思惟導圖,分享給你們

img

一、java中會存在內存泄漏嗎,請簡單描述。

會。本身實現堆載的數據結構時有可能會出現內存泄露。

二、64 位 JVM 中,int 的長度是多數?

Java 中,int 類型變量的長度是一個固定值,與平臺無關,都是 32 位。意思就是說,在 32 位 和 64 位 的 Java 虛擬機中,int 類型的長度是相同的。

三、Serial 與 Parallel GC 之間的不一樣之處?

Serial 與 Parallel 在 GC 執行的時候都會引發 stop-the-world。它們之間主要不一樣 serial 收集器是默認的複製收集器,執行 GC 的時候只有一個線程,而parallel 收集器使用多個 GC 線程來執行。

四、32 位和 64 位的 JVM,int 類型變量的長度是多數?

32 位和 64 位的 JVM 中,int 類型變量的長度是相同的,都是 32 位或者 4個字節。

五、Java 中 WeakReference 與 SoftReference 的區別?

雖然 WeakReference 與 SoftReference 都有利於提升 GC 和 內存的效率,可是 WeakReference ,一旦失去最後一個強引用,就會被 GC回收,而軟引用雖然不能阻止被回收,可是能夠延遲到 JVM 內存不足的時候。

六、JVM 選項 -XX:+UseCompressedOops 有什麼做用?爲何要使用

當你將你的應用從 32 位的 JVM 遷移到 64 位的 JVM 時,因爲對象的指針從32 位增長到了 64 位,所以堆內存會忽然增長,差很少要翻倍。這也會對 CPU緩存(容量比內存小不少)的數據產生不利的影響。由於,遷移到 64 位的 JVM主要動機在於能夠指定最大堆大小,經過壓縮OOP 能夠節省必定的內存。經過-XX:+UseCompressedOops 選項,JVM 會使用 32 位的 OOP,而不是 64 位的 OOP。

七、怎樣經過 Java 程序來判斷 JVM 是 32 位 仍是 64位?

你能夠檢查某些系統屬性如 sun.arch.data.model 或 os.arch 來獲取該信息。

八、32 位 JVM 和 64 位 JVM 的最大堆內存分別是多數?

理論上說上 32 位的 JVM 堆內存能夠到達 2^32,即 4GB,但實際上會比這個小不少。不一樣操做系統之間不一樣,如 Windows 系統大約 1.5GB,Solaris 大約3GB。64 位 JVM 容許指定最大的堆內存,理論上能夠達到 2^64,這是一個很是大的數字,實際上你能夠指定堆內存大小到 100GB。甚至有的 JVM,如 Azul,堆內存到 1000G 都是可能的。

九、JRE、JDK、JVM 及 JIT 之間有什麼不一樣?

JRE 表明 Java 運行時(Java run-time),是運行 Java 引用所必須的。JDK 表明 Java 開發工具(Java development kit),是 Java 程序打開發工具,如 Java編譯器,它也包含 JRE。JVM 表明 Java 虛擬機(Java virtual machine),它的責任是運行 Java 應用。JIT 表明即時編譯(Just In Time compilation),當代碼執行的次數超過必定的閾值時,會將 Java 字節碼轉換爲本地代碼,如,主要的熱點代碼會被準換爲本地代碼,這樣有利大幅度提升 Java 應用的性能。

十、解釋 Java 堆空間及 GC?

當經過 Java 命令啓動 Java 進程的時候,會爲它分配內存。內存的一部分用於建立堆空間,當程序中建立對象的時候,就從對空間中分配內存。GC 是 JVM 內部的一個進程,回收無效對象的內存用於未來的分配

十一、JVM 內存區域

img

JVM 內存區域主要分爲線程私有區域【程序計數器、虛擬機棧、本地方法區】、線程共享區域【JAVA 堆、方法區】、直接內存。

線程私有數據區域生命週期與線程相同, 依賴用戶線程的啓動/結束 而 建立/銷燬(在 Hotspot VM 內, 每一個線程都與操做系統的本地線程直接映射, 所以這部份內存區域的存/否跟隨本地線程的生/死對應)。

線程共享區域隨虛擬機的啓動/關閉而建立/銷燬。

直接內存並非 JVM 運行時數據區的一部分, 但也會被頻繁的使用: 在 JDK 1.4 引入的 NIO 提供了基於Channel與 Buffer的IO方式, 它能夠使用Native函數庫直接分配堆外內存, 而後使用DirectByteBuffer 對象做爲這塊內存的引用進行操做(詳見: Java I/O 擴展), 這樣就避免了在 Java堆和 Native 堆中來回複製數據, 所以在一些場景中能夠顯著提升性能。

img

十二、程序計數器(線程私有)

一塊較小的內存空間, 是當前線程所執行的字節碼的行號指示器,每條線程都要有一個獨立的程序計數器,這類內存也稱爲「線程私有」 的內存。

正在執行 java 方法的話,計數器記錄的是虛擬機字節碼指令的地址(當前指令的地址) 。若是仍是 Native 方法,則爲空。

這個內存區域是惟一一個在虛擬機中沒有規定任何 OutOfMemoryError 狀況的區域。

1三、虛擬機棧(線程私有)

是描述java方法執行的內存模型,每一個方法在執行的同時都會建立一個棧幀(Stack Frame)用於存儲局部變量表、操做數棧、動態連接、方法出口等信息。 每個方法從調用直至執行完成的過程,就對應着一個棧幀在虛擬機棧中入棧到出棧的過程。

棧幀( Frame)是用來存儲數據和部分過程結果的數據結構,同時也被用來處理動態連接(Dynamic Linking)、 方法返回值和異常分派(Dispatch Exception)。 棧幀隨着方法調用而建立,隨着方法結束而銷燬——不管方法是正常完成仍是異常完成(拋出了在方法內未被捕獲的異常)都算做方法結束。

img

1四、本地方法區(線程私有)

本地方法區和 Java Stack 做用相似, 區別是虛擬機棧爲執行 Java 方法服務, 而本地方法棧則爲Native 方法服務, 若是一個 VM 實現使用 C-linkage 模型來支持 Native 調用, 那麼該棧將會是一個C 棧,但 HotSpot VM 直接就把本地方法棧和虛擬機棧合二爲一 。

1五、你能保證 GC 執行嗎?

不能,雖然你能夠調用 System.gc() 或者 Runtime.gc(),可是沒有辦法保證 GC的執行。

1六、怎麼獲取 Java 程序使用的內存?堆使用的百分比?

能夠經過 java.lang.Runtime 類中與內存相關方法來獲取剩餘的內存,總內存及最大堆內存。經過這些方法你也能夠獲取到堆使用的百分比及堆內存的剩餘空間。Runtime.freeMemory() 方法返回剩餘空間的字節數,Runtime.totalMemory()方法總內存的字節數,Runtime.maxMemory() 返回最大內存的字節數。

1七、Java 中堆和棧有什麼區別?

JVM 中堆和棧屬於不一樣的內存區域,使用目的也不一樣。棧經常使用於保存方法幀和局部變量,而對象老是在堆上分配。棧一般都比堆小,也不會在多個線程之間共享,而堆被整個 JVM 的全部線程共享。

1八、描述一下 JVM 加載 class 文件的原理機制

JVM 中類的裝載是由類加載器(ClassLoader)和它的子類來實現的,Java 中各種加載器是一個重要的 Java 運行時系統組件,它負責在運行時查找和裝入類文件中的類。

因爲 Java 的跨平臺性,通過編譯的 Java 源程序並非一個可執行程序,而是一個或多個類文件。當 Java 程序須要使用某個類時,JVM 會確保這個類已經被加載、鏈接(驗證、準備和解析)和初始化。類的加載是指把類的.class 文件中的數據讀入到內存中,一般是建立一個字節數組讀入.class 文件,而後產生與所加載類對應的 Class 對象。

加載完成後,Class 對象還不完整,因此此時的類還不可用。當類被加載後就進入鏈接階段,這一階段包括驗證、準備(爲靜態變量分配內存並設置默認的初始值)和解析(將符號引用替換爲直接引用)三個步驟。最後 JVM 對類進行初始化,包括:1)若是類存在直接的父類而且這個類尚未被初始化,那麼就先初始化父類;2)若是類中存在初始化語句,就依次執行這些初始化語句。

類的加載是由類加載器完成的,類加載器包括:根加載器(BootStrap)、擴展加載器(Extension)、系統加載器(System)和用戶自定義類加載器(java.lang.ClassLoader 的子類)。

從 Java 2(JDK 1.2)開始,類加載過程採起了父親委託機制(PDM)。PDM 更好的保證了 Java 平臺的安全性,在該機制中,JVM 自帶的Bootstrap 是根加載器,其餘的加載器都有且僅有一個父類加載器。類的加載首先請求父類加載器加載,父類加載器無能爲力時才由其子類加載器自行加載。JVM 不會向 Java 程序提供對 Bootstrap 的引用。下面是關於幾個類

加載器的說明:

(1)Bootstrap:通常用本地代碼實現,負責加載 JVM 基礎核心類庫(rt.jar);

(2)Extension:從 java.ext.dirs 系統屬性所指定的目錄中加載類庫,它的父加載器是 Bootstrap;

(3)System:又叫應用類加載器,其父類是 Extension。它是應用最普遍的類加載器。它從環境變量 classpath 或者系統屬性

java.class.path 所指定的目錄中記載類,是用戶自定義加載器的默認父加載器。

1九、GC 是什麼?爲何要有 GC?

GC 是垃 圾收 集的 意思 ,內存 處理 是編 程人 員容 易出 現問 題的 地方 ,忘記 或者 錯誤的內 存回 收會 致使 程序 或系 統的 不穩 定甚 至崩 潰, Java 提供 的 GC 功能 能夠 自動監測 對象 是否 超過 做用 域從 而達 到自 動回 收內 存的 目的 ,Java 語言 沒有 提供 釋放已分配內存的 顯示 操做 方法 。Java 程序 員不 用擔 心內 存管 理, 由於 垃圾 收集 器會自動 進行 管理 。要 請求 垃圾 收集 ,可 以調 用下 面的 方法 之一 :System.gc() 或Runtime.getRuntime().gc() ,但 JVM 能夠 屏蔽 掉線 示的 垃圾 回收 調用 。

垃圾回收能夠有效的防止內存泄露,有效的使用能夠使用的內存。垃圾回收器一般是做爲一個單獨的低優先級的線程運行,不可預知的狀況下對內存堆中已經死亡的或者長時間沒有使用的對象進行清除和回收,程序員不能實時的調用垃圾回收器對某個對象或全部對象進行垃圾回收。在 Java 誕生初期,垃圾回收是 Java最大的亮點之一,由於服務器端的編程須要有效的防止內存泄露問題,然而時過境遷,現在 Java 的垃圾回收機制已經成爲被詬病的東。移動智能終端用戶一般以爲 iOS 的系統比 Android 系統有更好的用戶體驗,其中一個深層次的緣由就在於 Android 系統中垃圾回收的不可預知性。

20、堆(Heap-線程共享) -運行時數據區

是被線程共享的一塊內存區域, 建立的對象和數組都保存在 Java 堆內存中,也是垃圾收集器進行垃圾收集的最重要的內存區域。 因爲現代VM 採用分代收集算法, 所以 Java 堆從 GC 的角度還能夠細分爲: 新生代(Eden 區、 From Survivor 區和 To Survivor 區)和老年代。

2一、方法區/永久代(線程共享)

即咱們常說的永久代(Permanent Generation), 用於存儲被 JVM 加載的類信息、常量、靜態變量即、時編譯器編譯後的代碼等數據.HotSpot VM把GC分代收集擴展至方法區, 即便用Java堆的永久代來實現方法區, 這樣 HotSpot 的垃圾收集器就能夠像管理 Java 堆同樣管理這部份內存,而沒必要爲方法區開發專門的內存管理器(永久帶的內存回收的主要目標是針對常量池的回收和類型的卸載, 所以收益通常很小) 。

運行時常量池(Runtime Constant Pool)是方法區的一部分。 Class 文件中除了有類的版本、字段、方法、接口等描述等信息外,還有一項信息是常量池 (Constant Pool Table),用於存放編譯期生成的各類字面量和符號引用,這部份內容將在類加載後存放到方法區的運行時常量池中。 Java 虛擬機對 Class 文件的每一部分(天然也包括常量池)的格式都有嚴格的規定,每個字節用於存儲哪一種數據都必須符合規範上的要求,這樣纔會被虛擬機承認、裝載和執行。

2二、JVM 運行時內存

Java 堆從 GC 的角度還能夠細分爲: 新生代(Eden 區、 From Survivor 區和 To Survivor 區)和老年代。

img

2三、新生代

是用來存放新生的對象。通常佔據堆的 1/3 空間。因爲頻繁建立對象,因此新生代會頻繁觸發MinorGC 進行垃圾回收。新生代又分爲 Eden區、 ServivorFrom、 ServivorTo 三個區。

Eden 區

Java 新對象的出生地(若是新建立的對象佔用內存很大,則直接分配到老年代)。當 Eden 區內存不夠的時候就會觸發 MinorGC,對新生代區進行一次垃圾回收。

ServivorFrom

上一次 GC 的倖存者,做爲這一次 GC 的被掃描者。

ServivorTo

保留了一次 MinorGC 過程當中的倖存者。

MinorGC 的過程(複製->清空->互換)

MinorGC 採用複製算法。

(1) eden、 servicorFrom 複製到 ServicorTo,年齡+1

首先,把 Eden 和 ServivorFrom 區域中存活的對象複製到 ServicorTo 區域(若是有對象的年齡以及達到了老年的標準,則賦值到老年代區),同時把這些對象的年齡+1(若是 ServicorTo 不夠位置了就放到老年區);

(2)清空 eden、 servicorFrom

而後,清空 Eden 和 ServicorFrom 中的對象;

(3) ServicorTo 和 ServicorFrom 互換

最後, ServicorTo 和 ServicorFrom 互換,原 ServicorTo 成爲下一次 GC 時的 ServicorFrom區。

2四、老年代

主要存放應用程序中生命週期長的內存對象。

老年代的對象比較穩定,因此 MajorGC 不會頻繁執行。在進行 MajorGC 前通常都先進行了一次MinorGC,使得有新生代的對象晉身入老年代,致使空間不夠用時才觸發。當沒法找到足夠大的連續空間分配給新建立的較大對象時也會提早觸發一次 MajorGC 進行垃圾回收騰出空間。

MajorGC 採用標記清除算法:首先掃描一次全部老年代,標記出存活的對象,而後回收沒有標記的對象。 ajorGC 的耗時比較長,由於要掃描再回收。 MajorGC 會產生內存碎片,爲了減小內存損耗,咱們通常須要進行合併或者標記出來方便下次直接分配。當老年代也滿了裝不下的時候,就會拋出 OOM(Out of Memory)異常。

2五、永久代

指內存的永久保存區域,主要存放 Class 和 Meta(元數據)的信息,Class 在被加載的時候被放入永久區域, 它和和存放實例的區域不一樣,GC不會在主程序運行期對永久區域進行清理。因此這也致使了永久代的區域會隨着加載的 Class 的增多而脹滿,最終拋出 OOM 異常。

2六、JAVA8 與元數據

在 Java8 中, 永久代已經被移除,被一個稱爲「元數據區」(元空間)的區域所取代。元空間的本質和永久代相似,元空間與永久代之間最大的區別在於: 元空間並不在虛擬機中,而是使用本地內存。所以,默認狀況下,元空間的大小僅受本地內存限制。 類的元數據放入nativememory, 字符串池和類的靜態變量放入 java 堆中, 這樣能夠加載多少類的元數據就再也不由MaxPermSize 控制, 而由系統的實際可用空間來控制。

2七、引用計數法

在 Java 中,引用和對象是有關聯的。若是要操做對象則必須用引用進行。所以,很顯然一個簡單的辦法是經過引用計數來判斷一個對象是否能夠回收。簡單說,即一個對象若是沒有任何與之關聯的引用, 即他們的引用計數都不爲 0, 則說明對象不太可能再被用到,那麼這個對象就是可回收對象。

2八、可達性分析

爲了解決引用計數法的循環引用問題, Java 使用了可達性分析的方法。經過一系列的「GC roots」對象做爲起點搜索。若是在「GC roots」和一個對象之間沒有可達路徑,則稱該對象是不可達的。要注意的是,不可達對象不等價於可回收對象, 不可達對象變爲可回收對象至少要通過兩次標記過程。兩次標記後仍然是可回收對象,則將面臨回收。

2九、標記清除算法( Mark-Sweep)

最基礎的垃圾回收算法,分爲兩個階段,標註和清除。標記階段標記出全部須要回收的對象,清除階段回收被標記的對象所佔用的空間。如圖

img

從圖中咱們就能夠發現,該算法最大的問題是內存碎片化嚴重,後續可能發生大對象不能找到可利用空間的問題。

30、複製算法(copying)

爲了解決 Mark-Sweep 算法內存碎片化的缺陷而被提出的算法。按內存容量將內存劃分爲等大小的兩塊。每次只使用其中一塊,當這一塊內存滿後將尚存活的對象複製到另外一塊上去,把已使用的內存清掉,如圖:

img

這種算法雖然實現簡單,內存效率高,不易產生碎片,可是最大的問題是可用內存被壓縮到了本來的一半。且存活對象增多的話, Copying算法的效率會大大下降。

3一、標記整理算法(Mark-Compact)

結合了以上兩個算法,爲了不缺陷而提出。標記階段和 Mark-Sweep 算法相同, 標記後不是清理對象,而是將存活對象移向內存的一端。而後清除端邊界外的對象。如圖:

img

3二、分代收集算法

分代收集法是目前大部分 JVM 所採用的方法,其核心思想是根據對象存活的不一樣生命週期將內存劃分爲不一樣的域,通常狀況下將 GC 堆劃分爲老生代(Tenured/Old Generation)和新生代(YoungGeneration)。老生代的特色是每次垃圾回收時只有少許對象須要被回收,新生代的特色是每次垃圾回收時都有大量垃圾須要被回收,所以能夠根據不一樣區域選擇不一樣的算法。

3三、新生代與複製算法

目前大部分 JVM 的 GC 對於新生代都採起 Copying 算法,由於新生代中每次垃圾回收都要回收大部分對象,即要複製的操做比較少,但一般並非按照 1: 1 來劃分新生代。通常將新生代劃分爲一塊較大的 Eden 空間和兩個較小的 Survivor 空間(From Space, To Space),每次使用Eden 空間和其中的一塊 Survivor 空間,當進行回收時,將該兩塊空間中還存活的對象複製到另外一塊 Survivor 空間中。

img

3四、老年代與標記複製算法

而老年代由於每次只回收少許對象,於是採用 Mark-Compact 算法。

(1)JAVA 虛擬機提到過的處於方法區的永生代(Permanet Generation), 它用來存儲 class 類,常量,方法描述等。對永生代的回收主要包括廢棄常量和無用的類。

(2)對象的內存分配主要在新生代的 Eden Space 和 Survivor Space 的 From Space(Survivor 目前存放對象的那一塊),少數狀況會直接分配到老生代。

(3)當新生代的 Eden Space 和 From Space 空間不足時就會發生一次 GC,進行 GC 後, EdenSpace 和 From Space 區的存活對象會被挪到 To Space,而後將 Eden Space 和 FromSpace 進行清理。

(4)若是 To Space 沒法足夠存儲某個對象,則將這個對象存儲到老生代。

(5)在進行 GC 後,使用的即是 Eden Space 和 To Space 了,如此反覆循環。

(6)當對象在 Survivor 去躲過一次 GC 後,其年齡就會+1。 默認狀況下年齡到達 15 的對象會被移到老生代中。

3五、JAVA 強引用

在 Java 中最多見的就是強引用, 把一個對象賦給一個引用變量,這個引用變量就是一個強引用。當一個對象被強引用變量引用時,它處於可達狀態,它是不可能被垃圾回收機制回收的,即便該對象之後永遠都不會被用到 JVM 也不會回收。所以強引用是形成 Java 內存泄漏的主要緣由之一。

3六、JAVA軟引用

軟引用須要用 SoftReference 類來實現,對於只有軟引用的對象來講,當系統內存足夠時它不會被回收,當系統內存空間不足時它會被回收。軟引用一般用在對內存敏感的程序中。

3七、JAVA弱引用

弱引用須要用 WeakReference 類來實現,它比軟引用的生存期更短,對於只有弱引用的對象來講,只要垃圾回收機制一運行,無論 JVM 的內存空間是否足夠,總會回收該對象佔用的內存。

3八、JAVA虛引用

虛引用須要 PhantomReference 類來實現,它不能單獨使用,必須和引用隊列聯合使用。 虛引用的主要做用是跟蹤對象被垃圾回收的狀態。

3九、分代收集算法

當前主流 VM 垃圾收集都採用」分代收集」 (Generational Collection)算法, 這種算法會根據對象存活週期的不一樣將內存劃分爲幾塊, 如 JVM 中的新生代、老年代、永久代, 這樣就能夠根據各年代特色分別採用最適當的 GC 算法

40、在新生代-複製算法

每次垃圾收集都能發現大批對象已死, 只有少許存活. 所以選用複製算法, 只須要付出少許存活對象的複製成本就能夠完成收集

4一、在老年代-標記整理算法

由於對象存活率高、沒有額外空間對它進行分配擔保, 就必須採用「標記—清理」或「標記—整理」 算法來進行回收, 沒必要進行內存複製, 且直接騰出空閒內存。

4二、分區收集算法

分區算法則將整個堆空間劃分爲連續的不一樣小區間, 每一個小區間獨立使用, 獨立回收. 這樣作的好處是能夠控制一次回收多少個小區間 , 根據目標停頓時間, 每次合理地回收若干個小區間(而不是整個堆), 從而減小一次 GC 所產生的停頓。

4三、GC 垃圾收集器

Java 堆內存被劃分爲新生代和年老代兩部分,新生代主要使用複製和標記-清除垃圾回收算法;年老代主要使用標記-整理垃圾回收算法,所以 java 虛擬中針對新生代和年老代分別提供了多種不一樣的垃圾收集器, JDK1.6 中 Sun HotSpot 虛擬機的垃圾收集器以下:

img

4四、Serial 垃圾收集器(單線程、 複製算法)

Serial(英文連續) 是最基本垃圾收集器,使用複製算法,曾經是JDK1.3.1 以前新生代惟一的垃圾收集器。 Serial 是一個單線程的收集器,它不但只會使用一個 CPU 或一條線程去完成垃圾收集工做,而且在進行垃圾收集的同時,必須暫停其餘全部的工做線程,直到垃圾收集結束。

Serial 垃圾收集器雖然在收集垃圾過程當中須要暫停全部其餘的工做線程,可是它簡單高效,對於限定單個 CPU 環境來講,沒有線程交互的開銷,能夠得到最高的單線程垃圾收集效率,所以 Serial垃圾收集器依然是 java 虛擬機運行在 Client 模式下默認的新生代垃圾收集器。

4五、ParNew 垃圾收集器(Serial+多線程)

ParNew 垃圾收集器實際上是 Serial 收集器的多線程版本,也使用複製算法,除了使用多線程進行垃圾收集以外,其他的行爲和 Serial 收集器徹底同樣, ParNew 垃圾收集器在垃圾收集過程當中一樣也要暫停全部其餘的工做線程。

ParNew 收集器默認開啓和 CPU 數目相同的線程數,能夠經過-XX:ParallelGCThreads 參數來限制垃圾收集器的線程數。 【Parallel:平行的】

ParNew 雖然是除了多線程外和Serial 收集器幾乎徹底同樣,可是ParNew垃圾收集器是不少 java虛擬機運行在 Server 模式下新生代的默認垃圾收集器。

4六、Parallel Scavenge 收集器(多線程複製算法、高效)

Parallel Scavenge 收集器也是一個新生代垃圾收集器,一樣使用複製算法,也是一個多線程的垃圾收集器, 它重點關注的是程序達到一個可控制的吞吐量(Thoughput, CPU 用於運行用戶代碼的時間/CPU 總消耗時間,即吞吐量=運行用戶代碼時間/(運行用戶代碼時間+垃圾收集時間)),高吞吐量能夠最高效率地利用 CPU 時間,儘快地完成程序的運算任務,主要適用於在後臺運算而不須要太多交互的任務。 自適應調節策略也是 ParallelScavenge 收集器與 ParNew 收集器的一個重要區別。

4七、Serial Old 收集器(單線程標記整理算法 )

Serial Old 是 Serial 垃圾收集器年老代版本,它一樣是個單線程的收集器,使用標記-整理算法,這個收集器也主要是運行在 Client 默認的

java 虛擬機默認的年老代垃圾收集器。在 Server 模式下,主要有兩個用途:

(1)在 JDK1.5 以前版本中與新生代的 Parallel Scavenge 收集器搭配使用。

(2)做爲年老代中使用 CMS 收集器的後備垃圾收集方案。新生代 Serial 與年老代 Serial Old 搭配垃圾收集過程圖:

img

新生代 Parallel Scavenge 收集器與 ParNew 收集器工做原理相似,都是多線程的收集器,都使用的是複製算法,在垃圾收集過程當中都須要暫停全部的工做線程。新生代 ParallelScavenge/ParNew 與年老代 Serial Old 搭配垃圾收集過程圖:

img

4八、Parallel Old 收集器(多線程標記整理算法)

Parallel Old 收集器是Parallel Scavenge的年老代版本,使用多線程的標記-整理算法,在 JDK1.6纔開始提供。

在 JDK1.6 以前,新生代使用 ParallelScavenge 收集器只能搭配年老代的 Serial Old 收集器,只能保證新生代的吞吐量優先,沒法保證總體的吞吐量, Parallel Old 正是爲了在年老代一樣提供吞吐量優先的垃圾收集器, 若是系統對吞吐量要求比較高,能夠優先考慮新生代Parallel Scavenge和年老代 Parallel Old 收集器的搭配策略。

新生代 Parallel Scavenge 和年老代 Parallel Old 收集器搭配運行過程圖

img

4九、CMS 收集器(多線程標記清除算法)

Concurrent mark sweep(CMS)收集器是一種年老代垃圾收集器,其最主要目標是獲取最短垃圾回收停頓時間, 和其餘年老代使用標記-整理算法不一樣,它使用多線程的標記-清除算法。最短的垃圾收集停頓時間能夠爲交互比較高的程序提升用戶體驗。CMS 工做機制相比其餘的垃圾收集器來講更復雜。整個過程分爲如下 4 個階段:

初始標記

只是標記一下 GC Roots 能直接關聯的對象,速度很快,仍然須要暫停全部的工做線程。

併發標記

進行 GC Roots 跟蹤的過程,和用戶線程一塊兒工做,不須要暫停工做線程。

從新標記

爲了修正在併發標記期間,因用戶程序繼續運行而致使標記產生變更的那一部分對象的標記記錄,仍然須要暫停全部的工做線程。

併發清除

清除 GC Roots 不可達對象,和用戶線程一塊兒工做,不須要暫停工做線程。因爲耗時最長的併發標記和併發清除過程當中,垃圾收集線程能夠和用戶如今一塊兒併發工做, 因此整體上來看CMS 收集器的內存回收和用戶線程是一塊兒併發地執行。CMS 收集器工做過程

img

50、G1 收集器

Garbage first 垃圾收集器是目前垃圾收集器理論發展的最前沿成果,相比與 CMS 收集器, G1 收集器兩個最突出的改進是:

(1)基於標記-整理算法,不產生內存碎片。

(2)能夠很是精確控制停頓時間,在不犧牲吞吐量前提下,實現低停頓垃圾回收。G1 收集器避免全區域垃圾收集,它把堆內存劃分爲大小固定的幾個獨立區域,而且跟蹤這些區域的垃圾收集進度,同時在後臺維護一個優先級列表,每次根據所容許的收集時間, 優先回收垃圾最多的區域。區域劃分和優先級區域回收機制,確保 G1 收集器能夠在有限時間得到最高的垃圾收集效率

5一、JVM 類加載機制

JVM 類加載機制分爲五個部分:加載,驗證,準備,解析,初始化,下面咱們就分別來看一下這五個過程。

img

加載

加載是類加載過程當中的一個階段, 這個階段會在內存中生成一個表明這個類的 java.lang.Class 對象, 做爲方法區這個類的各類數據的入口。注意這裏不必定非得要從一個 Class 文件獲取,這裏既能夠從 ZIP 包中讀取(好比從 jar 包和 war 包中讀取),也能夠在運行時計算生成(動態代理),也能夠由其它文件生成(好比將 JSP 文件轉換成對應的 Class 類)。

驗證

這一階段的主要目的是爲了確保 Class 文件的字節流中包含的信息是否符合當前虛擬機的要求,而且不會危害虛擬機自身的安全。

準備

準備階段是正式爲類變量分配內存並設置類變量的初始值階段,即在方法區中分配這些變量所使用的內存空間。注意這裏所說的初始值概念,好比一個類變量定義爲:

實際上變量 v 在準備階段事後的初始值爲 0 而不是 8080, 將 v 賦值爲 8080 的 put static 指令是程序被編譯後, 存放於類構造器方法之中。

可是注意若是聲明爲:

public static final int v = 8080;

在編譯階段會爲 v 生成 ConstantValue 屬性,在準備階段虛擬機會根據 ConstantValue 屬性將 v賦值爲 8080。

解析

解析階段是指虛擬機將常量池中的符號引用替換爲直接引用的過程。符號引用就是 class 文件中的

public static int v = 8080;

實際上變量 v 在準備階段事後的初始值爲 0 而不是 8080, 將 v 賦值爲 8080 的 put static 指令是程序被編譯後, 存放於類構造器方法之中。可是注意若是聲明爲:

在編譯階段會爲 v 生成 ConstantValue 屬性,在準備階段虛擬機會根據 ConstantValue 屬性將 v賦值爲 8080。

解析

解析階段是指虛擬機將常量池中的符號引用替換爲直接引用的過程。符號引用就是 class 文件中的

public static final int v = 8080;

在編譯階段會爲 v 生成 ConstantValue 屬性,在準備階段虛擬機會根據 ConstantValue 屬性將 v賦值爲 8080。

解析

解析階段是指虛擬機將常量池中的符號引用替換爲直接引用的過程。符號引用就是 class 文件中的:

(1) CONSTANT_Class_info

(2)CONSTANT_Field_info

(3)CONSTANT_Method_info

等類型的常量。

符號引用

符號引用與虛擬機實現的佈局無關, 引用的目標並不必定要已經加載到內存中。 各類虛擬機實現的內存佈局能夠各不相同,可是它們能接受的符號引用必須是一致的,由於符號引用的字面量形式明肯定義在 Java 虛擬機規範的 Class 文件格式中。

直接引用

直接引用能夠是指向目標的指針,相對偏移量或是一個能間接定位到目標的句柄。若是有了直接引用,那引用的目標一定已經在內存中存在。

初始化

初始化階段是類加載最後一個階段,前面的類加載階段以後,除了在加載階段能夠自定義類加載器之外,其它操做都由 JVM 主導。到了初始階段,纔開始真正執行類中定義的 Java 程序代碼。

類構造器

初始化階段是執行類構造器方法的過程。 方法是由編譯器自動收集類中的類變量的賦值操做和靜態語句塊中的語句合併而成的。虛擬機會保證子方法執行以前,父類的方法已經執行完畢, 若是一個類中沒有對靜態變量賦值也沒有靜態語句塊,那麼編譯器能夠不爲這個類生成()方法。注意如下幾種狀況不會執行類初始化:

(1)經過子類引用父類的靜態字段,只會觸發父類的初始化,而不會觸發子類的初始化。

(2)定義對象數組,不會觸發該類的初始化。

(3)常量在編譯期間會存入調用類的常量池中,本質上並無直接引用定義常量的類,不會觸發定義常量所在的類。

(4)經過類名獲取 Class 對象,不會觸發類的初始化。

(5)經過 Class.forName 加載指定類時,若是指定參數 initialize 爲 false 時,也不會觸發類初始化,其實這個參數是告訴虛擬機,是否要對類進行初始化。

(6)經過 ClassLoader 默認的 loadClass 方法,也不會觸發初始化動做。

5二、類加載器

虛擬機設計團隊把加載動做放到 JVM 外部實現,以便讓應用程序決定如何獲取所需的類, JVM 提供了 3 種類加載器:

啓動類加載器(Bootstrap ClassLoader)

負責加載 JAVA_HOME\lib 目錄中的, 或經過-Xbootclasspath 參數指定路徑中的, 且被虛擬機承認(按文件名識別, 如 rt.jar) 的類。

擴展類加載器(Extension ClassLoader)

負責加載 JAVA_HOME\lib\ext 目錄中的,或經過 java.ext.dirs 系統變量指定路徑中的類庫。

應用程序類加載器(Application ClassLoader):

負責加載用戶路徑(classpath)上的類庫。JVM 經過雙親委派模型進行類的加載, 固然咱們也能夠經過繼承 java.lang.ClassLoader實現自定義的類加載器。

img

5三、雙親委派

當一個類收到了類加載請求,他首先不會嘗試本身去加載這個類,而是把這個請求委派給父類去完成,每個層次類加載器都是如此,所以全部的加載請求都應該傳送到啓動類加載其中,只有當父類加載器反饋本身沒法完成這個請求的時候(在它的加載路徑下沒有找到所需加載的Class), 子類加載器纔會嘗試本身去加載。

採用雙親委派的一個好處是好比加載位於 rt.jar 包中的類 java.lang.Object,無論是哪一個加載器加載這個類,最終都是委託給頂層的啓動類加載器進行加載,這樣就保證了使用不一樣的類加載器最終獲得的都是一樣一個 Object 對象

img

5四、OSGI( 動態模型系統)

OSGi(Open Service Gateway Initiative),是面向 Java 的動態模型系統,是 Java 動態化模塊化系統的一系列規範。

5五、動態改變構造

OSGi 服務平臺提供在多種網絡設備上無需重啓的動態改變構造的功能。爲了最小化耦合度和促使這些耦合度可管理, OSGi 技術提供一種面向服務的架構,它能使這些組件動態地發現對方。

5六、模塊化編程與熱插拔

OSGi 旨在爲實現 Java 程序的模塊化編程提供基礎條件,基於 OSGi 的程序極可能能夠實現模塊級的熱插拔功能,當程序升級更新時,能夠只停用、從新安裝而後啓動程序的其中一部分,這對企業級程序開發來講是很是具備誘惑力的特性。

OSGi 描繪了一個很美好的模塊化開發目標,並且定義了實現這個目標的所須要服務與架構,同時也有成熟的框架進行實現支持。但並不是全部的應用都適合採用 OSGi 做爲基礎架構,它在提供強大功能同時,也引入了額外的複雜度,由於它不遵照了類加載的雙親委託模型。

5七、JVM內存模型

線程獨佔:棧,本地方法棧,程序計數器

線程共享:堆,方法區

5八、棧

又稱方法棧,線程私有的,線程執行方法是都會建立一個棧陣,用來存儲局部變量表,操做棧,動態連接,方法出口等信息.調用方法時執行入棧,方法返回式執行出棧.

5九、本地方法棧

與棧相似,也是用來保存執行方法的信息.執行Java方法是使用棧,執行Native方法時使用本地方法棧.

60、程序計數器

保存着當前線程執行的字節碼位置,每一個線程工做時都有獨立的計數器,只爲執行Java方法服務,執行Native方法時,程序計數器爲空.

6一、堆

JVM內存管理最大的一塊,對被線程共享,目的是存放對象的實例,幾乎所欲的對象實例都會放在這裏,當堆沒有可用空間時,會拋出OOM異常.根據對象的存活週期不一樣,JVM把對象進行分代管理,由垃圾回收器進行垃圾的回收管理

6二、方法區

又稱非堆區,用於存儲已被虛擬機加載的類信息,常量,靜態變量,即時編譯器優化後的代碼等數據.1.7的永久代和1.8的元空間都是方法區的一種實現。

6三、分代回收

分代回收基於兩個事實:大部分對象很快就不使用了,還有一部分不會當即無用,但也不會持續很長時間

img

年輕代->標記-複製

老年代->標記-清除

6四、堆和棧的區別

棧是運行時單位,表明着邏輯,內含基本數據類型和堆中對象引用,所在區域連續,沒有碎片;堆是存儲單位,表明着數據,可被多個棧共享(包括成員中基本數據類型、引用和引用對象),所在區域不連續,會有碎片。

(1)功能不一樣

棧內存用來存儲局部變量和方法調用,而堆內存用來存儲Java中的對象。不管是成員變量,局部變量,仍是類變量,它們指向的對象都存儲在堆內存中。

(2)共享性不一樣

棧內存是線程私有的。

堆內存是全部線程共有的。

(3)異常錯誤不一樣

若是棧內存或者堆內存不足都會拋出異常。

棧空間不足:java.lang.StackOverFlowError。

堆空間不足:java.lang.OutOfMemoryError。

(4)空間大小

棧的空間大小遠遠小於堆的

6五、何時會觸發FullGC

除直接調用System.gc外,觸發Full GC執行的狀況有以下四種。

(1)舊生代空間不足

舊生代空間只有在新生代對象轉入及建立爲大對象、大數組時纔會出現不足的現象,當執行Full GC後空間仍然不足,則拋出以下錯

誤:

java.lang.OutOfMemoryError: Java heap space

爲避免以上兩種情況引發的FullGC,調優時應儘可能作到讓對象在Minor GC階段被回收、讓對象在新生代多存活一段時間及不要建立過大的對象及數組。

(2) Permanet Generation空間滿

PermanetGeneration中存放的爲一些class的信息等,當系統中要加載的類、反射的類和調用的方法較多時,Permanet Generation可能會被佔滿,在未配置爲採用CMS GC的狀況下會執行Full GC。若是通過Full GC仍然回收不了,那麼JVM會拋出以下錯誤信息:

java.lang.OutOfMemoryError: PermGen space

爲避免Perm Gen佔滿形成Full GC現象,可採用的方法爲增大Perm Gen空間或轉爲使用CMS GC。

(3)CMS GC時出現promotion failed和concurrent mode failure

對於採用CMS進行舊生代GC的程序而言,尤爲要注意GC日誌中是否有promotion failed和concurrent mode failure兩種情況,當這兩種情況出現時可能會觸發Full GC。

promotionfailed是在進行Minor GC時,survivor space放不下、對象只能放入舊生代,而此時舊生代也放不下形成的;concurrentmode failure是在執行CMS GC的過程當中同時有對象要放入舊生代,而此時舊生代空間不足形成的。

應對措施爲:增大survivorspace、舊生代空間或調低觸發併發GC的比率,但在JDK 5.0+、6.0+的版本中有可能會因爲JDK的bug29致使CMS在remark完畢後好久才觸發sweeping動做。對於這種情況,可經過設置-XX:CMSMaxAbortablePrecleanTime=5(單位爲ms)來避免。

(4)統計獲得的Minor GC晉升到舊生代的平均大小大於舊生代的剩餘空間

這是一個較爲複雜的觸發狀況,Hotspot爲了不因爲新生代對象晉升到舊生代致使舊生代空間不足的現象,在進行Minor GC時,作了一個判斷,若是以前統計所獲得的Minor GC晉升到舊生代的平均大小大於舊生代的剩餘空間,那麼就直接觸發Full GC。

例如程序第一次觸發MinorGC後,有6MB的對象晉升到舊生代,那麼當下一次Minor GC發生時,首先檢查舊生代的剩餘空間是否大於6MB,若是小於6MB,則執行Full GC。

當新生代採用PSGC時,方式稍有不一樣,PS GC是在Minor GC後也會檢查,例如上面的例子中第一次Minor GC後,PS GC會檢查此時舊生代的剩餘空間是否大於6MB,如小於,則觸發對舊生代的回收。除了以上4種情況外,對於使用RMI來進行RPC或管理的Sun JDK應用而言,默認狀況下會一小時執行一次Full GC。可經過在啓動時經過- java-Dsun.rmi.dgc.client.gcInterval=3600000來設置Full GC執行的間隔時間或經過-XX:+ DisableExplicitGC來禁止RMI調用System.gc

6六、什麼是Java虛擬機?爲何Java被稱做是「平臺無關的編程語言」?

Java虛擬機是一個能夠執行Java字節碼的虛擬機進程。Java源文件被編譯成能被Java虛擬機執行的字節碼文件。 Java被設計成容許應用程序能夠運行在任意的平臺,而不須要程序員爲每個平臺單獨重寫或者是從新編譯。Java虛擬機讓這個變爲可能,由於它知道底層硬件平臺的指令長度和其餘特性。

6七、對象分配規則

(1)對象優先分配在Eden區,若是Eden區沒有足夠的空間時,虛擬機執行一次Minor GC。

(2)大對象直接進入老年代(大對象是指須要大量連續內存空間的對象)。這樣作的目的是避免在Eden區和兩個Survivor區之間發生大量的內存拷貝(新生代採用複製算法收集內存)。

(3)長期存活的對象進入老年代。虛擬機爲每一個對象定義了一個年齡計數器,若是對象通過了1次Minor GC那麼對象會進入Survivor區,以後每通過一次Minor GC那麼對象的年齡加1,知道達到閥值對象進入老年區。

(4) 動態判斷對象的年齡。若是Survivor區中相同年齡的全部對象大小的總和大於Survivor空間的一半,年齡大於或等於該年齡的對象能夠直接進入老年代。

(5) 空間分配擔保。每次進行Minor GC時,JVM會計算Survivor區移至老年區的對象的平均大小,若是這個值大於老年區的剩餘值大小則進行一次Full GC,若是小於檢查HandlePromotionFailure設置,若是true則只進行Monitor GC,若是false則進行Full GC

6八、描述一下JVM加載class文件的原理機制?

JVM中類的裝載是由類加載器(ClassLoader)和它的子類來實現的,Java中的類加載器是一個重要的Java運行時系統組件,它負責在運行時查找和裝入類文件中的類。

因爲Java的跨平臺性,通過編譯的Java源程序並非一個可執行程序,而是一個或多個類文件。當Java程序須要使用某個類時,JVM會確保這個類已經被加載、鏈接(驗證、準備和解析)和初始化。

類的加載是指把類的.class文件中的數據讀入到內存中,一般是建立一個字節數組讀入.class文件,而後產生與所加載類對應的Class對象。加載完成後,Class對象還不完整,因此此時的類還不可用。

當類被加載後就進入鏈接階段,這一階段包括驗證、準備(爲靜態變量分配內存並設置默認的初始值)和解析(將符號引用替換爲直接引用)三個步驟。最後JVM對類進行初始化,

包括:

(1)若是類存在直接的父類而且這個類尚未被初始化,那麼就先初始化父類;

(2)若是類中存在初始化語句,就依次執行這些初始化語句。 類的加載是由類加載器完成的,類加載器包括:根加載器(BootStrap)、擴展加載器(Extension)、系統加載器(System)和用戶自定義類加載器(java.lang.ClassLoader的子類)。

從Java 2(JDK 1.2)開始,類加載過程採起了父親委託機制(PDM)。PDM更好的保證了Java平臺的安全性,在該機制中,JVM自帶的Bootstrap是根加載器,其餘的加載器都有且僅有一個父類加載器。類的加載首先請求父類加載器加載,父類加載器無能爲力時才由其子類加載器自行加載。JVM不會向Java程序提供對Bootstrap的引用。下面是關於幾個類加載器的說明

Bootstrap:通常用本地代碼實現,負責加載JVM基礎核心類庫(rt.jar);

Extension:從java.ext.dirs系統屬性所指定的目錄中加載類庫,它的父加載器是Bootstrap;

System:又叫應用類加載器,其父類是Extension。它是應用最普遍的類加載器。它從環境變量classpath或者系統屬性java.class.path所指定的目錄中記載類,是用戶自定義加載器的默認父加載器。

6九、Java對象建立過程

(1)JVM遇到一條新建對象的指令時首先去檢查這個指令的參數是否能在常量池中定義到一個類的符號引用。而後加載這個類(類加載過程在後邊講)

(2)爲對象分配內存。一種辦法「指針碰撞」、一種辦法「空閒列表」,最終經常使用的辦法「本地線程緩衝分配(TLAB)」

(3)將除對象頭外的對象內存空間初始化爲0

(4)對對象頭進行必要設置

70、簡述Java的對象結構

Java對象由三個部分組成:對象頭、實例數據、對齊填充。

對象頭由兩部分組成,第一部分存儲對象自身的運行時數據:哈希碼、GC分代年齡、鎖標識狀態、線程持有的鎖、偏向線程ID(通常佔32/64 bit)。第二部分是指針類型,指向對象的類元數據類型(即對象表明哪一個類)。若是是數組對象,則對象頭中還有一部分用來記錄數組長度。

實例數據用來存儲對象真正的有效信息(包括父類繼承下來的和本身定義的)

對齊填充:JVM要求對象起始地址必須是8字節的整數倍(8字節對齊 )

7一、如何判斷對象能夠被回收

判斷對象是否存活通常有兩種方式:

引用計數:每一個對象有一個引用計數屬性,新增一個引用時計數加1,引用釋放時計數減1,計數爲0時能夠回收。此方法簡單,沒法解決對象相互循環引用的問題。

可達性分析(Reachability Analysis):從GC Roots開始向下搜索,搜索所走過的路徑稱爲引用鏈。當一個對象到GC Roots沒有任何引用鏈相連時,則證實此對象是不可用的,不可達對象。

7二、JVM的永久代中會發生垃圾回收麼

垃圾回收不會發生在永久代,若是永久代滿了或者是超過了臨界值,會觸發徹底垃圾回收(Full GC)。若是你仔細查看垃圾收集器的輸出信息,就會發現永久代也是被回收的。這就是爲何正確的永久代大小對避免Full GC是很是重要的緣由。請參考下Java8:從永久代到元數據區 (注:Java8中已經移除了永久代,新加了一個叫作元數據區的native內存區)

7三、垃圾收集算法

GC最基礎的算法有三種: 標記 -清除算法、複製算法、標記-壓縮算法,咱們經常使用的垃圾回收器通常都採用分代收集算法。

標記 -清除算法

「標記-清除」(Mark-Sweep)算法,如它的名字同樣,算法分爲「標記」和「清除」兩個階段:首先標記出全部須要回收的對象,在標記完成後統一回收掉全部被標記的對象。

複製算法

「複製」(Copying)的收集算法,它將可用內存按容量劃分爲大小相等的兩塊,每次只使用其中的一塊。當這一塊的內存用完了,就將還存活着的對象複製到另一塊上面,而後再把已使用過的內存空間一次清理掉。

標記-壓縮算法

標記過程仍然與「標記-清除」算法同樣,但後續步驟不是直接對可回收對象進行清理,而是讓全部存活的對象都向一端移動,而後直接清理掉端邊界之外的內存

分代收集算法

「分代收集」(Generational Collection)算法,把Java堆分爲新生代和老年代,這樣就能夠根據各個年代的特色採用最適當的收集算法

7四、調優命令有哪些?

Sun JDK監控和故障處理命令有jps jstat jmap jhat jstack jinfo

(1)jps,JVM Process Status Tool,顯示指定系統內全部的HotSpot虛擬機進程。

(1)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 這個命令做用是實時查看和調整虛擬機運行參數

7五、調優工具

經常使用調優工具分爲兩類,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的內存分析工具,是一個快速、功能豐富的Javaheap分析工具,它能夠幫助咱們查找內存泄漏和減小內存消耗

(4)GChisto,一款專業分析gc日誌的工具

7六、Minor GC與Full GC分別在何時發生?

新生代內存不夠用時候發生MGC也叫YGC,JVM內存不夠的時候發生FGC

7七、你知道哪些JVM性能調優

設定堆內存大小

-Xmx:堆內存最大限制。

設定新生代大小。 新生代不宜過小,不然會有大量對象涌入老年代

-XX:NewSize:新生代大小

-XX:NewRatio 新生代和老生代佔比

-XX:SurvivorRatio:伊甸園空間和倖存者空間的佔比

設定垃圾回收器 年輕代用 -XX:+UseParNewGC 年老代用-XX:+UseConcMarkSweepGC

六、常見設計模式

1.說一下設計模式?你都知道哪些?

答:設計模式總共有 23 種,整體來講能夠分爲三大類:建立型模式( Creational Patterns )、結構型模式( Structural Patterns )和行爲型模式( Behavioral Patterns )。

**分類** **包含** **關注點**     建立型模式 工廠模式、抽象工廠模式、單例模式、建造者模式、原型模式 關注於對象的建立,同時隱藏建立邏輯   結構型模式 適配器模式、過濾器模式、裝飾模式、享元模式、代理模式、外觀模式、組合模式、橋接模式 關注類和對象之間的組合   行爲型模式 責任鏈模式、命令模式、中介者模式、觀察者模式、狀態模式、策略模式、模板模式、空對象模式、備忘錄模式、迭代器模式、解釋器模式、訪問者模式 關注對象之間的通訊

下面會對經常使用的設計模式分別作詳細的說明。

2.什麼是單例模式?

答:單例模式是一種經常使用的軟件設計模式,在應用這個模式時,單例對象的類必須保證只有一個實例存在,整個系統只能使用一個對象實例。

優勢:不會頻繁地建立和銷燬對象,浪費系統資源。

使用場景:IO 、數據庫鏈接、Redis 鏈接等。

單例模式代碼實現:

class Singleton {
    private static Singleton instance = new Singleton();
    public static Singleton getInstance() {
        return instance;
    }
}

單例模式調用代碼:

public class Lesson7\_3 {
    public static void main(String[] args) {
        Singleton singleton1 = Singleton.getInstance();
        Singleton singleton2 = Singleton.getInstance();
        System.out.println(singleton1 == singleton2); 
    }
}

程序的輸出結果:true

能夠看出以上單例模式是在類加載的時候就建立了,這樣會影響程序的啓動速度,那如何實現單例模式的延遲加載?在使用時再建立?

單例延遲加載代碼:

// 單例模式-延遲加載版
class SingletonLazy {
    private static SingletonLazy instance;
    public static SingletonLazy getInstance() {
        if (instance == null) {
            instance = new SingletonLazy();
        }
        return instance;
    }
}

以上爲非線程安全的,單例模式如何支持多線程?

使用 synchronized 來保證,單例模式的線程安全代碼:

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

3.什麼是簡單工廠模式?

答:簡單工廠模式又叫靜態工廠方法模式,就是創建一個工廠類,對實現了同一接口的一些類進行實例的建立。好比,一臺咖啡機就能夠理解爲一個工廠模式,你只須要按下想喝的咖啡品類的按鈕(摩卡或拿鐵),它就會給你生產一杯相應的咖啡,你不須要管它內部的具體實現,只要告訴它你的需求便可。

優勢

  • 工廠類含有必要的判斷邏輯,能夠決定在何時建立哪個產品類的實例,客戶端能夠免除直接建立產品對象的責任,而僅僅「消費」產品;簡單工廠模式經過這種作法實現了對責任的分割,它提供了專門的工廠類用於建立對象;
  • 客戶端無須知道所建立的具體產品類的類名,只須要知道具體產品類所對應的參數便可,對於一些複雜的類名,經過簡單工廠模式能夠減小使用者的記憶量;
  • 經過引入配置文件,能夠在不修改任何客戶端代碼的狀況下更換和增長新的具體產品類,在必定程度上提升了系統的靈活性。

缺點

  • 不易拓展,一旦添加新的產品類型,就不得不修改工廠的建立邏輯;
  • 產品類型較多時,工廠的建立邏輯可能過於複雜,一旦出錯可能形成全部產品的建立失敗,不利於系統的維護。

簡單工廠示意圖以下:

img

簡單工廠代碼實現

class Factory {
    public static String createProduct(String product) {
        String result = null;
        switch (product) {
            case "Mocca":
                result = "摩卡";
                break;
            case "Latte":
                result = "拿鐵";
                break;
            default:
                result = "其餘";
                break;
        }
        return result;
    }
}

4.什麼是抽象工廠模式?

答:抽象工廠模式是在簡單工廠的基礎上將將來可能須要修改的代碼抽象出來,經過繼承的方式讓子類去作決定。

好比,以上面的咖啡工廠爲例,某天個人口味忽然變了,不想喝咖啡了想喝啤酒,這個時候若是直接修改簡單工廠裏面的代碼,這種作法不但不夠優雅,也不符合軟件設計的「開閉原則」,由於每次新增品類都要修改原來的代碼。這個時候就能夠使用抽象工廠類了,抽象工廠裏只聲明方法,具體的實現交給子類(子工廠)去實現,這個時候再有新增品類的需求,只須要新建立代碼便可。

抽象工廠實現代碼以下:

public class AbstractFactoryTest {
   public static void main(String[] args) {
       // 抽象工廠
       String result = (new CoffeeFactory()).createProduct("Latte");
       System.out.println(result); // output:拿鐵
   }
}
// 抽象工廠
abstract class AbstractFactory{
   public abstract String createProduct(String product);
}
// 啤酒工廠
class BeerFactory extends AbstractFactory{
   @Override
   public String createProduct(String product) {
       String result = null;
       switch (product) {
           case "Hans":
               result = "漢斯";
               break;
           case "Yanjing":
               result = "燕京";
               break;
           default:
               result = "其餘啤酒";
               break;
       }
       return result;
   }
}
/\* \* 咖啡工廠 \*/
class CoffeeFactory extends AbstractFactory{
   @Override
   public String createProduct(String product) {
       String result = null;
       switch (product) {
           case "Mocca":
               result = "摩卡";
               break;
           case "Latte":
               result = "拿鐵";
               break;
           default:
               result = "其餘咖啡";
               break;
       }
       return result;
   }
}

5.什麼是觀察者模式?

觀察者模式是定義對象間的一種一對多依賴關係,使得每當一個對象狀態發生改變時,其相關依賴對象皆獲得通知並被自動更新。觀察者模式又叫作發佈-訂閱(Publish/Subscribe)模式、模型-視圖(Model/View)模式、源-監聽器(Source/Listener)模式或從屬者(Dependents)模式。 優勢

  • 觀察者模式能夠實現表示層和數據邏輯層的分離,並定義了穩定的消息更新傳遞機制,抽象了更新接口,使得能夠有各類各樣不一樣的表示層做爲具體觀察者角色;
  • 觀察者模式在觀察目標和觀察者之間創建一個抽象的耦合;
  • 觀察者模式支持廣播通訊;
  • 觀察者模式符合開閉原則(對拓展開放,對修改關閉)的要求。

缺點

  • 若是一個觀察目標對象有不少直接和間接的觀察者的話,將全部的觀察者都通知到會花費不少時間;
  • 若是在觀察者和觀察目標之間有循環依賴的話,觀察目標會觸發它們之間進行循環調用,可能致使系統崩潰;
  • 觀察者模式沒有相應的機制讓觀察者知道所觀察的目標對象是怎麼發生變化的,而僅僅只是知道觀察目標發生了變化。

在觀察者模式中有以下角色:

  • Subject:抽象主題(抽象被觀察者),抽象主題角色把全部觀察者對象保存在一個集合裏,每一個主題均可以有任意數量的觀察者,抽象主題提供一個接口,能夠增長和刪除觀察者對象;
  • ConcreteSubject:具體主題(具體被觀察者),該角色將有關狀態存入具體觀察者對象,在具體主題的內部狀態發生改變時,給全部註冊過的觀察者發送通知;
  • Observer:抽象觀察者,是觀察者者的抽象類,它定義了一個更新接口,使得在獲得主題更改通知時更新本身;
  • ConcrereObserver:具體觀察者,實現抽象觀察者定義的更新接口,以便在獲得主題更改通知時更新自身的狀態。

觀察者模式實現代碼以下。

1)定義觀察者(消息接收方)
/\* \* 觀察者(消息接收方) \*/
interface Observer {
    public void update(String message);
}
/\* \* 具體的觀察者(消息接收方) \*/
class ConcrereObserver implements Observer {
    private String name;

    public ConcrereObserver(String name) {
        this.name = name;
    }

    @Override
    public void update(String message) {
        System.out.println(name + ":" + message);
    }
}
2)定義被觀察者(消息發送方)
/\* \* 被觀察者(消息發佈方) \*/
interface Subject {
    // 增長訂閱者
    public void attach(Observer observer);
    // 刪除訂閱者
    public void detach(Observer observer);
    // 通知訂閱者更新消息
    public void notify(String message);
}
/\* \* 具體被觀察者(消息發佈方) \*/
class ConcreteSubject implements Subject {
    // 訂閱者列表(存儲信息)
    private List<Observer> list = new ArrayList<Observer>();
    @Override
    public void attach(Observer observer) {
        list.add(observer);
    }
    @Override
    public void detach(Observer observer) {
        list.remove(observer);
    }
    @Override
    public void notify(String message) {
        for (Observer observer : list) {
            observer.update(message);
        }
    }
}
3)代碼調用
public class ObserverTest {
    public static void main(String[] args) {
        // 定義發佈者
        ConcreteSubject concreteSubject = new ConcreteSubject();
        // 定義訂閱者
        ConcrereObserver concrereObserver = new ConcrereObserver("老王");
        ConcrereObserver concrereObserver2 = new ConcrereObserver("Java");
        // 添加訂閱
        concreteSubject.attach(concrereObserver);
        concreteSubject.attach(concrereObserver2);
        // 發佈信息
        concreteSubject.notify("更新了");
    }
}

程序執行結果以下:

老王:更新了

Java:更新了

6.什麼是裝飾器模式?

答:裝飾器模式是指動態地給一個對象增長一些額外的功能,同時又不改變其結構。

優勢:裝飾類和被裝飾類能夠獨立發展,不會相互耦合,裝飾模式是繼承的一個替代模式,裝飾模式能夠動態擴展一個實現類的功能。

裝飾器模式的關鍵:裝飾器中使用了被裝飾的對象。

好比,建立一個對象「laowang」,給對象添加不一樣的裝飾,穿上夾克、戴上帽子......,這個執行過程就是裝飾者模式,實現代碼以下。

1)定義頂層對象,定義行爲
interface IPerson {
    void show();
}
2)定義裝飾器超類
class DecoratorBase implements IPerson{
    IPerson iPerson;
    public DecoratorBase(IPerson iPerson){
        this.iPerson = iPerson;
    }
    @Override
    public void show() {
        iPerson.show();
    }
}
3)定義具體裝飾器
class Jacket extends DecoratorBase {
    public Jacket(IPerson iPerson) {
        super(iPerson);
    }
    @Override
    public void show() {
        // 執行已有功能
        iPerson.show();
        // 定義新行爲
        System.out.println("穿上夾克");
    }
}
class Hat extends DecoratorBase {
    public Hat(IPerson iPerson) {
        super(iPerson);
    }
    @Override
    public void show() {
        // 執行已有功能
        iPerson.show();
        // 定義新行爲
        System.out.println("戴上帽子");
    }
}
4)定義具體對象
class LaoWang implements IPerson{
    @Override
    public void show() {
        System.out.println("什麼都沒穿");
    }
}
5)裝飾器模式調用
public class DecoratorTest {
    public static void main(String[] args) {
        LaoWang laoWang = new LaoWang();
        Jacket jacket = new Jacket(laoWang);
        Hat hat = new Hat(jacket);
        hat.show();
    }
}

7.什麼是模板方法模式?

答:模板方法模式是指定義一個模板結構,將具體內容延遲到子類去實現。

優勢

  • 提升代碼複用性:將相同部分的代碼放在抽象的父類中,而將不一樣的代碼放入不一樣的子類中;
  • 實現了反向控制:經過一個父類調用其子類的操做,經過對子類的具體實現擴展不一樣的行爲,實現了反向控制而且符合開閉原則。

以給冰箱中放水果爲例,好比,我要放一個香蕉:開冰箱門 → 放香蕉 → 關冰箱門;若是我再要放一個蘋果:開冰箱門 → 放蘋果 → 關冰箱門。能夠看出它們之間的行爲模式都是同樣的,只是存放的水果品類不一樣而已,這個時候就很是適用模板方法模式來解決這個問題,實現代碼以下:

/\* \* 添加模板方法 \*/
abstract class Refrigerator {
    public void open() {
        System.out.println("開冰箱門");
    }
    public abstract void put();

    public void close() {
        System.out.println("關冰箱門");
    }
}
class Banana extends Refrigerator {
    @Override
    public void put() {
        System.out.println("放香蕉");
    }
}
class Apple extends Refrigerator {
    @Override
    public void put() {
        System.out.println("放蘋果");
    }
}
/\* \* 調用模板方法 \*/
public class TemplateTest {
    public static void main(String[] args) {
        Refrigerator refrigerator = new Banana();
        refrigerator.open();
        refrigerator.put();
        refrigerator.close();
    }
}

程序執行結果:

開冰箱門

放香蕉

關冰箱門

8.什麼是代理模式?

代理模式是給某一個對象提供一個代理,並由代理對象控制對原對象的引用。

優勢

  • 代理模式可以協調調用者和被調用者,在必定程度上下降了系統的耦合度;
  • 能夠靈活地隱藏被代理對象的部分功能和服務,也增長額外的功能和服務。

缺點

  • 因爲使用了代理模式,所以程序的性能沒有直接調用性能高;
  • 使用代理模式提升了代碼的複雜度。

舉一個生活中的例子:好比買飛機票,因爲離飛機場太遠,直接去飛機場買票不太現實,這個時候咱們就能夠上攜程 App 上購買飛機票,這個時候攜程 App 就至關因而飛機票的代理商。

代理模式實現代碼以下:

/\* \* 定義售票接口 \*/
interface IAirTicket {
    void buy();
}
/\* \* 定義飛機場售票 \*/
class AirTicket implements IAirTicket {
    @Override
    public void buy() {
        System.out.println("買票");
    }
}
/\* \* 代理售票平臺 \*/
class ProxyAirTicket implements IAirTicket {
    private AirTicket airTicket;
    public ProxyAirTicket() {
        airTicket = new AirTicket();
    }
    @Override
    public void buy() {
        airTicket.buy();
    }
}
/\* \* 代理模式調用 \*/
public class ProxyTest {
    public static void main(String[] args) {
        IAirTicket airTicket = new ProxyAirTicket();
        airTicket.buy();
    }
}

9.什麼是策略模式?

答:策略模式是指定義一系列算法,將每一個算法都封裝起來,而且使他們之間能夠相互替換。

優勢:遵循了開閉原則,擴展性良好。

缺點:隨着策略的增長,對外暴露愈來愈多。

以生活中的例子來講,好比咱們要出去旅遊,選擇性不少,能夠選擇騎車、開車、坐飛機、坐火車等,就能夠使用策略模式,把每種出行做爲一種策略封裝起來,後面增長了新的交通方式了,如超級高鐵、火箭等,就能夠不須要改動原有的類,新增交通方式便可,這樣也符合軟件開發的開閉原則。 策略模式實現代碼以下:

/\* \* 聲明旅行 \*/
interface ITrip {
    void going();
}
class Bike implements ITrip {
    @Override
    public void going() {
        System.out.println("騎自行車");
    }
}
class Drive implements ITrip {
    @Override
    public void going() {
        System.out.println("開車");
    }
}
/\* \* 定義出行類 \*/
class Trip {
    private ITrip trip;

    public Trip(ITrip trip) {
        this.trip = trip;
    }

    public void doTrip() {
        this.trip.going();
    }
}
/\* \* 執行方法 \*/
public class StrategyTest {
    public static void main(String[] args) {
        Trip trip = new Trip(new Bike());
        trip.doTrip();
    }
}

程序執行的結果:

騎自行車

10.什麼是適配器模式?

答:適配器模式是將一個類的接口變成客戶端所指望的另外一種接口,從而使本來因接口不匹配而沒法一塊兒工做的兩個類可以在一塊兒工做。

優勢

  • 可讓兩個沒有關聯的類一塊兒運行,起着中間轉換的做用;
  • 靈活性好,不會破壞原有的系統。

缺點:過多地使用適配器,容易使代碼結構混亂,如明明看到調用的是 A 接口,內部調用的倒是 B 接口的實現。

以生活中的例子來講,好比有一個充電器是 MicroUSB 接口,而手機充電口倒是 TypeC 的,這個時候就須要一個把 MicroUSB 轉換成 TypeC 的適配器,以下圖所示:

適配器實現代碼以下:

/\* \* 傳統的充電線 MicroUSB \*/
interface MicroUSB {
    void charger();
}
/\* \* TypeC 充電口 \*/
interface ITypeC {
    void charger();
}
class TypeC implements ITypeC {
    @Override
    public void charger() {
        System.out.println("TypeC 充電");
    }
}
/\* \* 適配器 \*/
class AdapterMicroUSB implements MicroUSB {
    private TypeC typeC;

    public AdapterMicroUSB(TypeC typeC) {
        this.typeC = typeC;
    }

    @Override
    public void charger() {
        typeC.charger();
    }
}
/\* \* 測試調用 \*/
public class AdapterTest {
    public static void main(String[] args) {
        TypeC typeC = new TypeC();
        MicroUSB microUSB = new AdapterMicroUSB(typeC);
        microUSB.charger();

    }
}

程序執行結果:

TypeC 充電

11.JDK 類庫經常使用的設計模式有哪些?

答:JDK 經常使用的設計模式以下:

1)工廠模式

java.text.DateFormat 工具類,它用於格式化一個本地日期或者時間。

public final static DateFormat getDateInstance();
public final static DateFormat getDateInstance(int style);
public final static DateFormat getDateInstance(int style,Locale locale);

加密類

KeyGenerator keyGenerator = KeyGenerator.getInstance("DESede");
Cipher cipher = Cipher.getInstance("DESede");
2)適配器模式

把其餘類適配爲集合類

List<Integer> arrayList = java.util.Arrays.asList(new Integer[]{1,2,3});
List<Integer> arrayList = java.util.Arrays.asList(1,2,3);
3)代理模式

如 JDK 自己的動態代理。

interface Animal {
    void eat();
}
class Dog implements Animal {
    @Override
    public void eat() {
        System.out.println("The dog is eating");
    }
}
class Cat implements Animal {
    @Override
    public void eat() {
        System.out.println("The cat is eating");
    }
}

// JDK 代理類
class AnimalProxy implements InvocationHandler {
    private Object target; // 代理對象
    public Object getInstance(Object target) {
        this.target = target;
        // 取得代理對象
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("調用前");
        Object result = method.invoke(target, args); // 方法調用
        System.out.println("調用後");
        return result;
    }
}

public static void main(String[] args) {
    // JDK 動態代理調用
    AnimalProxy proxy = new AnimalProxy();
    Animal dogProxy = (Animal) proxy.getInstance(new Dog());
    dogProxy.eat();
}
4)單例模式

全局只容許有一個實例,好比:

Runtime.getRuntime();
5)裝飾器

爲一個對象動態的加上一系列的動做,而不須要由於這些動做的不一樣而產生大量的繼承類。

java.io.BufferedInputStream(InputStream);  
java.io.DataInputStream(InputStream);  
java.io.BufferedOutputStream(OutputStream);  
java.util.zip.ZipOutputStream(OutputStream);  
java.util.Collections.checkedList(List list, Class type) ;
6)模板方法模式

定義一個操做中算法的骨架,將一些步驟的執行延遲到其子類中。

好比,Arrays.sort() 方法,它要求對象實現 Comparable 接口。

class Person implements Comparable{
    private Integer age;
    public Person(Integer age){
        this.age = age;
    }
    @Override
    public int compareTo(Object o) {
        Person person = (Person)o;
        return this.age.compareTo(person.age);
    }
}
public class SortTest(){
    public static void main(String[] args){
        Person p1 = new Person(10);
        Person p2 = new Person(5);
        Person p3 = new Person(15);
        Person[] persons = {p1,p2,p3};
        //排序
        Arrays.sort(persons);
    }
}

12.IO 使用了什麼設計模式?

答:IO 使用了適配器模式和裝飾器模式。

  • 適配器模式:因爲 InputStream 是字節流不能享受到字符流讀取字符那麼便捷的功能,藉助 InputStreamReader 將其轉爲 Reader 子類,於是能夠擁有便捷操做文本文件方法;
  • 裝飾器模式:將 InputStream 字節流包裝爲其餘流的過程就是裝飾器模式,好比,包裝爲 FileInputStream、ByteArrayInputStream、PipedInputStream 等。

13.Spring 中都使用了哪些設計模式?

答:Spring 框架使用的設計模式以下。

  • 代理模式:在 AOP 中有使用
  • 單例模式:bean 默認是單例模式
  • 模板方法模式:jdbcTemplate
  • 工廠模式:BeanFactory
  • 觀察者模式:Spring 事件驅動模型就是觀察者模式很經典的一個應用,好比,ContextStartedEvent 就是 ApplicationContext 啓動後觸發的事件
  • 適配器模式:Spring MVC 中也是用到了適配器模式適配 Controller

七、uml

一、什麼是UML?具體包括哪些內容?
答:標準建模語言UML。包括用例圖,靜態圖(包括類圖、對象圖和包圖),行爲圖,交互圖(順序圖和合做圖)和實現圖。

二、Java EE經常使用的設計模式?
答:Java中的23種設計模式包括:Factory(工廠模式),Builder(建造模式),FactoryMethod(工廠方法模式),Prototype(原始模型模式),Singleton(單例模式),Facade(門面模式),Adapter(適配器模式),Bridge(橋樑模式),Composite(合成模式),Decorator(裝飾模式),Flyweight(享元模式),Proxy(代理模式),Command(命令模式),?Interpreter(解釋器模式),Visitor(訪問者模式),Iterator(迭代子模式),Mediator(調停者模式),Memento(備忘錄模式),Observer(觀察者模式),State(狀態模式),Strategy(策略模式),Template Method(模板方法模式), Chain Of Responsibleity(責任鏈模式)

三、說說你是如何理解工廠模式的?
答:工廠模式是一種常常被使用到的模式,根據工廠模式實現的類能夠根據提供的數據生成一組類中某一個類的實例,一般這一組類都擁有一個公共的抽象父類而且實現了相同的方法,可是,這些方法針對不一樣的數據進行了不一樣的操做。
工廠模式的具體實現方法是:首先須要定義一個基類,該類的子類經過不一樣的方法實現了基類中的方法。而後須要定義一個工廠類,工廠類能夠根據條件生成不一樣的子類實例。當獲得子類的實例後,開發人員能夠調用基類中的方法而沒必要考慮到底返回的是哪個子類的實例。

四、肯定模塊的功能和模塊的接口是在軟件設計的那個隊段完成的?
  答:概要設計階段。

UML 統一建模語言

計算機軟件:

軟件是計算機系統中與硬件相互依存的另外一部分, 它包括程序,相關數據及其說明文檔

程序: 是按照實現設計的算法要求執行的指令序列

數據: 是程序能正常操做的信息

文檔: 是程序開發維護和使用有關的各類圖文資料

軟件的生產過程:

軟件的生產過程: 分爲需求分析, 系統分析, 系統設計, 功能設計, 實現, 測試, 運行和維 護等幾個主要階段

項目的開發模型:

瀑布模型: 它將軟件開發過程劃分爲若干個相互區別又彼此關聯的階段,每一個階段的工做都是以上一階段的結果爲依據, 同時爲下一階段的工做提供了前提

適用於: 已明確了客戶需求, 切記是明確客戶需求切需求不會發生改變時

漸增模型:

是由一組有計劃的,循環漸進的, 不斷改進的過程版本組成

演化模型:

對事先不能完整定義需求的軟件項目的開發能夠使用演化模型, 演化模型能夠減小因爲需求不明給項目帶來的風險

螺旋模型:

螺旋模型結合了瀑布模型和演化模型的優勢, 並增長了風險分析, 該模塊將其活動劃分爲**定製計劃, 風險分析, 實施開發和客戶評估**四類, 已採用了循環往復,逐漸完善的方式工做

定製計劃: 肯定軟件開發目標, 選定實施方案, 弄清項目的限制條件

風險分析: 分析所選方案, 考慮如何識別的消除風險

實施開發: 實施軟件開發

客戶評估: 評價軟件的功能和性能, 提出修正建議

智能模型:

基於知識庫和專家的軟件開發模型, 是知識工程的軟件工程在開發模型商相結合的人工智能產物

軟件生存週期:

軟件設計,開發,使用

包括: 需求分析, 概要設計, 詳細設計,實現,組裝測試,確認測試,使用, 維護,更新換代

軟件開發方法:

結構化程序設計方法

模塊化程序設計方法

面向對象程序設計方法

UML的特色:

統一標準

面向對象

可視化,表達能力強

獨立於過程

易掌握, 易用

軟件系統體系結構:

視圖:

用例視圖

邏輯視圖

構件視圖

進程視圖

配置視圖

UML軟件已用例爲中心, 已係統體系結構爲主線, 採用循環, 迭代, 漸增的方式進行開發

UML系統模型與建模:

三大模型圖

用例模型圖: 用例圖

靜態模型圖: 類圖, 對象圖,包圖,構建圖和配置圖

動態模型圖: 活動圖, 順序圖, 狀態圖和合做圖

UML擴展:

UML擴展機制包括三種: 構造型,標記型和約束型

UML開發的特徵:

用例驅動的系統

以體系結構爲中心

螺旋上升式的開發過程

以質量控制和風險管理爲目標

項目的可行性研究與風險分析:

可行性研究分爲:經濟可行性研究,技術可行性研究和法律可行性研究

類之間的關係:

關聯關係

彙集關係

繼承關係

依賴和細化關係

UML設計模式:

分類:

建立型設計模式:工廠模式,建造模式,原型模式,單例模式

工廠模式分爲三種: 簡單工廠, 工廠方法,抽象方法

結構型設計模式:適配器對象模式,橋接模式,組合模式,裝飾模式,外觀模式,享元模式, 代理模式

行爲型審計模式:命令模式,迭代器模式,責任鏈模式,中介者模式,備忘錄模式,觀察者模式,狀態模式,策略模式,訪問者模式

八、基礎類庫 零碎

1.java中有幾種類型的流?JDK爲每種類型的流提供了一些抽象類以供繼承,請說出他們分別是哪些類?

答:字節流,字符流。字節流繼承於Input Stream Output Stream,字符流繼承於Reader Writer。在java.io包中還有許多其餘的流,低層流與調層流,高層流主要是爲了提升性能和使用方便。

二、啓動一個線程是用run()仍是start()?

答:啓動一個線程是調用start()方法,啓動線程並調用run方法。

三、線程的基本概念、線程的基本狀態以及狀態之間的關係是什麼?

答:線程是進程內的併發,沒有自已內存空間,共享進程的,線程間的通訊成本較低。Java中的線程有四種狀態分別是:運行、就緒、掛起、結束。

四、java多線程有幾種實現方法,都是什麼?同步有幾種實現方法,都是什麼? stop()和suspend()方法爲什麼不推薦使用?

答:多線程有兩種實現方法,分別是繼承Thread類與實現Runnable接口。

同步的實現方面有兩種,分別是synchronized,wait與notify。

反對使用stop(),是由於它不安全。它會解除由線程獲取的全部鎖定,並且若是對象處於一種不連貫狀態,那麼其餘線程能在那種狀態下檢查和修改它們。結果很難檢查出真正的問題所在。suspend()方法容易發生死鎖。調用suspend()的時候,目標線程會停下來,但卻仍然持有在這以前得到的鎖定。此時,其餘任何線程都不能訪問鎖定的資源,除非被"掛起"的線程恢復運行。對任何線程來講,若是它們想恢復目標線程,同時又試圖使用任何一個鎖定的資源,就會形成死鎖。因此不該該使用suspend(),而應在本身的Thread類中置入一個標誌,指出線程應該活動仍是掛起。若標誌指出線程應該掛起,便用wait()命其進入等待狀態。若標誌指出線程應當恢復,則用一個notify()從新啓動線程。

五、同步和異步有和異同,在什麼狀況下分別使用他們?舉例說明。

答:同步:上一段代碼沒的完成,下一段必須等到上一段代碼完成後才能夠執行。如買票排隊

異步:上一段代碼沒的完成,下一段沒必要等到上一段代碼完成就能夠執行。如手機發送短信。

六、所知道的線程同步的方法都有什麼?

答:HashTable中的put,get,remove,Vector的相關方法。

七、如何列出某個目錄下的全部文件?

答:代碼以下:

Import java.io.File;
File f=new File("C:\\");
        File[] f1=f.listFiles();
        for(int i=0;i
八、sleep() 和 wait() 有什麼區別?
答:Sleep是指休眠給定的時間,當這個時間達到以後,線程會再次醒來。
Wait是等待狀態,多長時間不清楚,由另外一個線程將其喚醒。
九、當一個線程進入一個對象的一個synchronized方法後,其它線程是否可進入此對象的其它方法?
答:如只其它方法是同步方法,不能夠進入。若是不是能夠進入。
十、Java集合框架是什麼?說出一些集合框架的優勢?
答:每種編程語言中都有集合,最初的Java版本包含幾種集合類:Vector、Stack、HashTable和Array。隨着集合的普遍使用,Java1.2提出了囊括全部集合接口、實現和算法的集合框架。在保證線程安全的狀況下使用泛型和併發集合類,Java已經經歷了好久。它還包括在Java併發包中,阻塞接口以及它們的實現。集合框架的部分優勢以下:
(1)使用核心集合類下降開發成本,而非實現咱們本身的集合類。
(2)隨着使用通過嚴格測試的集合框架類,代碼質量會獲得提升。
(3)經過使用JDK附帶的集合類,能夠下降代碼維護成本。
(4)複用性和可操做性。

九、Java的高級特性

\1. Struts 2體系結構流程中最熱門的6個Struts 2面試問題和答案

img

-> 1.啓動應用程序服務器時,容器將加載web.xml文件。

-> 2.從客戶端瀏覽器向服務器發出帶有特定URL的第一個請求時,控件首先到達web.xml文件。它在web.xml中檢查URL的映射,並找到過濾器分派器。

-> 3.過濾器分派器肯定是否使用動做映射器調用動做,並將控件委派給動做代理。

-> 4. ActionProxy從Configuration Manager得到幫助,Configuration Manager是從struts.xml初始化的。ActionProxy建立一個ActionInvocation。

-> 5. ActionInvocation查找要爲此請求調用的Action類,並發現與動做映射關聯的intereptor。

-> 6.如今,ActionInvocation調用堆棧中第一個攔截器的intercept()方法。執行第一個攔截器後,Invoke()將檢查下一個攔截器。

-> 7.執行完全部攔截器後,將調用動做類。最後,將返回結果字符串,並將呈現相應的視圖。

\2. Struts 1.x和Struts 2.x之間的區別?

img

3.什麼是攔截器?

攔截器是在請求的預處理和後處理中調用的對象。在struts 2中,攔截器用於執行諸如驗證,異常處理,國際化,顯示中間結果等操做。

4.什麼是價值棧和OGNL?

值堆棧是Struts 2 在其中存儲應用程序數據以處理客戶端請求的存儲區域。數據存儲在ActionContext對象中,該對象使用ThreadLocal對特定請求線程具備特定的值。

對象圖導航語言(OGNL)是一種功能強大的表達語言,用於處理存儲在ValueStack上的數據。從體系結構圖中能夠看到,兩個攔截器和結果頁均可以使用OGNL訪問存儲在ValueStack上的數據。

\5. Struts2動做和攔截器是不是線程安全的?

Struts 2 動做類是線程安全的,由於會爲處理該請求的每一個請求實例化一個對象。

Struts 2 攔截器是單例類,而且建立了一個新線程來處理請求,所以它不是線程安全的,咱們須要仔細實現它們,以免共享數據出現任何問題。

6.什麼是動做上下文和動做調用?

ActionContext是在其中執行動做的對象的容器。每一個線程(即ThreadLocal)中存儲在ActionContext中的值是惟一的。所以,咱們不須要使操做線程安全。

咱們能夠經過調用ActionContext類的getContext()方法來獲取ActionContext的引用。這是靜態工廠方法。例如:
ActionContext ctx = ActionContext.getContext();

ActionInvocation表示動做的執行狀態。它包含動做和攔截器對象。

Struts框架提供了ActionInvocation接口來處理ActionInvocation。它提供了許多方法,其中一些方法可用於獲取ValueStack,ActionProxy,ActionContext,Result等的實例。

===============================================================

\1. java.lang之間的差別。StringBuffer和java.lang。StringBuilder?

java.lang.StringBuffer:線程安全的,同步的,而且沒有那麼快。

java.lang.StringBuilder:更快,不執行同步。

2.如何處理線程的未捕獲異常?

@FunctionalInterface
公共靜態接口線程。UncaughtExceptionHandler

\3. [已檢查的例外]與[未檢查的例外]之間的區別?

該類 Exception 和全部不是其子類的子類 RuntimeException 都是 檢查異常。若是檢查的異常能夠由方法或構造函數的throws 執行拋出,並在方法或構造函數的邊界外傳播,則須要在方法或構造函數的子句中聲明 它們。

Checked Exceptions應該用於預期的但沒法預防的錯誤,能夠從中恢復。

4.如何使用非靜態內部類和靜態內部類?

非靜態:Out.In in = new Out()。new In() ;

靜態:Out.StaticIn in = new Out.StaticIn() ;

5.匿名內部階級?

匿名內部類必須擴展超類或實現接口。

ClassA a =新的ClassA();

a.test(新產品(){

  公共雙重處理(){

  }

});

6.如何建立動態代理類和動態代理實例?

java.lang.reflect。代理
接口InvocationHandler

靜態Class <?> getProxyClass(ClassLoader loader,Class <?> ...接口)
靜態對象newProxyInstance(ClassLoader loader,Class <?> []接口,InvocationHandler h)

InvocationHandler handler =新的MyInvocationHandler(...);

類proxyClass = Proxy.getProxyClass(Foo.class.getClassLoader(),新Class [] {Foo.class});
構造函數ctor = proxyClass.getConstructor(new Class [] {InvocationHandler.class});
Foo f =(Foo)ctor.newInstance(new Object [] {handler});

Foo f =(Foo)Proxy.newProxyInstance(Foo.class.getClassLoader(),new Class [] {Foo.class},handler);

7.三種類型的類加載器之間有什麼區別?

公共抽象類ClassLoader擴展Object

loadClass(字符串名稱,布爾值解析)

findClass(字符串名稱)

Bootstrap ClassLoader:加載Java的核心類。(java.exe -Xbootclasspath能夠加載其餘類)。不是java.lang.ClassLoader的子類。

jre / lib / *。jar

jre /類

擴展ClassLoader:jre / lib / ext / *或由java.ext.dirs系統屬性定義。

系統ClassLoader:java -classpath或由java.class.path定義

java.lang.Object
  java.lang.ClassLoader
    java.security.SecureClassLoader
      java.net。URLClassLoader

擴展ClassLoader和System ClassLoader的父級。

8.接口Serializable如何工做?

可序列化類的全部子類型自己都是可序列化的。

在序列化和反序列化期間須要特殊處理的類:

私有無效writeObject(java.io.ObjectOutputStream輸出)拋出IOException
私有無效readObject(java.io.ObjectInputStream輸入)拋出IOException,ClassNotFoundException;
私有void readObjectNoData()拋出ObjectStreamException;

指定將對象寫入流時要使用的替代對象:

ANY-ACCESS-MODIFIER對象writeReplace()拋出ObjectStreamException;

從流中讀取時指定一個替換:

ANY-ACCESS-MODIFIER對象readResolve()拋出ObjectStreamException;

ANY-ACCESS-MODIFIER靜態最終長serialVersionUID = 42L;

\9. Buffer如何工做?

java.nio.Buffer
容量是它包含的元素數量。
limit是不該讀取或寫入的第一個元素的索引。
position是下一個要讀取或寫入的元素的索引。
mark是調用reset方法時將其位置重置到的索引。

0 <=標記<=位置<=極限<=容量

clear()將限制設置爲容量,並將位置設置爲零。

flip()將限制設置爲當前位置,而後將位置設置爲零。

rewind()保持限制不變,並將位置設置爲零。

緩衝區不能安全用於多個併發線程。

b.flip()。position(23).limit(42);

\10. runnable和callable 接口之間的區別?

可調用可具備返回值。

\11. java.util之間的區別。集合和接口Map <K,V>?

集合僅包含項目,而地圖包含鍵值對。

12.接口Set (java.util.HashSet )和 接口List (java.util.ArrayList 之間的區別?

Set 無序且無重複,List 無序且重複。

\13. HashSet,TreeSet和EnumSet之間的區別?

\14. HashTable,HashMap,EnumMap和TreeMap 之間的區別?

對於HashMap,非線程安全,鍵和值能夠爲null。

哈希表的線程安全,鍵和值不能爲null。

轉載於:http://www.javashuo.com/article/p-cwijfzqn-mr.html

十、算法

1)請簡單解釋算法是什麼?

算法是一個定義良好的計算過程,它將一些值做爲輸入併產生相應的輸出值。簡單來講,它是將輸入轉換爲輸出的一系列計算步驟。

2)解釋什麼是快速排序算法?

快速排序算法可以快速排序列表或查詢。它基於分割交換排序的原則,這種類型的算法佔用空間較小,它將待排序列表分爲三個主要部分:

  • 小於Pivot的元素
  • 樞軸元素Pivot(選定的比較值)
  • 大於Pivot的元素

3)解釋算法的時間複雜度?

算法的時間複雜度表示程序運行完成所需的總時間,它一般用大O表示法來表示。

4)請問用於時間複雜度的符號類型是什麼?

用於時間複雜度的符號類型包括:

  • Big Oh:它表示小於或等於目標多項式
  • Big Omega:它表示大於或等於目標多項式
  • Big Theta:它表示與目標多項式相等
  • Little Oh:它表示小於目標多項式
  • Little Omega:它表示大於目標多項式

5)解釋二分法檢索如何工做?

在二分法檢索中,咱們先肯定數組的中間位置,而後將要查找的值與數組中間位置的值進行比較,若小於數組中間值,則要查找的值應位於該中間值以前,依此類推,不斷縮小查找範圍,直至獲得最終結果。

6)解釋是否能夠使用二分法檢索鏈表?

因爲隨機訪問在鏈表中是不可接受的,因此不可能到達O(1)時間的中間元素。所以,對於鏈表來講,二分法檢索是不能夠的(對順序鏈表或排序後的鏈表是能夠用的)。

7)解釋什麼是堆排序?

堆排序能夠當作是選擇排序的改進,它能夠定義爲基於比較的排序算法。它將其輸入劃分爲未排序和排序的區域,經過不斷消除最小元素並將其移動到排序區域來收縮未排序區域。

8)說明什麼是Skip list?

Skip list數據結構化的方法,它容許算法在符號表或字典中搜索、刪除和插入元素。在Skip list中,每一個元素由一個節點表示。搜索函數返回與key相關的值的內容。插入操做將指定的鍵與新值相關聯,刪除操做可刪除指定的鍵。

9)解釋插入排序算法的空間複雜度是多少?

插入排序是一種就地排序算法,這意味着它不須要額外的或僅須要少許的存儲空間。對於插入排序,它只須要將單個列表元素存儲在初始數據的外側,從而使空間複雜度爲O(1)。

10)解釋什麼是「哈希算法」,它們用於什麼?

「哈希算法」是一個哈希函數,它使用任意長度的字符串,並將其減小爲惟一的固定長度字符串。它用於密碼有效性、消息和數據完整性以及許多其餘加密系統。

11)解釋如何查找鏈表是否有循環?

要知道鏈表是否有循環,咱們將採用兩個指針的方法。若是保留兩個指針,而且在處理兩個節點以後增長一個指針,而且在處理每一個節點以後,遇到指針指向同一個節點的狀況,這隻有在鏈表有循環時纔會發生。

12)解釋加密算法的工做原理?

加密是將明文轉換爲稱爲「密文」的密碼格式的過程。要轉換文本,算法使用一系列被稱爲「鍵」的位來進行計算。密鑰越大,建立密文的潛在模式數越多。大多數加密算法使用長度約爲64到128位的固定輸入塊,而有些則使用流方法。

13)列出一些經常使用的加密算法?

一些經常使用的加密算法是:

  • 3-way
  • Blowfish
  • CAST
  • CMEA
  • GOST
  • DES 和Triple DES
  • IDEA
  • LOKI等等

14)解釋一個算法的最佳狀況和最壞狀況之間有什麼區別?

·最佳狀況:算法的最佳狀況解釋爲算法執行最佳的數據排列。例如,咱們進行二分法檢索,若是目標值位於正在搜索的數據中心,則這就是最佳狀況,最佳狀況時間複雜度爲0。

·最差狀況:給定算法的最差輸入參考。例如快速排序,若是選擇關鍵值的子列表的最大或最小元素,則會致使最差狀況出現,這將致使時間複雜度快速退化到O(n2)。

15)解釋什麼是基數排序算法?

基數排序又稱「桶子法」,是經過比較數字將其分配到不一樣的「桶裏」來排序元素的。它是線性排序算法之一。

16)解釋什麼是遞歸算法?

遞歸算法是一個解決複雜問題的方法,將問題分解成較小的子問題,直到分解的足夠小,能夠輕鬆解決問題爲止。一般,它涉及一個調用自身的函數。

17)提到遞歸算法的三個定律是什麼?

全部遞歸算法必須遵循三個規律

  1. 遞歸算法必須有一個基點
  2. 遞歸算法必須有一個趨向基點的狀態變化過程
  3. 遞歸算法必須自我調用

18)解釋什麼是冒泡排序算法?

冒泡排序算法也稱爲下沉排序。在這種類型的排序中,要排序的列表的相鄰元素之間互相比較。若是它們按順序排列錯誤,將交換值並以正確的順序排列,直到最終結果「浮」出水面。

十一、集合

概述:

  • List , Set, Map都是接口,前兩個繼承至Collection接口,Map爲獨立接口
  • Set下有HashSet,LinkedHashSet,TreeSet
  • List下有ArrayList,Vector,LinkedList
  • Map下有Hashtable,LinkedHashMap,HashMap,TreeMap
  • Collection接口下還有個Queue接口,有PriorityQueue類

這裏寫圖片描述

注意:

  • Queue接口與List、Set同一級別,都是繼承了Collection接口。
    看圖你會發現,LinkedList既能夠實現Queue接口,也能夠實現List接口.只不過呢, LinkedList實現了Queue接口。Queue接口窄化了對LinkedList的方法的訪問權限(即在方法中的參數類型若是是Queue時,就徹底只能訪問Queue接口所定義的方法 了,而不能直接訪問 LinkedList的非Queue的方法),以使得只有恰當的方法才能夠使用。
  • SortedSet是個接口,它裏面的(只有TreeSet這一個實現可用)中的元素必定是有序的。

總結:

Connection接口:

List 有序,可重複

  • ArrayList
    優勢: 底層數據結構是數組,查詢快,增刪慢。
    缺點: 線程不安全,效率高
  • Vector
    優勢: 底層數據結構是數組,查詢快,增刪慢。
    缺點: 線程安全,效率低
  • LinkedList
    優勢: 底層數據結構是鏈表,查詢慢,增刪快。
    缺點: 線程不安全,效率高

Set 無序,惟一

  • HashSet
    底層數據結構是哈希表。(無序,惟一)
    如何來保證元素惟一性?
    1.依賴兩個方法:hashCode()和equals()
  • LinkedHashSet
    底層數據結構是鏈表和哈希表。(FIFO插入有序,惟一)
    1.由鏈表保證元素有序
    2.由哈希表保證元素惟一
  • TreeSet
    底層數據結構是紅黑樹。(惟一,有序)
    \1. 如何保證元素排序的呢?
    天然排序
    比較器排序
    2.如何保證元素惟一性的呢?
    根據比較的返回值是不是0來決定

針對Collection集合咱們到底使用誰呢?(掌握)

惟一嗎?

是:Set

排序嗎?

是:TreeSet或LinkedHashSet
否:HashSet
若是你知道是Set,可是不知道是哪一個Set,就用HashSet。

否:List

要安全嗎?

是:Vector
否:ArrayList或者LinkedList

查詢多:ArrayList
增刪多:LinkedList
若是你知道是List,可是不知道是哪一個List,就用ArrayList。

若是你知道是Collection集合,可是不知道使用誰,就用ArrayList。
若是你知道用集合,就用ArrayList。

說完了Collection,來簡單說一下Map.

Map接口:

上圖:
這裏寫圖片描述

Map接口有三個比較重要的實現類,分別是HashMap、TreeMap和HashTable。

  • TreeMap是有序的,HashMap和HashTable是無序的。
  • Hashtable的方法是同步的,HashMap的方法不是同步的。這是二者最主要的區別。

這就意味着:

  • Hashtable是線程安全的,HashMap不是線程安全的。
  • HashMap效率較高,Hashtable效率較低。
    若是對同步性或與遺留代碼的兼容性沒有任何要求,建議使用HashMap。 查看Hashtable的源代碼就能夠發現,除構造函數外,Hashtable的全部 public 方法聲明中都有 synchronized關鍵字,而HashMap的源碼中則沒有。
  • Hashtable不容許null值,HashMap容許null值(key和value都容許)
  • 父類不一樣:Hashtable的父類是Dictionary,HashMap的父類是AbstractMap

重點問題重點分析:

(一).TreeSet, LinkedHashSet and HashSet 的區別

1. 介紹

  • TreeSet, LinkedHashSet and HashSet 在java中都是實現Set的數據結構
  • TreeSet的主要功能用於排序
  • LinkedHashSet的主要功能用於保證FIFO即有序的集合(先進先出)
  • HashSet只是通用的存儲數據的集合

2. 相同點

  • Duplicates elements: 由於三者都實現Set interface,因此三者都不包含duplicate elements
  • Thread safety: 三者都不是線程安全的,若是要使用線程安全能夠Collections.synchronizedSet()

3. 不一樣點

  • Performance and Speed: HashSet插入數據最快,其次LinkHashSet,最慢的是TreeSet由於內部實現排序
  • Ordering: HashSet不保證有序,LinkHashSet保證FIFO即按插入順序排序,TreeSet安裝內部實現排序,也能夠自定義排序規則
  • null:HashSet和LinkHashSet容許存在null數據,可是TreeSet中插入null數據時會報NullPointerException
(1).天然排序

天然排序要進行一下操做:
1.Student類中實現 Comparable接口
2.重寫Comparable接口中的Compareto方法

(2).比較器排序

比較器排序步驟:
1.單首創建一個比較類,這裏以MyComparator爲例,而且要讓其繼承Comparator接口
2.重寫Comparator接口中的Compare方法

3.在主類中使用下面的 構造方法

十二、TCP/IP UDP

特色

安全(三次交互,以保證鏈接的可靠),不會發生數據丟失,可是效率沒有UDP高

何時應該使用TCP?

當對網絡通信質量有要求的時候,好比:整個數據要準確無誤的傳遞給對方,這每每用於一些要求可靠的應用,好比HTTP、HTTPS、FTP等傳輸文件的協議,POP、SMTP等郵件傳輸的協議。

UDP是無鏈接通訊協議,即在數據傳輸時,數據的發送端和接收端不創建邏輯鏈接。

何時應該使用UDP?

當對網絡通信質量要求不高的時候,要求網絡通信速度能儘可能的快,這時就能夠使用UDP。

1三、HTTP

HTTP協議的特色

  • 基於請求/響應模型的協議。
    • 請求和響應必須成對;
    • 先有請求後有響應。
  • 簡單快捷
    • 由於發送請求的時候只須要發送請求方式和請求路徑便可
  • HTTP協議默認的端口:80

1四、集合和數組的區別

集合和數組的區別

區別1:

  • 數組既能夠存儲基本數據類型,又能夠存儲引用數據類型,基本數據類型存儲的是值,引用數據類型存儲的是地址值。
  • 集合只能存儲引用數據類型(對象)。集合也能存儲基本數據類型(有點矛盾,看後句),可是在存儲的時候會自動裝箱變成對象。

區別2:

  • 數組長度是固定的,不能自動增加。
  • 集合的長度是可變的,能夠根據元素的增加而增加。

2、集合和數組何時用

 一、若是元素個數是固定的推薦用數組,效率高。

 二、若是元素個數不是固定的推薦用集合

3、集合和數組何時用的緣由

  部分集合內部是用數組實現(固然還有是哈希,二叉樹等),當每次建立一個集合對象是,默認建立10個長度的數組。當添加到第11個時,它會按照原數組1.5倍建立,在把原來的數組值複製過去,原來10長度的數組將變成垃圾被回收。當添加到16或者更多,之後都是按照原數組1.5倍建立,複製這個過程。因此,當固定長度爲100的時候,你選擇了集合就是低效率,選擇數組就是高效率,由於集合裏面有不少建立,複製,銷燬過程。

1五、數據類型

java的數據類型分兩大類:

基本數據類型

方法的參數爲基本數據類型時,傳遞的是數據值。

基本類型是經過諸如 int a = 5; long b = 6L;的形式來定義的,稱爲自動變量,自動變量存放的是字面值,不是類的實例,它存放在內存的堆棧中,數據大小和生存期必須是肯定的,存取速度比較快,在堆棧中的字面值能夠共享,也就是說咱們定義一個int a = 5;而後又定義了一個int b = 5;這時a與b在內存中指向的是同一個字面常量。

四類八種

四類:整形、浮點型、布爾型、字符型

八種:

  • 整數型 (取值範圍)
    • 字節型byte(1個字節)(-128~127) 短整型short(2個字節)(-32768~32767) 整形int(4個字節)(通常默認)(-21億~21億) 長整型long(8個字節)( )
  • 浮點型
    • float(單精度浮點數) (4個字節) double(雙精度浮點數)(8個字節)(通常默認)
  • 字符型
    • char(字符型)(2個字節)
  • 布爾型
    • boolean(1個字節)(只有兩個結果true或者false)

8種基本類型在java中都有對應的封裝類型,也就是引用類型:
整數類型 Byte、Short、Integer(-128~127)、Long
浮點數類型 Float、Double
字符型 Character
布爾類型 Boolean

在參數傳遞時,基本類型都是傳值,也就是傳遞的都是原變量的值得拷貝,改變這個值不會改變原變量

引用數據類型

方法的參數爲引用數據類型時傳遞的是地址值。

除了基本數據就是引用數據類型

基本數據類型加上[] 就變成引用數據類型

類、接口、數組

引用類型通常是經過new關鍵字來建立,好比Integer num = new Integer(3);它存放在內存的堆中,能夠在運行時動態的分配內存大小,生存期也沒必要事先告訴編譯器,當引用類型變量不被使用時,Java內部的垃圾回收器GC會自動回收走。引用變量中存放的不是變量的內容,而是存放變量內容的地址。

引用類型傳遞的是地址,也就是參數與原變量指向的是同一個地址,因此若是改變參數的值,原變量的值也會改變

1六、棧內存 堆內存

image-20200913144016793

1七、自動裝箱拆箱

DK 1.5 (之後的版本)的新特性自動裝箱和拆箱

\1. 自動裝箱:把基本類型轉換爲包裝類類型
int a =10;
Integer i = new Integer(a);

Integer value = 10;
爲何基本類型就能直接轉化爲Integer ,Integer 不該該是new出來的嗎
內部會自動的 new Integer(10) 自動裝箱

\2. 自動拆箱: 把包裝類型轉換爲基本類型
Integer value2 = new Integer(120);
int a = value2;
對象賦值給基本數據類型,爲何會不報錯,由於內部會調用 value2.intValue() 這種就是自動拆箱

觸類旁通

Double d1 = 9.14 //內部會自動new一個Double 而後傳遞進去

new 出來的東西 每一個都會分配一個內存地址

拆箱:devaning
裝箱:

裝箱拆箱面試題: 考點(Integer內部裝箱的內部實現)

看程序寫結果
1.

Integer value1 = new Integer(97);
Integer value2 = new Integer(97);
System.out.println(value1 == value2);
System.out.println(value.equals(value2)); //這個就是比較值
System.out.println("-------------------");

答案 : false true

\2. 自動裝箱,若是值同樣、地址也同樣

Integer value3 = 127; //自動裝箱
Integer value4 = 127;
System.out.println(value3 == value4);
System.out.println(value3.equals(value4)); //這個也是比較值

答案:true true

`3.

Integer value5 = 128;
Integer value6 = 128;
System.out.println(value5==value6); //false
System.out.println(value5.equals(value6)); //true

答案: false true

總結: 自動裝箱,範圍在 -128 ~ 127 【256個數字 】的地址是同樣的,其餘範圍地址不同
-128 到 127 之間的有個自動裝箱的緩存池 若是不在這個範圍,就會使用new 新建立

1八、Java中的內存分配

棧:stack 存儲局部變量,方法在棧中開闢一塊棧並運行

堆:heap 存儲對象及其成員變量,以及成員方法的內存地址,全部 new 的都在堆中

方法區:其實就是堆中的永久代,Jdk8 改成元空間:存儲 class 文件及方法相關信息

本地方法棧:與操做系統相關

寄存器:與 cpu 相關

1九、SSM框架

一、Spring 在ssm中起什麼做用?

Spring:輕量級框架

做用:Bean工廠,用來管理Bean的生命週期和框架集成。

兩大核心:

①. IOC/DI(控制反轉/依賴注入) :把dao依賴注入到service層,service層反轉給action層,Spring頂層容器爲BeanFactory。

②. AOP:面向切面編程

二、Spring的事務?

編程式事務管理:編程方式管理事務,極大靈活性,難維護。

聲明式事務管理:能夠將業務代碼和事務管理分離,用註解和xml配置來管理事務。

三、IOC 在項目中的做用?

做用:Ioc解決對象之間的依賴問題,把全部Bean的依賴關係經過配置文件或註解關聯起來,下降了耦合度。

四、Spring的配置文件中的內容?

開啓事務註解驅動

事務管理器

開啓註解功能,並配置掃描包

配置數據庫

配置SQL會話工廠,別名,映射文件

不用編寫Dao層的實現類

五、Spring下的註解?

註冊:@Controller @Service @Component

注入:@Autowired @Resource

請求地址:@RequestMapping

返回具體數據類型而非跳轉:@ResponseBody

六、Spring DI 的三種方式?

構造器注入:經過構造方法初始化

<constructor-arg index="0" type="java.lang.String" value="寶馬"></constructor-arg>
1

setter方法注入:經過setter方法初始化

<property name="id" value="1111"></property>
1

接口注入

七、Spring主要使用了什麼模式?

工廠模式:每一個Bean的建立經過方法

單例模式:默認的每一個Bean的做用域都是單例

代理模式:關於Aop的實現經過代理模式

八、IOC,AOP的實現原理?

IOC:經過反射機制生成對象注入

AOP:動態代理

2、SpringMvc面試題

一、SpringMvc 的控制器是否是單例模式,若是是,有什麼問題,怎麼解決?

問題:單例模式,在多線程訪問時有線程安全問題

解決方法:不要用同步,在控制器裏面不能寫字段

二、SpringMvc 中控制器的註解?

@Controller:該註解代表該類扮演控制器的角色

三、@RequestMapping 註解用在類上的做用?

做用:用來映射一個URL到一個類或者一個特定的處理方法上

四、前臺多個參數,這些參數都是一個對象,快速獲得對象?

方法:直接在方法中聲明這個對象,SpringMvc就自動把屬性賦值到這個對象裏面

五、SpringMvc中函數的返回值?

String,ModelAndView,List,Set 等

通常String,Ajax請求,返回一個List集合

六、SpringMvc中的轉發和重定向?

轉發: return:「hello」

重定向 :return:「redirect:hello.jsp」

經過JackSon框架把java裏面對象直接轉換成js可識別的json對象,具體步驟以下:

加入JackSon.jar

在配置文件中配置json的映射

在接受Ajax方法裏面直接返回Object,list等,方法前面須要加上註解@ResponseBody

八、SpringMvc的工做流程圖?

img

九、Struts2 和 SpringMvc的區別?

入口不一樣:

Struts2:filter過濾器

SpringMvc:一個Servlet即前端控制器

開發方式不一樣:

Struts2:基於類開發,傳遞參數經過類的屬性,只能設置爲多例

SpringMvc:基於方法開發(一個url對應一個方法),請求參數傳遞到方法形參,能夠爲單例也能夠爲多例(建議單例)

請求方式不一樣:

Struts2:值棧村塾請求和響應的數據,經過OGNL存取數據

SpringMvc:經過參數解析器將request請求內容解析,給方法形參賦值,將數據和視圖封裝成ModelAndView對象,最後又將ModelAndView中的模型數據經過request域傳輸到頁面,jsp視圖解析器默認使用的是jstl。

3、Mybatis面試題

一、Ibatis和Mybatis?

Ibatis:2010年,apache的Ibatis框架中止更新,並移交給了google團隊,同時改名爲MyBatis。從2010年後Ibatis在沒更新過,完全變成了一個孤兒框架。一個沒人維護的框架註定被mybatis拍在沙灘上。

Mybatis:Ibatis的升級版本。

二、什麼是Mybatis的接口綁定,有什麼好處?

Mybatis實現了DAO接口與xml映射文件的綁定,自動爲咱們生成接口的具體實現,使用起來變得更加省事和方便。

三、什麼狀況用註解,什麼狀況用xml綁定?

註解使用狀況:Sql語句簡單時

xml綁定使用狀況:xml綁定 (@RequestMap用來綁定xml文件)

四、Mybatis在覈心處理類叫什麼?

SqlSession

五、查詢表名和返回實體Bean對象不一致,如何處理?

映射鍵值對便可

<result column="title" property="title" javaType="java.lang.String"/>
1

column:數據庫中表的列名

property:實體Bean中的屬性名

六、Mybatis的好處?

把Sql語句從Java中獨立出來。

封裝了底層的JDBC,API的調用,而且可以將結果集自動轉換成JavaBean對象,簡化了Java數據庫編程的重複工做。

本身編寫Sql語句,更加的靈活。

入參無需用對象封裝(或者map封裝),使用@Param註解

七、Mybatis配置一對多?

<collection property="topicComment" column="id" ofType="com.tmf.bbs.pojo.Comment" select="selectComment" />
1

property:屬性名

column:共同列

ofType:集合中元素的類型

select:要鏈接的查詢

八、Mybatis配置一對一?

<association property="topicType" select="selectType" column="topics_type_id" javaType="com.tmf.bbs.pojo.Type"/>
1

property:屬性名

select:要鏈接的查詢

column:共同列

javaType:集合中元素的類型

9 、${} 和 #{}的區別?

:簡單字符串替換,把{}:簡單字符串替換,把:簡單字符串替換,把{}直接替換成變量的值,不作任何轉換,這種是取值之後再去編譯SQL語句。

#{}:預編譯處理,sql中的#{}替換成?,補全預編譯語句,有效的防止Sql語句注入,這種取值是編譯好SQL語句再取值。

總結:通常用#{}來進行列的代替

十、獲取上一次自動生成的主鍵值?

select last _insert_id()
1

十一、Mybatis如何分頁,分頁原理?

RowBounds對象分頁

在Sql內直接書寫,帶有物理分頁

十二、Mybatis工做原理?

image

原理:

經過SqlSessionFactoryBuilder從mybatis-config.xml配置文件中構建出SqlSessionFactory。

SqlSessionFactory開啓一個SqlSession,經過SqlSession實例得到Mapper對象而且運行Mapper映射的Sql語句。

完成數據庫的CRUD操做和事務提交,關閉SqlSession。

框架整合

Spring

概念

Spring是一個開源的輕量級的應用開發框架,其目的是用於簡化企業級應用程序開發,減小侵入;[h1] (IOC)

Spring的IOC和AOP應用,將組件的耦合度降至最低,即解耦,便於系統的維護和升級;

能夠與第三方框架和技術整合應用,能夠自由選擇技術進行開發。

特色

輕量:Spring 是輕量的,基本的版本大約2MB

控制反轉IOC:Spring經過控制反轉實現了鬆散耦合,爲對象提供所需的依賴,而不須要對象去建立或查找依賴

面向切面的編程(AOP):Spring支持面向切面的編程,而且把應用業務邏輯和系統服務分開

容器:Spring 包含並管理應用中對象的生命週期和配置

MVC****框架:Spring的WEB框架是個精心設計的框架,是Web框架的一個很好的替代品

事務管理:Spring 提供一個持續的事務管理接口,能夠擴展到上至本地事務下至全局事務(JTA[A2]

異常處理:Spring 提供方便的API把具體技術相關的異常(好比由JDBC,Hibernate or JDO[A3] 拋出的)轉化爲一致的unchecked 異常。

組成模塊

微信圖片_20190215201411

核心容器(應用上下文) 模塊 Core Container

Core:核心模塊提供了框架的基本組成部分,包括 IoC (控制反轉) 和DI (依賴注入)功能。

Bean:(注入bean)工廠是工廠模式的一個實現,提供了控制反轉功能,用來把應用的配置和依賴從真正的應用代碼中分離。最經常使用的BeanFactory 實現是XmlBeanFactory 類。

XMLBeanFactory,它根據XML文件中的定義加載beans。該容器從XML 文件讀取配置元數據並用它去建立一個徹底配置的系統或應用。

Context:上下文模塊創建在由核心和 Bean 模塊提供的堅實基礎上,它是訪問定義和配置的任何對象的媒介。ApplicationContext 接口是上下文模塊的重點。

SpEl:表達式語言模塊在運行時提供了查詢和操做一個對象圖的強大的表達式語言。

數據訪問/集成

JDBC:模塊提供了刪除冗餘的 JDBC 相關編碼的 JDBC 抽象層[A4]

ORM[A5] :模塊爲流行的對象關係映射 API,包括 JPA,JDO,Hibernate 和 iBatis,提供了集成層。

OXM:模塊提供了抽象層,它支持對 JAXB,Castor,XMLBeans,JiBX 和 XStream 的對象/XML 映射實現。

JMS:Java消息服務 JMS 模塊包含生產和消費的信息的功能。

Transactions****事務模塊:爲實現特殊接口的類及全部的 POJO 支持編程式和聲明式事務管理。

Web模塊

Web:模塊提供了基本的面向 web 的集成功能,例如多個文件上傳的功能和使用 servlet 監聽器和麪向 web 應用程序的上下文來初始化 IoC 容器。

Web-MVC:模塊包含 Spring 的model模型-view視圖-controller控制器(MVC),實現了 web 應用程序。

Web-Socket:模塊爲 WebSocket-based 提供了支持,並且在 web 應用程序中提供了客戶端和服務器端之間通訊的兩種方式。

Web-Portlet:模塊提供了在 portlet 環境中實現 MVC,而且反映了 Web-Servlet 模塊的功能。

其餘模塊

AOP[A6] :模塊提供了面向切面的編程實現,容許你定義方法攔截器和切入點對代碼進行乾淨地解耦,它實現了應該分離的功能。

Aspects:模塊提供了與 AspectJ 的集成,這是一個功能強大且成熟的面向切面編程(AOP)框架。

Instrumentation:模塊在必定的應用服務器中提供了類 instrumentation 的支持和類加載器的實現。

Messaging:模塊爲 STOMP 提供了支持做爲在應用程序中 WebSocket 子協議的使用。它也支持一個註解編程模型,它是爲了選路和處理來自 WebSocket 客戶端的 STOMP 信息。

test:測試模塊支持對具備 JUnit 或 TestNG 框架的 Spring 組件的測試。

Spring IOC 控制反轉

什麼是IOC 控制反轉[A7]

Ioc—Inversion of Control,即「控制反轉」,不是什麼技術,而是一種設計思想。

原始:當某個角色須要另一個角色協助的時候,在傳統的程序設計過程當中,一般由調用者來建立被調用者的實例。例如:service須要調用dao,就須要在service中建立一個dao對象

Spring:在spring中建立被調用者的工做再也不由調用者來完成,建立被調用者的工做由spring容器來完成,而後用註解注入調用者,所以稱爲控制反轉。

核心原理:配置文件(Bean) + 反射(工廠)+ 容器(map)

做用:它能指導咱們如何設計出鬆耦合、更優良的程序。在傳統應用中,程序都是由咱們在類內部主動建立依賴對象,從而致使類與類之間高耦合,難於測試;如今經過IoC容器,把建立和查找依賴對象的控制權交給了容器,由容器進行注入組合對象,因此對象與對象之間是鬆散耦合,這樣也方便測試,利於功能複用,更重要的是使得程序的整個體系結構變得很是靈活。

總之,IoC很好的體現了面向對象設計法則之一—— 好萊塢法則:「別找咱們,咱們找你」;即由IoC容器幫對象找相應的依賴對象並注入,而不是由對象主動去找。其設計方式是從頂層開始逐步添加細節,而不是之前傳統的底層細節決定頂層。

什麼是DI 依賴注入

DI—Dependency Injection,即「依賴注入」:是組件之間依賴關係由容器在運行期決定,形象的說,即由容器動態的將某個依賴關係注入到組件之中。

做用:依賴注入的目的並不是爲軟件系統帶來更多功能,而是爲了提高組件重用的頻率。其重點是完成自身的業務邏輯,而不須要關心具體的資源來自何處,由誰實現。

方式:在pom問題添加依賴

Spring AOP 切面編程

什麼是AOP 切面編程

AOP—Aspect Oriented Programming,即「面向切面編程」,不是什麼技術,而是一種設計思想。在程序開發中主要用來解決一些系統層面上的問題,好比日誌,事務,權限等。利用AOP能夠將那些與業務無關,卻被業務模塊共同調用的邏輯提取並封裝起來,減小了系統中的重複代碼,下降了模塊間的耦合度,同時提升了系統的可維護性。

基本概念

Aspect 切面:一般是一個類,裏面能夠定義切入點和通知

JointPoint 鏈接點:程序執行過程當中明確的點,通常是方法的調用

Advice 通知:AOP在特定的切入點上執行的加強處理,有before前置, after後置, afterReturning, afterThrowing, around環繞

Pointcut 切入點:就是帶有通知的鏈接點,在程序中主要體現爲書寫切入點表達式

AOP****代理:AOP框架建立的對象,代理就是目標對象的增強。Spring中的AOP代理能夠使用JDK動態代理,也能夠是CGLIB代理,前者基於接口(InvocationHandler),後者基於子類。

織入方式

織入是將切面和到其餘應用類型或對象鏈接或建立一個被通知對象的過程。

織入能夠在編譯時,加載時,或運行時完成。

編譯時織入:須要特殊的java編譯器,如Aspectj

類加載時織入:須要特殊的java編譯器,如Aspectj和AspectWerkz

運行時織入:Spring採用的方式,經過動態代理的方式實現。

Spring Aop的實現

Spring中的AOP代理仍是離不開Spring的IOC容器,代理的生成,管理及其依賴關係都是由IOC容器負責,Spring默認使用JDK動態代理,在須要代理類而不是代理接口的時候,Spring會自動切換爲使用CGLIB代理,不過如今的項目都是面向接口編程,因此JDK動態代理相對來講用的仍是多一些。

實現策略

基於XML的實現

在這種狀況下,切面由常規類以及基於XML的配置實現。

基於註解的切面實現

在這種狀況下(基於@AspectJ的實現),涉及到的切面聲明的風格與帶有java5標註的普通java類一致。

Spring beans

Spring bean是什麼

Spring beans 是那些造成Spring應用的主幹的java對象。它們被Spring IOC容器初始化,裝配,和管理。Spring容器理解成一個大型工廠,Bean就是該工廠的產品,工廠(Spirng容器)裏能生產出來什麼樣的產品(Bean),徹底取決於咱們在配置文件中的配置。

生命週期

在說明前能夠思考一下Servlet的生命週期:實例化,初始init,接收請求service,銷燬destroy;

一、實例化一個Bean--也就是咱們常說的new;

二、按照Spring上下文對實例化的Bean進行配置--也就是IOC注入;

實現一系列Aware接口,主要描述Bean和容器直接關係。

三、若是這個Bean已經實現了BeanNameAware接口,會調用它實現的setBeanName(String)方法,此處傳遞的就是Spring配置文件中Bean的id值

四、若是這個Bean已經實現了BeanFactoryAware接口,會調用它實現的setBeanFactory(setBeanFactory(BeanFactory)傳遞的是Spring工廠自身(能夠用這個方式來獲取其它Bean,只需在Spring配置文件中配置一個普通的Bean就能夠);

五、若是這個Bean已經實現了ApplicationContextAware接口,會調用setApplicationContext(ApplicationContext)方法,傳入Spring上下文(一樣這個方式也能夠實現步驟4的內容,但比4更好,由於ApplicationContext是BeanFactory的子接口,有更多的實現方法);

六、若是這個Bean關聯了BeanPostProcessor接口,將會調用postProcessBeforeInitialization(Object obj, String s)方法,BeanPostProcessor常常被用做是Bean內容的更改,而且因爲這個是在Bean初始化結束時調用那個的方法,也能夠被應用於內存或緩存技術;

七、若是Bean在Spring配置文件中配置了init-method屬性會自動調用其配置的初始化方法。

八、若是這個Bean關聯了BeanPostProcessor接口,將會調用postProcessAfterInitialization(Object obj, String s)方法、;

注:以上工做完成之後就能夠應用這個Bean了,那這個Bean是一個Singleton的,因此通常狀況下咱們調用同一個id的Bean會是在內容地址相同的實例,固然在Spring配置文件中也能夠配置非Singleton****,這裏咱們不作贅述。

九、當Bean再也不須要時,會通過清理階段,若是Bean實現了DisposableBean這個接口,會調用那個其實現的destroy()方法;

十、最後,若是這個Bean的Spring配置中配置了destroy-method屬性,會自動調用其配置的銷燬方法。

以上10步驟能夠做爲面試或者筆試的模板,另外咱們這裏描述的是應用Spring上下文Bean的生命週期,若是應用Spring的工廠也就是BeanFactory的話去掉第5步就Ok了。

做用域

singleton :

bean在每一個Spring ioc 容器中只有一個實例。缺省的Spring bean 的做用域是Singleton。默認的(估計是爲了體現輕量級和性能,並且通常狀況下單例足夠了,若須要其餘方式可自行更改)

prototype

一個bean的定義能夠有多個實例。

request

每次http請求都會建立一個bean,該做用域僅在基於web的Spring ApplicationContext情形下有效。

session

在一個HTTP Session中,一個bean定義對應一個實例。該做用域僅在基於web的Spring ApplicationContext情形下有效。

global-session

在一個全局的HTTP Session中,一個bean定義對應一個實例。該做用域僅在基於web的Spring ApplicationContext情形下有效。

自動裝配

在Spring 容器中把bean組裝到一塊兒,前提是容器須要知道bean的依賴關係,如何經過依賴注入來把它們裝配到一塊兒。所以體現配置好bean的依賴關係,Spring 容器可以自動裝配相互合做的bean,能經過Bean工廠自動處理bean之間的協做。

裝配方式

no:默認的方式是不進行自動裝配,經過顯式設置ref 屬性來進行裝配。

byName:經過參數名 自動裝配,Spring容器在配置文件中發現bean的autowire屬性被設置成byname,以後容器試圖匹配、裝配和該bean的屬性具備相同名字的bean。

byType:經過參數類型自動裝配,Spring容器在配置文件中發現bean的autowire屬性被設置成byType,以後容器試圖匹配、裝配和該bean的屬性具備相同類型的bean。若是有多個bean符合條件,則拋出錯誤。

constructor:這個方式相似於byType, 可是要提供給構造器參數,若是沒有肯定的帶參數的構造器參數類型,將會拋出異常。

autodetect:首先嚐試使用constructor來自動裝配,若是沒法工做,則使用byType方式。

侷限性

重寫:你仍需用 和配置來定義依賴,意味着總要重寫自動裝配。

基本數據類型:你不能自動裝配簡單的屬性,如基本數據類型,String字符串,和類。

模糊特性:自動裝配不如顯式裝配精確,若是有可能,建議使用顯式裝配。

建立及依賴配置

實例化 Bean 的三種方式:

構造器方法建立Bean:調用構造方法建立Bean是最經常使用的一種狀況Spring容器經過new關鍵字調用構造器來建立Bean實例,注:無參使用的是property 標籤,有參使用的是constructor-arg標籤。

靜態工廠方法建立Bean:須要額外提供工廠類,工廠方法是靜態方法

實例工廠方法建立Bean:建立實例化工廠類,先註冊工程而後引用工廠

依賴配置方式

構造方法方式注入:構造器依賴注入經過容器觸發一個類的構造器來實現的,該類有一系列參數,每一個參數表明一個對其餘類的依賴。構造器參數實現強制依賴

set****方法方式的注入:Setter方法注入是容器經過調用無參構造器或無參static工廠 方法實例化bean以後,調用該bean的setter方法,即實現了基於setter的依賴注入。setter****方法實現可選依賴

名稱空間注入:Spring2.5之後提供了一種p名稱空間和C名稱空間的屬性注入. 經過直接寫屬性名的方式注入屬性值。

SpEL****的屬性注入:使用spring 提供的一種表達式方式去賦值,最大的好處在於,它能像EL表達式通常,在裏面進行運算、 邏輯判斷,還能夠調用其它Bean的屬性和方法給當前屬性賦值。

BeanFactory和ApplicationContext比較

相同點

BeanFactory和ApplicationContext,其中ApplicationContext是BeanFactory的子接口。他們均可表明Spring容器,Spring容器是生成Bean實例的工廠,而且管理容器中的Bean。

不一樣點

BeanFactory核心庫是在bean中,實現類是XMLBeanFactory,它根據XML文件中的定義加載beans。該容器從XML 文件讀取配置元數據並用它去建立一個徹底配置的系統或應用。

ApplicationContext核心庫是context中,ApplicationContext一般的實現方式:

FileSystemXmlApplicationContext :此容器從一個XML文件中加載beans的定義,XML Bean 配置文件的全路徑名必須提供給它的構造函數。

ClassPathXmlApplicationContext:此容器也從一個XML文件中加載beans的定義,這裏,你須要正確設置classpath由於這個容器將在classpath裏找bean配置。

WebXmlApplicationContext:此容器加載一個XML文件,此文件定義了一個WEB應用的全部bean。

Application contexts提供一種方法處理文本消息,一個一般的作法是加載文件資源(好比鏡像),它們能夠向註冊爲監聽器的bean發佈事件。另外,在容器或容器內的對象上執行的那些不得不禁bean工廠以程序化方式處理的操做,能夠在Application contexts中以聲明的方式處理。Application contexts實現了MessageSource接口,該接口的實現以可插拔的方式提供獲取本地化消息的方法。

初始化區別

BeanFactory在初始化容器時,並未實例化Bean,直到第一次訪問某個Bean時才實例目標Bean;

ApplicationContext則在初始化應用上下文時就實例化全部單實例的Bean。所以ApplicationContext的初始化時間會比BeanFactory稍長一些,不過稍後的調用則沒有」第一次懲罰」的問題

JavaBean/ Spring bean/ POJO

JavaBean****: JavaBean是遵循了Sun制定的JavaBean 標準的類。其三個特性是:包含默認(無參數)的構造函數,容許經過訪問器(getter和setter方法)來訪問類的成員屬性,實現java.io.Serializable接口。

POJO****: POJO是 Plain Old Java Object(簡單的Java對象)的縮寫。是一種花式的對普通Java對象的稱呼。這個詞主要用來區分簡單、輕量的Java對象和「重量級「的類

Spring bean****: Spring bean 表示受到Spring管理的對象。具體說來,它是被Spring框架容器初始化、配置和管理的對象。

Spring事務管理

編程式事務管理

編程式事務管理:這意味你經過編程的方式管理事務,給你帶來極大的靈活性,可是難維護。

聲明式事務管理

聲明式事務管理:這意味着你能夠將業務代碼和事務管理分離,你只需用註解和XML配置來管理事務。

兩種事務比較

大多數Spring框架的用戶選擇聲明式事務管理,由於它對應用代碼的影響最小,所以更符合一個無侵入的輕量級容器的思想。聲明式事務管理要優於編程式事務管理,雖然比編程式事務管理(這種方式容許你經過代碼控制事務)少了一點靈活性。

事務的傳播特性和隔離級別

propagation_required 須要 若是存在一個事務,則指出當前事情。若是沒有事務則開啓事務

propagation_supports 支持 若是存在一個事務,支持當前事務,若是沒有事務,則非事務的執行

propagation_mandatory 必要的 若是已經存在一個事務,支持當前事務。若是沒有一個活動的事務,則拋出異常

propagation_requires_new 老是開啓一個新的事務,若是一個事務已經存在,則將這個存在的事務掛起

propagation_not_supported 老是非事務的執行,並掛起任何存在的事務

propagation_never 老是非事務的執行,若是存在一個活動事務,則拋出異常

propagation_nested 若是一個活動的事務存在,則運行在一個嵌套的事務中,若是沒有就開啓事務

Spring註解

基於註解的容器配置

相對於XML文件,註解型的配置依賴於經過字節碼元數據裝配組件,而非尖括號的聲明。

開發者經過在相應的類,方法或屬性上使用註解的方式,直接組件類中進行配置,而不是使用xml表述bean的裝配關係。

怎樣開啓註解裝配

開啓配置

<context:component-scan base-package="掃描的包名"></context:component-scan>

註解裝配在默認狀況下是不開啓的,爲了使用註解裝配,咱們必須在Spring配置文件中配置元素。

控制類註解

@Component****()

將該類注入到Spring容器中。

value:指定 bean 的 id。若是不指定 value 屬性,默認 bean 的 id 是當前類的類名。首字母小寫。

衍生註解:@Controller修飾WEB層類; @Service修飾業務層類;@Repository修飾DAO層類。

@scope

註解用來描述類的做用範圍的,默認值singleton;多例的使用prototype。

@PostConstrut

設置spring框架初始化此類實例時調用的初始化方法,標註在此類的初始化方法上。

@PreDestroy

用來設置spring框架銷燬此類實例時調用的銷燬方法,標註在此類的銷燬方法上。

注入屬性

@Value

注入基本數據類型和 String 類型數據的。

@Autowired

自動按照類型注入,找到變量名一致的id對象給注入進去。

@Qualifier(value = "accountDao02")

value:指定bean的id。再按照Bean的id注入,必須和 @Autowired一塊兒使用。

@Resource(name = "accountDao02")

指定找具體的某一個實現,name參數設置要找到類的名稱。

功能性註解

@Configuration

用於指定當前類是一個spring配置類,當建立容器時會從該類上加載註解

@ComponentScan("com.itheima")

用於指定spring在初始化容器時要掃描的包。

@Bean

該註解只能寫在方法上,代表使用此方法建立一個對象,而且放入spring容器。

name:給當前@Bean註解方法建立的對象指定一個名稱(即bean的id)。

@Import

用於導入其餘配置類

@PropertySource

用於加載.properties文件中的配置。@Value結合該註解能夠直接給類的屬性初始化

測試註解

@RunWith(SpringJUnit4ClassRunner.class)

指明運行的測試環境

@ContextConfiguration("classpath:applicationContext.xml")

指明spring框架的加載的配置文件 有applicationContext.xml

@ContextConfiguration(classes = SpringConfiguration.class)

指明spring框架的加載的配置類 純註解

切面

aop:aspectj-autoproxy/開啓AOP註解

@Aspect在切面類中定義切入點方法

@Before 前置通知

@AfterReturning 後置通知

@Around 環繞通知

@AfterThrowing 異常拋出通知

@After 最終通知

Spring 中的設計模式

單例模式

在 spring 的配置文件中設置 bean 默認爲單例模式。

代理模式

jdk 的 java.lang.reflect.Proxy類代理:若目標對象實現了若干接口,IOC, AOP

spring 使用 CGLIB 庫生成目標類的子類:若目標兌現沒有實現任何接口

模板模式

用來解決代碼重複的問題:RestTemplate、 JmsTemplate、 JpaTemplate

前端控制器模式

spring 提供了前端控制器 DispatherServlet 來對請求進行分發

依賴注入

貫穿於 BeanFactory/ApplacationContext 接口的核心理念。

工廠模式

咱們在建立對象時不會對客戶端暴露建立邏輯,而且是經過使用同一個接口來指向新建立的對象。Spring 中使用 beanFactory 來建立對象的實例。

SpringMVC

什麼是SpringMVC

概念

SpringMVC 是一種基於Java實現的MVC設計模型的請求驅動類型的輕量級WEB框架。主要做用是處理全部的HTTP請求和響應。

WebApplicationContext[h8] :WebApplicationContext 繼承了ApplicationContext 並增長了一些WEB應用必備的特有功能,它不一樣於通常的ApplicationContext ,由於它能處理主題,並找到被關聯的servlet。

優勢

一、清晰的角色劃分

二、分工明確,並且擴展點至關靈活

三、命令對象就是一個 POJO,無需繼承框架特定 API,能夠使用命令對象直接做爲業務對象

四、和 Spring 其餘框架無縫集成,是其它 Web 框架所不具有的

五、可適配,經過 HandlerAdapter 能夠支持任意的類做爲處理器

六、可定製性, HandlerMapping、 ViewResolver 等可以很是簡單的定製

七、功能強大的數據驗證、格式化、綁定機制

八、強大的 JSP 標籤庫,使 JSP 編寫更容易

九、RESTful風格的支持、簡單的文件上傳、約定大於配置的契約式編程支持、基於註解的零配置支持

控制流程原理

img

流程解釋

一、客戶端發送request請求,服務器前端控制器DispatcherServlet接受請求響應;DisoatcherServlet對請求URL進行解析,獲得請求資源標識符URI;將請求轉發給HandlerMapping;

二、HandlerMapping處理映射器接受後,將請求映射到對應的處理器來處理請求,將映射結果返回DispatcherServlet

三、DispatcherServlet再請求HandlerAdapter處理器適配器,由適配器制定具體的Handler的去執行

四、Handler處理器是編寫的具體業務控制器,在Spring 當中若是寫一些處理器組件,通常實現Controller 接口;

五、Handler處理器處理的結果返回ModelAndView給適配器,適配器再返回給前端控制器

六、前端控制器接收ModelAndView後請求視圖解析器View Resolver據邏輯視圖名解析成物理視圖名即具體的頁面地址;再生成 View 視圖對象,最後對 View 進行渲染將處理結果經過頁面展現給用戶。

組件模塊

DispatcherServlet****前端控制器

整個流程控制的中心,它就至關於 mvc 模式中的 c,由它調用其它組件處理用戶的請求,dispatcherServlet 的存在下降了組件之間的耦合性。

HandlerMapping****處理器映射器

負責根據用戶請求找到 Handler 即處理器;SpringMVC 提供了不一樣的映射器實現不一樣的映射方式;例如:配置文件方式,實現接口方式,註解方式等。

HandlAdapter****處理器適配器

經過 HandlerAdapter 對處理器進行執行,這是適配器模式的應用,經過擴展適配器能夠對更多類型的處理器進行執行。

Handler****處理器

開發中要編寫的具體業務控制器。由 DispatcherServlet 把用戶請求轉發到 Handler,對具體的用戶請求進行處理。

View Resolver****視圖解析器

首先根據邏輯視圖名解析成物理視圖名即具體的頁面地址,再生成 View 視圖對象。

View****視圖

View 視圖過頁面標籤或頁面模版技術將模型數據經過頁面展現給用戶。

參數類型

請求參數

String 類型:普通的字符串類型;

POJO 類型:要求表單中參數名稱和 POJO 類的屬性名稱保持一致。而且控制器方法的參數類型是 POJO 類型,表單中 name 屬性 必須和pojo的對象屬性名一致;

POJO 類中List

POJO 類中Map<String,Account>

返回值

字符串:通過視圖解析器解析爲 jsp 物理路徑: /WEB-INF/pages/success.jsp;

Void:RequestMapping的取值做爲視圖名稱;

ModelAndView:ModelAndView 是 SpringMVC 爲咱們提供的一個對象,該對象也能夠用做控制器方法的返回值。

forward 轉發:若是用了 formward: 則路徑必須寫成實際視圖 url,不能寫邏輯視圖。

Redirect 重定向:返回一個新的url並跳轉。

響應json:使用@ResponseBody 註解實現將 controller 方法返回對象轉換爲 json 響應給客戶端

經常使用註解

@RequestMapping

做用在方法和類上

path:指定請求路徑的url

method :指定該方法的請求方式

Params :指定限制請求參數的條件

Headers :發送的請求中必須包含的請求頭

@RequestBody

用於獲取請求體內容。直接使用獲得是 key=value&key=value...結構的數據

@PathVaribale

用於綁定 url 中的佔位符。

url支持佔位符是spring3.0以後加入的。是springmvc支持restful風格URL的一個重要標誌。

@RequestHeader

用於獲取請求消息頭,value:提供消息頭名稱

@CookieValue

用於把指定 cookie 名稱的值傳入控制器方法參數,value:指定 cookie 的名稱。

@SessionAttributes

用於屢次執行(屢次請求)控制器方法間的參數共享。(該註解定義在類上)

@ModelAttribute

出如今方法上,表示當前方法會在控制器的方法執行以前,出如今參數上,獲取指定的數據給參數賦值。

Struts2和SpringMVC比較

核心控制器(前端控制器、預處理控制器)

核心控制器的主要用途是處理全部的請求,而後對那些有特殊請求統一進行處理(字符編碼、文件上傳、參數接受、異常處理)

SpringMVC核心控制器是Servlet,SrpingMVC控制器基於方法級別的攔截,處理器設計爲單實例。

struct2是Filter,Struts2控制器基於類級別的攔截,處理器設計爲多實例,並且只能設計爲多實例模式,消耗更多的服務器內存。

控制器實例

SpringMVC會比Struts快一些,SpringMVC是基於方法設計

Sturts是基於對象,每一次請求都有有個action實例,每次請求執行對應的方法便可。

管理方式

大部分的公司的核心框架結構中,就會使用到spring,而springmvc又是spring中一個模塊,因此spring對於springmvc的控制器管理更加方便,並且提供了全註解方式進行管理,各類功能的註解會比較全面,使用簡單。

而使用struct2須要採用xml不少配置參數來管理,方式會比較麻煩。

參數傳遞

springmvc是經過方法和參數進行接收。

Struts2中自身提供多種參數接收,其實都是經過valuestack進行傳遞和賦值的。

學習難度

springmvc學習成本比較簡單,容易上手。

Struts增長了不少新的技術點,好比攔擊器,值棧及OGNL表達式,學習成本較高。

intercept實現機制

springmvc是使用的獨立的AOP方式,是方法級別的攔截。

Struts是有本身的interceptor機制,配置文件量比較大。

數據返回

springmvc處理ajax請求,直接返回數據,方法中使用註解@ResponseBody或者@RestController,自動幫轉換爲json對象。

攔截器與過濾器比較

攔截器

SpringSecurity

SpringBoot[h9]

概念

相對於spring方式開發中須要進行大量的配置文件,並且還須要考慮jar包之間的衝突,所以,人們基於約定優於配置的思想,將一些約定俗稱的的配置信息添加進行配置好,在開發過程只須要進行簡單的起步就完成項目基本框架搭建。SpringBoot不是對Spring功能上的加強,而是提供了一種快速使用Spring的方式。

特色

起步依賴

起步依賴本質上是一個Maven項目對象模型(Project Object Model,POM),定義了對其餘庫的傳遞依賴,這些東西加在一塊兒即支持某項功能。

簡單的說,起步依賴就是將具有某種功能的座標打包到一塊兒,並提供一些默認的功能。

自動配置

Spring Boot的自動配置是一個運行時(更準確地說,是應用程序啓動時【main方法】)的過程,考慮了衆多因素,才決定Spring配置應該用哪一個,不應用哪一個。該過程是Spring自動完成的。

起步原理

@SpringBootApplication

下面有三個重要的註解:

@SpringBootConfiguration

它就是JavaConfig形式的Spring Ioc容器的配置類使用的那個@Configuration,SpringBoot社區推薦使用基於JavaConfig的配置形式,因此,這裏的啓動類標註了@Configuration以後,自己其實也是一個IoC容器的配置類。

@ComponentScan

@ComponentScan的功能其實就是自動掃描並加載符合條件的組件(好比@Component和@Controller, @Service, @Repository等)或者bean定義,最終將這些bean定義加載到IoC容器中。

咱們能夠經過basePackages等屬性來細粒度的定製@ComponentScan自動掃描的範圍,若是不指定,則默認Spring框架實現會從聲明@ComponentScan所在類的package進行掃描當前包及其子包下的文件。

@EnableAutoConfiguration

@EnableAutoConfiguration也是藉助@Import的幫助,將全部符合自動配置條件的bean定義加載到IoC容器,僅此而已!會掃描到spring.factories 文件, 加載application.properties 或者application.yal(yaml)文件

@AutoConfigurationPackage:自動配置包

它實際上是註冊了一個Bean的定義。

new PackageImport(metadata).getPackageName(),它其實返回了當前主程序類的 同級以及子級的包組件。

@Import: 導入自動配置的組件

Import(AutoConfigurationImportSelector.class)註解:

AutoConfigurationImportSelector 繼承了 DeferredImportSelector 繼承了 ImportSelector

ImportSelector有一個方法爲:selectImports。

能夠看到第九行,它實際上是去加載 public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";外部文件。這個外部文件,有不少自動配置的類。以下:

@Import(EnableAutoConfigurationImportSelector.class),藉助EnableAutoConfigurationImportSelector,@EnableAutoConfiguration能夠幫助SpringBoot應用將全部符合條件的@Configuration配置都加載到當前SpringBoot建立並使用的IoC容器。就像一隻「八爪魚」同樣。

SpringFactoriesLoader

藉助於Spring框架原有的一個工具類:SpringFactoriesLoader的支持,@EnableAutoConfiguration能夠智能的自動配置功效才得以大功告成!

SpringFactoriesLoader屬於Spring框架私有的一種擴展方案,其主要功能就是從指定的配置文件META-INF/spring.factories加載配置。@EnableAutoConfiguration藉助於Spring框架原有的一個工具類:SpringFactoriesLoader的支持,才能夠智能的自動配置功效才得以大功告成!

配合@EnableAutoConfiguration使用的話,它更可能是提供一種配置查找的功能支持,即根據@EnableAutoConfiguration的完整類名org.springframework.boot.autoconfigure.EnableAutoConfiguration做爲查找的Key,獲取對應的一組@Configuration類。

@EnableAutoConfiguration自動配置的魔法騎士就變成了:從classpath中搜尋全部的META-INF/spring.factories配置文件,並將其中org.springframework.boot.autoconfigure.EnableutoConfiguration對應的配置項經過反射(Java Refletion)實例化爲對應的標註了@Configuration的JavaConfig形式的IoC容器配置類,而後彙總爲一個並加載到IoC****容器。

執行流程

第一步:使用的是SpringApplication的靜態run方法建立一個SpringApplication對象實例。

個建立好的SpringApplication的實例方法。在SpringApplication實例初始化的時候,它會提早作幾件事情:

根據classpath裏面是否存在某個特徵類

(org.springframework.web.context.ConfigurableWebApplicationContext)來決定是否應該

一個爲Web應用使用的ApplicationContext類型。

使用SpringFactoriesLoader在應用的classpath中查找並加載全部可用的ApplicationContextInitializer。

使用SpringFactoriesLoader在應用的classpath中查找並加載全部可用的ApplicationListener。

推斷並設置main方法的定義類。

第二步:遍歷執行全部經過SpringFactoriesLoader能夠查找到並加載的SpringApplicationRunListener。調用它們的started()方法,告訴這些SpringApplicationRunListener,「嘿,SpringBoot應用要開始執行咯!

第三步:建立並配置當前Spring Boot應用將要使用的Environment(包括配置要使用的PropertySource以及Profile)。

遍歷調用全部SpringApplicationRunListener的environmentPrepared()的方法,告訴他們:「當前SpringBoot應用使用的Environment準備好了咯!」。

第四步:若是SpringApplication的showBanner屬性被設置爲true,則打印banner。

第五步:根據用戶是否明確設置了applicationContextClass類型以及初始化階段的推斷結果,決定該爲當前SpringBoot應用建立什麼類型的ApplicationContext並建立完成,而後根據條件決定是否添加ShutdownHook,決定是否使用自定義的BeanNameGenerator,決定是否使用自定義的ResourceLoader,固然,最重要的,將以前準備好的Environment設置給建立好的ApplicationContext使用。

第六步:ApplicationContext建立好以後,SpringApplication會再次藉助Spring-FactoriesLoader,查找並加載classpath中全部可用的ApplicationContext-Initializer,而後遍歷調用這些ApplicationContextInitializer的initialize(applicationContext)方法來對已經建立好的ApplicationContext進行進一步的處理。

第七步:遍歷調用全部SpringApplicationRunListener的contextPrepared()方法和contextLoaded()方法。將以前經過@EnableAutoConfiguration獲取的全部配置以及其餘形式的IoC容器配置加載到已經準備完畢的ApplicationContext。

第八步:調用ApplicationContext的refresh()方法,完成IoC容器可用的最後一道工序。 查找當前ApplicationContext中是否註冊有CommandLineRunner,若是有,則遍歷執行它們。正常狀況下,遍歷執行SpringApplicationRunListener的finished()方法

SpringCloud

概念

因爲微服務是也是分佈式的,須要一個解決方案來統一管理和配置這些零散的微服務。因而SpringCloud誕生了,就是基於SpringBoot提供的一套微服務解決方案。他巧妙的簡化了分佈式系統基礎設施的開發,提供了快速構建分佈式系統的一些工具,提供了服務註冊與發現,配置中心,全鏈路監控,服務網關,負載均衡,熔斷器等組件,是各個微服務架構落地技術的集合體。

SpringBoot能夠離開SpringCloud獨立使用開發項目,可是SpringCloud離不開SpringBoot,屬於依賴的關係。

和Dubbo對比

Dubbo在服務註冊中心用Zookeeper,服務調用方式RPC(方式) 內部封裝了Netty , 用了TCP協議。

Spring Cloud使用Eureka作爲註冊中心,服務調用方式是基於HTTP協議的restful風格的方式調用。此外,Spring Cloud做爲spring全家桶成員的一組,

還提供了6大核心組件,這個是Dubbo沒法比擬的[h10]

服務發現——****Netflix Eureka:主要作項目的註冊與發現管理,在負載均衡方面採用輪詢算法。

服務調用——****Netflix Feign:實現遠微服務程調用 , 例子: 問答微服務與基礎微服務之間的調用

熔斷器——****Netflix Hystrix:在須要調用的服務之間,開啓了熔斷器,防止出現雪崩。

服務網關[h11] ——****Netflix Zuul:分爲前臺網關和後臺網關,達到好管理微服務的做用.在這個項目中經過服務網關服務之間的創建路由配置,減輕了維護成本,同時採用了身份認證和安全,經過token驗證在Zuul進行過濾實現用戶後臺登陸問題。

分佈式配置—[h12] —****Spring Cloud Config:將各個微服務的配置文件放在git(私服)上面進行統一管理,方便配置的更改。

消息總線 —— Spring Cloud Bus:了實現配置文件更改後,咱們採用rabbitmq消息中間件實現配置的實時更新 , 配合Spring Cloud Config 一塊兒使用。

Eureka 服務發現組件

概念

SpringCloud將它集成在本身的子項目spring-cloud-netflix中,實現SpringCloud的服務發現功能。Eureka包含兩個組件:Eureka Server和Eureka Client

Eureka Server提供服務註冊服務,各個節點(各個微服務)啓動後,會在Eureka Server中進行註冊,這樣EurekaServer中的服務註冊表中將會存儲全部可用服務節點的信息。服務節點的信息能夠在界面中直觀的看到。

Eureka Client是一個java客戶端(微服務),用於簡化與Eureka Server的交互,客戶端同時也就別一個內置的、使用輪詢(round-robin)負載算法的負載均衡器。

服務發現心跳包:在應用啓動後,將會向Eureka Server發送心跳,默認週期爲30秒,若是Eureka Server在多個心跳週期內沒有接收到某個節點的心跳,Eureka Server將會從服務註冊表中把這個服務節點移除(默認90秒)

保護模式

因爲會出現因網絡故障緣由致使心跳超過90s,微服務自己實際上是健康的,因此爲了不這樣的誤刪因此有個保護模式的機制。

它的架構哲學是寧肯同時保留全部微服務(健康的微服務和不健康的微服務都會保留),也不盲目註銷任何健康的微服務。使用自我保護模式,可讓Eureka集羣更加的健壯、穩定.

在Spring Cloud中,能夠使用eureka.server.enable-self-preservation = false 禁用自我保護模式。 (不建議使用)

配置過程

服務端(註冊中心)開發

在父工程pom.xml定義SpringCloud版本;

建立子工程tensquare_eureka添加依賴

子工程tensquare_eureka添加application.yml

server:

port: 6868 端口

eureka:

client:

register-with-eureka: false #是否將本身註冊到Eureka服務中,自己就是全部無需註冊

fetch-registry: false #是否從Eureka中獲取註冊信息

serviceUrl: #Eureka客戶端與Eureka服務端進行交互的地址格式

defaultZone: http://127.0.0.1😒{server.port}/eureka/

編寫啓動類 ,建立包com.tensquare.eureka ,包下創建類 , 添加@EnableEurekaServer 開啓eureka服務

服務註冊(配置客戶端****)

在每一個須要註冊的微服務(客戶端)添加依賴

修改每一個微服務的application.yml,添加註冊eureka服務的配置

eureka:

client:

serviceUrl: #Eureka客戶端與Eureka服務端進行交互的地址

defaultZone: http://127.0.0.1:6868/eureka/

instance:

prefer-ip-address: true #把本身的ip地址報告給Eureka(遠程須要,本地測試沒有問題)

修改每一個服務類的啓動類,添加註解

@EnableEurekaClient

啓動測試:將每一個微服務啓動起來,會發現eureka的註冊列表中能夠看到這些微服務了

Feign實現服務間的調用

微服務調用範式

在1號模塊添加依賴,在項目包下建立接口LabelClient

@FeignClient註解用於指定從哪一個服務(依賴的服務)中調用功能 ,名稱必須同樣

@RequestMapping註解用於對被調用的微服務進行地址映射;

@PathVariable註解必定要指定參數名稱

在須要的類中

注入該接口進行調用,

此時啓動類添加兩個註解

@EnableDiscoveryClient

@EnableFeignClients

負載均衡

策略

IRule是一個接口用來定義負載均衡的, SpringCloud爲咱們提供了7種負載均衡算法

RoudRobinRule: 輪詢(默認的)

RandomRule: 隨機

過濾掉故障的,對身剩下的進行輪詢

根據平均響應時間就算全部服務的權重,響應時間越快服務權重越大被選中 的機率越高.

還有幾種獲取服務失敗則後處理的優化配置,用的不對,不記得了

AvailabilityFilteringRule: 會先過濾掉因爲屢次訪問故障而處於斷路器跳閘狀態的服務,還有併發的鏈接數超過閾值的服務,而後對剩餘的服務列表按照輪詢策略進行訪問

WeightResponseTimeRule: 根據平均響應時間就算全部服務的權重,響應時間越快服務權重越大被選中 的機率越高.剛啓動時若是統計信息不足,則使用RoudRobinRule策略,等統計信息足夠,會切換到WeightedResponseTimeRule

RetryRule: 先按照RoundRobinRule的策略獲取服務,若是獲取服務失敗則在指定時間內會進行重試,獲取可用的服務

BestAvailableRule: 會過濾掉因爲屢次訪問故障而處於斷路器跳閘狀態的服務,而後選擇一個併發量最小的服務

ZoneAvoidanceRule: 複合判斷server所在區域的性能和server的可用性選擇服務器

配置方式

在啓動類中注入IRule類,須要什麼負載均衡類型就返回對應的名稱便可。

Hystrix 熔斷器

背景介紹

對於高流量的應用來講,單一的後端依賴可能會致使全部服務器上的全部資源都在幾秒鐘內飽和。比失敗更糟糕的是,這些應用程序還可能致使服務之間的延遲增長,備份隊列,線程和其餘系統資源緊張,致使整個系統發生更多的級聯故障。這些都表示須要對故障和延遲進行隔離和管理,以便單個依賴關係的失敗,不能取消整個應用程序或系統。

解決方案

爲了防止雪崩效應而產生的解決方案。

處理原理

Hystrix 能使你的系統在出現依賴服務失效的時候,經過隔離系統所依賴的服務,防止服務級聯失敗,同時提供失敗回退機制,更優雅地應對失效,並使你的系統能更快地從異常中恢復。

處理過程

「斷路器」自己是一種開關裝置,當某個服務單元發生故障以後,經過斷路器的故障監控(相似熔斷保險絲),向調用方返回一個符合預期的、可處理的備選響應(FallBack),而不是長時間的等待或者拋出調用方沒法處理的異常,這樣就保證了服務調用方的線程不會被長時間、沒必要要地佔用,從而避免了故障在分佈式系統中的蔓延,乃至雪崩。

配置過程

Feign 自己支持Hystrix,不須要額外引入依賴。

在須要依賴的地方配置application.yml ,開啓hystrix

設置處理機制

其實須要繼承這個類 HystrixCommand

設置參數:

//至少有10個請求,熔斷器才進行錯誤率的計算;

//熔斷器中斷請求5秒後會進入半打開狀態,放部分流量過去重試;

//錯誤率達到50開啓熔斷保護

執行命令的幾種方法

Hystrix提供了4種執行命令的方法,execute()和queue() 適用於HystrixCommand對象,而observe()和toObservable()適用於HystrixObservableCommand對象。

execute()

以同步堵塞方式執行run(),只支持接收一個值對象。hystrix會從線程池中取一個線程來執行run(),並等待返回值。

queue()

以異步非阻塞方式執行run(),只支持接收一個值對象。調用queue()就直接返回一個Future對象。可經過 Future.get()拿到run()的返回結果,但Future.get()是阻塞執行的。若執行成功,Future.get()返回單個返回值。當執行失敗時,若是沒有重寫fallback,Future.get()拋出異常。

observe()

事件註冊前執行run()/construct(),支持接收多個值對象,取決於發射源。調用observe()會返回一個hot Observable,也就是說,調用observe()自動觸發執行run()/construct(),不管是否存在訂閱者。

若是繼承的是HystrixCommand,hystrix會從線程池中取一個線程以非阻塞方式執行run();若是繼承的是HystrixObservableCommand,將以調用線程阻塞執行construct()。

toObservable()

事件註冊後執行run()/construct(),支持接收多個值對象,取決於發射源。調用toObservable()會返回一個cold Observable,也就是說,調用toObservable()不會當即觸發執行run()/construct(),必須有訂閱者訂閱Observable時纔會執行。

若是繼承的是HystrixCommand,hystrix會從線程池中取一個線程以非阻塞方式執行run(),調用線程沒必要等待run();若是繼承的是HystrixObservableCommand,將以調用線程堵塞執行construct(),調用線程需等待construct()執行完才能繼續往下走。

返回方法

建立熔斷實現類,實現自接口LabelClient ,並注入到spring容器

return new Result(false, StatusCode.ERROR,"熔斷器啓動了");

修改調用接口註解配置

@FeignClient中添加fallback = 實現類.class

Zuul 微服務網關

背景介紹

在微服務多的狀況下,相互之間的請求會出現複雜,並且出現跨域、認證複雜,服務之間相互調時重構問題。

上述問題,均可以藉助微服務網關解決。微服務網關是介於客戶端和服務器端之間的中間層,全部的外部請求都會先通過微服務網關。

Zuul組件的核心是一系列的過濾器,這些過濾器能夠完成如下功能:身份認證和安全、動態路由、壓力測試、負載分配

配置過程

創建網關微服務

建立子模塊,pom.xml引入依賴zuul ,須要一個獨立的模塊

建立application.yml

tensquare-article: #文章(名字隨便取)

path: /article/** #配置路由規則

serviceId: tensquare-article #指定Eureka註冊中心中的服務id

配置端口,注入到註冊中心,配置須要經過zuul的微服務信息

微服務名字+配置路由規則+在Eureka註冊中心中的服務的id

編寫啓動類

@EnableZuulProxy

@EnableEurekaClient

@SpringBootApplication

請求路徑:http://localhost:9012/base/label 網關的端口+配置路由規則

Zuul過濾器

建立WebFilter過濾器並繼承ZuulFilter類

可重寫裏面方法:

filterType:控制過濾器的調用的位置,前、後、路由請求、發送錯誤

filterOrder:經過int值來定義過濾器的執行順序 越小優先級越高。優先級爲0,數字越大,優先級越低

shouldFilter:執行該過濾器,此處爲true,說明須要過濾

Run:過濾器的具體邏輯

運用

一、後臺管理

管理後臺使用,因此須要在過濾器中對token進行驗證。 驗證不經過,禁止訪問。

二、Token丟失的坑

通過網關的時候,header會丟失致使token丟失. 咱們如今在過濾器中接收header,轉發給微服務。

public Object run() throws ZuulException {

​ System.out.println("zuul過濾器...");

​ //向header中添加鑑權令牌

​ RequestContext requestContext = RequestContext.getCurrentContext();

​ //獲取header

​ HttpServletRequest request = requestContext.getRequest();

​ String authorization = request.getHeader("Authorization");

​ if (authorization != null) {

​ requestContext.addZuulRequestHeader("Authorization", authorization);

​ }

​ return null;

}

SpringCloudConfig 集中配置組件(分佈式配置)

背景

爲了方便服務配置文件統一管理,實時更新。支持放在遠程Git倉庫中。在spring cloud config 組件中,在spring cloud config 組件中,分兩個角色,一是config server,二是config client。

Config Server是一個可橫向擴展、集中式的配置服務器,它用於集中管理應用程序各個環境下的配置,默認使用Git存儲配置文件內容,也能夠使用SVN存儲,或者是本地文件存儲。

Config Client是Config Server的客戶端,用於操做存儲在Config Server中的配置內容。微服務在啓動時會請求Config Server獲取配置文件的內容,請求到後再啓動容器。

配置方式

配置中心微服務

一、在git上創建項目,上傳配置文件

二、建立工程模塊 配置中心微服務 tensquare_config

三、配置中心微服務 tensquare_config ,pom.xml引入依賴

四、複製對應項目git地址 ,放到編寫配置文件application.yml

六、啓動項目,添加註解@EnableConfigServer

配置客戶端

一、客戶端添加依賴spring-cloud-starter-config

二、bootstrap.yml 先於 application.yml 加載,bootstrap.yml用來程序引導時執行,記錄配置中心的請求路徑和統一管理的配置名稱

SpringCloudBus 消息總線組件

概念

修改服務器中的配置並無更新馬上到工程,只有從新啓動程序纔會讀取配置。 那咱們若是想在不重啓微服務的狀況下更新配置如何來實現呢? 咱們使用SpringCloudBus來實現配置的自動更新。目前惟一的實現是使用AMQP代理做爲傳輸。

內部依賴消息隊列,rabbitmq

Activemq 用jms協議

Rabbitmq 用AMQP協議

配置方式

配置服務端(配置中心****tensquare_config)

在配置工程項目中添加:spring-cloud-bus、spring-cloud-stream-binder-rabbit

修改application.yml ,添加配置:

一、添加rabbitmq的ip,放在Spring的容器下

二、暴露觸發消息總線的地址,bus-refresh

配置客戶端(微服務****base)

改完後須要通知微服務

引入依賴:spring-cloud-bus、spring-cloud-stream-binder-rabbit、spring-boot-starter-actuator

在碼雲的配置文件:配置rabbitMQ的地址,和服務端相同

增長自定義配置 sms:ip

在Controller層中經過

@Value("${sms.ip}"):讀取配置文件中ip屬性

@RefreshScope:在Controller添加,此註解用於刷新配置

@RequestMapping(value = "/ip", method = RequestMethod.GET)讀取配置文件信息

SpringData

概念

SpringData是 Spring 基於 ORM 框架、JPA 規範的基礎上封裝的一套JPA應用框架,解脫了DAO層的操做,基本上全部CRUD均可以依賴於它來實現,切換不一樣的ORM框架時提供了極大的方便,同時也使數據庫層操做更加簡單,方便解耦。

JPA規範本質上就是一種ORM規範,注意不是ORM框架——由於JPA並未提供ORM實現,它只是制訂了一些規範,提供了一些編程的API接口,但具體實現則由服務廠商來提供實現,其中Hibernte是服務廠商之一。

基本使用

接口定義進行CRUD操做

建立一個Dao層接口,並實現JpaRepository和JpaSpecificationExecutor。

@Query

經過配置nativeQuery可設置查詢方式:True的時候sql查詢、False的是jpql[h13] 查詢。實現多表關聯查詢

命名規則查詢

查詢方法以findBy開頭,條件的屬性用條件關鍵字鏈接,條件屬性首字母需大寫。

Specifications動態查詢[h14]

有時候咱們在查詢某個實體的時候,給定的條件是不固定的,這時就須要動態構建相應的查詢語句,相比JPQL,其優點是類型安全,更加的面向對象。

Spring Data 常見註解

建立映射關係

@Entity 聲明實體類

@Table(name="cst_customer") 創建實體類和表的映射關係

@Id 聲明當前私有屬性爲主鍵

@GeneratedValue(strategy=GenerationType.IDENTITY) 配置主鍵的生成策略

IDENTITY:主鍵由數據庫自動生成(主要是自動增加型),其中mysql數據庫能夠支持自增加

SEQUENCE:根據底層數據庫的序列來生成主鍵,條件是數據庫支持序列。其中oracle數據庫支持序列

AUTO:主鍵由程序控制(默認採用sequence的形式維護)

TABLE:使用一個特定的數據庫表格來保存主鍵

@Column(name="cust_id") 指定和表中cust_id字段的映射關係

sql查詢註解

@Query 註解的使用很是簡單,只需在方法上面標註該註解,同時提供一個JPQL查詢語句便可

@Modifying 來將該操做標識爲修改[A15] 查詢

關係映射

@OneToMany(targetEntity = LinkMan.class) 一的一方

@JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id") 關係映射

@ManyToOne(targetEntity = Customer.class) 多的一方

@ManyToMany(targetEntity = Role.class) 多對多關係

@JoinTable 多對多的中間表

事務管理

@Transactional 配置事務

@Rollback(false) 不自動回滾

和mybatis對比

相同點

都是java中orm框架,屏蔽了jdbc的api底層訪問細節,可直接完成數據庫的持久化操做。

不一樣點

Hibernate要比mybatis功能強大不少,由於hibernate自動生成sql語句

Hibernate沒法直接寫控制sql語句,對於特定、高效、複雜的的sql語言難以適應了

mybatis是在xml中配置sql語言,支持sql語句自動化

mybatis要比hibernate簡單,是面向sql的,不用考慮對象見一些複雜的映射關係。


減小入侵?這是什麼鬼呢?

涉及到一個設計方面的概念, 也就是耦合性的問題

拓展性強的設計的標準是」高內聚, 低耦合」, 侵入式強指的是耦合太強了, 判斷的標準就是當引入了這個組件致使其餘代碼或者設計要作相應的更改以適應新組件. 這樣的狀況咱們就認爲這個新組件具備入侵性.

Java Transaction API (Java 事務API)

Java data object

\1. 註冊驅動

\2. 獲取鏈接

\3. 建立sql 執行平臺

\4. 執行sql 語句

\5. 處理結果

\6. 釋放資源

全ORM 框架 : Hibernate

半ORM 框架 : Mybatis

切入點:

通知:

切面:切入點和通知

加強:

自我理解:

有時間瞭解下這個玩意吧

SpringBoot****經常使用註解

核心組件的原理和配置方法

Token丟失的坑

最新的方式:好像修改yml的配置便可,可自上網查看

如何部署在本地局域網服務器

語言要熟悉下

準備幾個常見的額類,和大概的流程

相關文章
相關標籤/搜索