Java八股文大全
一、Java基礎(chǔ)篇
1.接口和抽象類的區(qū)別
(1)抽象類可以有構(gòu)造方法,而接口沒有
(2)抽象類可以有抽象方法和具體方法,接口只能有抽象方法
(3)抽象類的成員4種權(quán)限修飾符都可以修飾,接口只能用private
2.重載和重寫的區(qū)別
重載發(fā)生在同一個類中,方法名相同、參數(shù)列表、返回類型、權(quán)限修飾符可以不同
重寫發(fā)生在子類中,方法名相、參數(shù)列表、返回類型都相同,權(quán)限修飾符要大于父類方法,聲明異常范圍要小于父類方法,但是final和private修飾的方法不可重寫
3.==和equals的區(qū)別
==比較基本類型,比較的是值,==比較引用類型,比較的是內(nèi)存地址
equlas是Object類的方法,本質(zhì)上與==一樣,但是有些類重寫了equals方法,比如String的equals被重寫后,比較的是內(nèi)存地址,另外重寫了equlas后,也必須重寫hashcode()方法
4.異常處理機制
(1)使用try、catch、finaly捕獲異常,finaly中的代碼一定會執(zhí)行,捕獲異常后程序會繼續(xù)執(zhí)行
(2)使用throws聲明該方法可能會拋出的異常類型,出現(xiàn)異常后,程序終止
5.HashMap原理
1.HashMap在Jdk1.8以后是基于數(shù)組+鏈表+紅黑樹來實現(xiàn)的,特點是,key不能重復(fù),可以為null,線程不安全
2.HashMap的擴容機制:
HashMap的默認容量為16,默認的負載因子為0.75,當(dāng)HashMap中元素個數(shù)超過容量乘以負載因子的個數(shù)時,就創(chuàng)建一個大小為前一次兩倍的新數(shù)組,再將原來數(shù)組中的數(shù)據(jù)復(fù)制到新數(shù)組中。當(dāng)數(shù)組長度到達64且鏈表長度大于8時,鏈表轉(zhuǎn)為紅黑樹
3.HashMap存取原理:
(1)計算key的hash值,然后進行二次hash,根據(jù)二次hash結(jié)果找到對應(yīng)的索引位置
(2)如果這個位置有值,先進性equals比較,若結(jié)果為true則取代該元素,若結(jié)果為false,就使用高低位平移法將節(jié)點插入鏈表(JDK8以前使用頭插法,但是頭插法在并發(fā)擴容時可能會造成環(huán)形鏈表或數(shù)據(jù)丟失,而高低位平移發(fā)會發(fā)生數(shù)據(jù)覆蓋的情況)
6.想要線程安全的HashMap怎么辦?
(1)使用ConcurrentHashMap
(2)使用HashTable
(3)Collections.synchronizedHashMap()方法
7.ConcurrentHashMap原如何保證的線程安全?
JDK1.7:使用分段鎖,將一個Map分為了16個段,每個段都是一個小的hashmap,每次操作只對其中一個段加鎖
JDK1.8:采用CAS+Synchronized保證線程安全,每次插入數(shù)據(jù)時判斷在當(dāng)前數(shù)組下標(biāo)是否是第一次插入,是就通過CAS方式插入,然后判斷f.hash是否=-1,是的話就說明其他線程正在進行擴容,當(dāng)前線程也會參與擴容;刪除方法用了synchronized修飾,保證并發(fā)下移除元素安全
8.HashTable與HashMap的區(qū)別
(1)HashTable的每個方法都用synchronized修飾,因此是線程安全的,但同時讀寫效率很低
(2)HashTable的Key不允許為null
(3)HashTable只對key進行一次hash,HashMap進行了兩次Hash
(4)HashTable底層使用的數(shù)組加鏈表
9.ArrayList和LinkedList的區(qū)別
ArratList的底層使用動態(tài)數(shù)組,默認容量為10,當(dāng)元素數(shù)量到達容量時,生成一個新的數(shù)組,大小為前一次的1.5倍,然后將原來的數(shù)組copy過來;
因為數(shù)組有索引,所以ArrayList查找數(shù)據(jù)更快,但是添加數(shù)據(jù)效率更低
LinkedList的底層使用鏈表,在內(nèi)存中是離散的,沒有擴容機制;LinkedList在查找數(shù)據(jù)時需要從頭遍歷,所以查找慢,但是添加數(shù)據(jù)效率更高
10.如何保證ArrayList的線程安全?
(1)使用collentions.synchronizedList()方法為ArrayList加鎖
(2)使用Vector,Vector底層與Arraylist相同,但是每個方法都由synchronized修飾,速度很慢
(3)使用juc下的CopyOnWriterArrayList,該類實現(xiàn)了讀操作不加鎖,寫操作時為list創(chuàng)建一個副本,期間其它線程讀取的都是原本list,寫操作都在副本中進行,寫入完成后,再將指針指向副本。
11.String、StringBuffer、StringBuilder的區(qū)別
String 由 char[] 數(shù)組構(gòu)成,使用了 final 修飾,對 String 進行改變時每次都會新生成一個 String 對象,然后把指針指向新的引用對象。
StringBuffer可變并且線程安全;
StringBuiler不可變并且線程不安全。
操作少量字符數(shù)據(jù)用 String;單線程操作大量數(shù)據(jù)用 StringBuilder;多線程操作大量數(shù)據(jù)用 StringBuffer
二.Java多線程篇
1.進程和線程的區(qū)別
進程:系統(tǒng)運行的基本單位,包含多個線程
線程:獨立運行的最小單位,是進程的實體,多個線程共享同一進程內(nèi)的系統(tǒng)資源
2. 什么是線程上下文切換
當(dāng)一個線程被剝奪cpu使用權(quán)時,切換到另外一個線程執(zhí)行
3.什么是死鎖
死鎖指多個線程在執(zhí)行過程中,因爭奪資源造成的一種相互等待的僵局
4.死鎖的必要條件
互斥條件:同一資源同時只能由一個線程讀取
不可搶占條件:不能強行剝奪線程占有的資源
請求和保持條件:請求其他資源的同時對自己手中的資源保持不放
循環(huán)等待條件:在相互等待資源的過程中,形成一個閉環(huán)
想要預(yù)防死鎖,只需要破壞其中一個條件即可,銀行家算法可以預(yù)防死鎖
5.Synchrpnized和lock的區(qū)別
(1)synchronized是關(guān)鍵字,lock是一個類
(2) synchronized在發(fā)生異常時會自動釋放鎖,lock需要手動釋放鎖
(3)synchronized是可重入鎖、非公平鎖、不可中斷鎖,lock是可重入鎖,可中斷鎖,可以是公平鎖
6.sleep()和wait()的區(qū)別
(1)wait()是Object的方法,sleep()是Thread類的方法
(2)wait()會釋放鎖,sleep()不會釋放鎖
(3)wait()要在同步方法或者同步代碼塊中執(zhí)行,sleep()沒有限制
(4)wait()要調(diào)用notify()或notifyall()喚醒,sleep()自動喚醒
7.yield()和join()區(qū)別
yield()調(diào)用后線程進入就緒狀態(tài)
A線程中調(diào)用B線程的join() ,則B執(zhí)行完前A進入阻塞狀態(tài)
8.線程池七大參數(shù)
核心線程數(shù):線程池中的基本線程數(shù)量
最大線程數(shù):當(dāng)阻塞隊列滿了之后,逐一啟動
最大線程的存活時間:當(dāng)阻塞隊列的任務(wù)執(zhí)行完后,最大線長的回收時間
最大線程的存活時間單位
阻塞隊列:當(dāng)核心線程滿后,后面來的任務(wù)都進入阻塞隊列
線程工廠:用于生產(chǎn)線程
任務(wù)拒絕策略:阻塞隊列滿后,拒絕任務(wù),有四種策略(1)拋異常(2)丟棄任務(wù)不拋異常(3)打回任務(wù)(4)嘗試與最老的線程競爭
9.Java內(nèi)存模型
JMM(Java內(nèi)存模型 )屏蔽了各種硬件和操作系統(tǒng)的內(nèi)存訪問差異,實現(xiàn)讓Java程序在各平臺下都能達到一致的內(nèi)存訪問效果,它定義了JVM如何將程序中的變量在主存中讀取
具體定義為:所有變量都存在主存中,主存是線程共享區(qū)域;每個線程都有自己獨有的工作內(nèi)存,線程想要操作變量必須從主從中copy變量到自己的工作區(qū),每個線程的工作內(nèi)存是相互隔離的
由于主存與工作內(nèi)存之間有讀寫延遲,且讀寫不是原子性操作,所以會有線程安全問題
10.保證并發(fā)安全的三大特性?
原子性:一次或多次操作在執(zhí)行期間不被其他線程影響
可見性:當(dāng)一個線程在工作內(nèi)存修改了變量,其他線程能立刻知道
有序性:JVM對指令的優(yōu)化會讓指令執(zhí)行順序改變,有序性是禁止指令重排
11.volatile
保證變量的可見性和有序性,不保證原子性。使用了 volatile 修飾變量后,在變量修改后會立即同步到主存中,每次用這個變量前會從主存刷新。
單例模式雙重校驗鎖變量為什么使用 volatile 修飾? 禁止 JVM 指令重排序,new Object()分為三個步驟:申請內(nèi)存空間,將內(nèi)存空間引用賦值給變量,變量初始化。如果不禁止重排序,有可能得到一個未經(jīng)初始化的變量。
12.線程使用方式
(1)繼承 Tread 類
(2)實現(xiàn) Runnable 接口
(3)實現(xiàn) Callable 接口:帶有返回值
13.ThreadLocal原理
原理是為每個線程創(chuàng)建變量副本,不同線程之間不可見,保證線程安全。每個線程內(nèi)部都維護了一個Map,key為threadLocal實例,value為要保存的副本。
但是使用ThreadLocal會存在內(nèi)存泄露問題,因為key為弱引用,而value為強引用,每次gc時key都會回收,而value不會被回收。所以為了解決內(nèi)存泄漏問題,可以在每次使用完后刪除value或者使用static修飾ThreadLocal,可以隨時獲取value
14.什么是CAS鎖
CAS鎖可以保證原子性,思想是更新內(nèi)存時會判斷內(nèi)存值是否被別人修改過,如果沒有就直接更新。如果被修改,就重新獲取值,直到更新完成為止。這樣的缺點是
(1)只能支持一個變量的原子操作,不能保證整個代碼塊的原子操作
(2)CAS頻繁失敗導(dǎo)致CPU開銷大
(3)ABS問題:線程1和線程2同時去修改一個變量,將值從A改為B,但線程1突然阻塞,此時線程2將A改為B,然后線程3又將B改成A,此時線程1將A又改為B,這個過程線程2是不知道的,這就是ABA問題,可以通過版本號或時間戳解決
15.Synchronized鎖原理和優(yōu)化
Synchronize是通過對象頭的markwordk來表明監(jiān)視器的,監(jiān)視器本質(zhì)是依賴操作系統(tǒng)的互斥鎖實現(xiàn)的。操作系統(tǒng)實現(xiàn)線程切換要從用戶態(tài)切換為核心態(tài),成本很高,此時這種鎖叫重量級鎖,在JDK1.6以后引入了偏向鎖、輕量級鎖、重量級鎖
偏向鎖:當(dāng)一段代碼沒有別的線程訪問,此時線程去訪問會直接獲取偏向鎖
輕量級鎖:當(dāng)鎖是偏向鎖時,有另外一個線程來訪問,偏向鎖會升級為輕量級鎖,這個線程會通過自旋方式不斷獲取鎖,不會阻塞,提高性能
重量級鎖:輕量級鎖自旋一段時間后線程還沒有獲取到鎖,線程就會進入阻塞狀態(tài),該鎖會升級為重量級鎖,重量級鎖時,來競爭鎖的所有線程都會阻塞,性能降低
注意,鎖只能升級不能降級
16.JUC常用輔助類
CountDownLatch:設(shè)定一個數(shù),當(dāng)調(diào)用CountDown()時數(shù)量減一,當(dāng)調(diào)用await() 時判斷計數(shù)器是否為0,不為0就阻塞,直到計數(shù)器為0
CyclicBarrier:設(shè)定一個數(shù),當(dāng)調(diào)用await() 時判斷計數(shù)器是否達到目標(biāo)值,未達到就阻塞,直到計數(shù)器達到目標(biāo)值
Semaphore:設(shè)定一個信號量,當(dāng)調(diào)用acquire()時判斷是否還有信號,有就信號量減一線程繼續(xù)執(zhí)行,沒有就阻塞等待其他線程釋放信號量,當(dāng)調(diào)用release()時釋放信號量,喚醒阻塞線程
17.如何根據(jù) CPU 核心數(shù)設(shè)計線程池線程數(shù)量
IO 密集型:核心數(shù)*2
計算密集型: 核心數(shù)+1
為什么加 1?即使當(dāng)計算密集型的線程偶爾由于缺失故障或者其他原因而暫停時,這個額外的線程也能確保 CPU 的時鐘周期不會被浪費。
三.JVM篇
1.JVM運行時數(shù)據(jù)區(qū)(內(nèi)存結(jié)構(gòu))
線程私有區(qū):
(1)虛擬機棧:每次調(diào)用方法都會在虛擬機棧中產(chǎn)生一個棧幀,每個棧幀中都有方法的參數(shù)、局部變量、方法出口等信息,方法執(zhí)行完畢后釋放棧幀
(2)本地方法棧:為native修飾的本地方法提供的空間,在HotSpot中與虛擬機合二為一
(3)程序計數(shù)器:保存指令執(zhí)行的地址,方便線程切回后能繼續(xù)執(zhí)行代碼
線程共享區(qū):
(4)堆內(nèi)存:Jvm進行垃圾回收的主要區(qū)域,存放對象信息,分為新生代和老年代
(5)方法區(qū):存放類信息、靜態(tài)變量、常量、運行時常量池等信息。JDK1.8之前用持久代實現(xiàn),JDK1.8后用元空間實現(xiàn),元空間使用的是本地內(nèi)存,而非在JVM內(nèi)存結(jié)構(gòu)中
2.什么情況下會內(nèi)存溢出?
堆內(nèi)存溢出:(1)當(dāng)對象一直創(chuàng)建而不被回收時(2)加載的類越來越多時(3)虛擬機棧的線程越來越多時
棧溢出:方法調(diào)用次數(shù)過多,一般是遞歸不當(dāng)造成
3.JVM有哪些垃圾回收算法?
(1)標(biāo)記清除算法: 標(biāo)記不需要回收的對象,然后清除沒有標(biāo)記的對象,會造成許多內(nèi)存碎片。
(2)復(fù)制算法: 將內(nèi)存分為兩塊,只使用一塊,進行垃圾回收時,先將存活的對象復(fù)制到另一塊區(qū)域,然后清空之前的區(qū)域。用在新生代
(3)標(biāo)記整理算法: 與標(biāo)記清除算法類似,但是在標(biāo)記之后,將存活對象向一端移動,然后清除邊界外的垃圾對象。用在老年代
4.GC如何判斷對象可以被回收?
(1)引用計數(shù)法:已淘汰,為每個對象添加引用計數(shù)器,引用為0時判定可以回收,會有兩個對象相互引用無法回收的問題
(2)可達性分析法:從GCRoot開始往下搜索,搜索過的路徑稱為引用鏈,若一個對象GCRoot沒有任何的引用鏈,則判定可以回收
GCRoot有:虛擬機棧中引用的對象,方法區(qū)中靜態(tài)變量引用的對象,本地方法棧中引用的對象
5.典型垃圾回收器
G1 :JDK1.9以后的默認垃圾回收器,支持并發(fā),采用標(biāo)記整理+復(fù)制算法,注重響應(yīng)速度
6.類加載器和雙親委派機制
從父類加載器到子類加載器分別為:
BootStrapClassLoader ? ?加載路徑為:JAVA_HOME/jre/lib
ExtensionClassLoader ? ?加載路徑為:JAVA_HOME/jre/lib/ext
ApplicationClassLoader ?加載路徑為:classpath
還有一個自定義類加載器
當(dāng)一個類加載器收到類加載請求時,會先把這個請求交給父類加載器處理,若父類加載器找不到該類,再由自己去尋找。該機制可以避免類被重復(fù)加載,還可以避免系統(tǒng)級別的類被篡改
7.類加載過程
(1)加載 :加載字節(jié)碼文件,將字節(jié)碼中的靜態(tài)變量和常量轉(zhuǎn)換到方法區(qū)中,在堆中生成class對象作為方法區(qū)入口
(2)連接:
驗證:驗證字節(jié)碼文件的正確性。
準(zhǔn)備:正式為類變量在方法區(qū)中分配內(nèi)存,并設(shè)置初始值。
解析:將符號引用(如類的全限定名)解析為直接引用(類在實際內(nèi)存中的地址)。()
(3)初始化 :執(zhí)行類構(gòu)造器(不是常規(guī)的構(gòu)造方法),為靜態(tài)變量賦初值并初始化靜態(tài)代碼塊。
8.JVM中有哪些引用?
強引用:new的對象。哪怕內(nèi)存溢出也不會回收
軟引用:只有內(nèi)存不足時才會回收
弱引用:每次垃圾回收都會回收
虛引用:必須配合引用隊列使用,一般用于追蹤垃圾回收動作
9.對象頭中有哪些信息
對象頭中有兩部分,一部分是MarkWork,存儲對象運行時的數(shù)據(jù),如GC分代年齡、GC標(biāo)記、鎖的狀態(tài)、線程ID等;另外一部分是指向?qū)ο箢愋偷闹羔槪绻菙?shù)組,還有一個部分存放數(shù)組長度
10.JVM內(nèi)存參數(shù)
-Xmx[]:堆空間最大內(nèi)存
-Xms[]:堆空間最小內(nèi)存,一般設(shè)置成跟堆空間最大內(nèi)存一樣的
-Xmn[]:新生代的最大內(nèi)存
-xx[use 垃圾回收器名稱]:指定垃圾回收器
-xss:設(shè)置單個線程棧大小
一般設(shè)堆空間為最大可用物理地址的百分之80
11.JVM類初始化順序
父類靜態(tài)代碼塊和靜態(tài)成員變量->子類靜態(tài)代碼塊和靜態(tài)成員變量->父類代碼塊和普通成員變量->父類構(gòu)造方法->子類代碼塊和普成員變量->子類構(gòu)造方法
四.Mysql篇
1.MyIAm和InnoDB的區(qū)別
InnoDB支持事務(wù),MyIAm不支持
InnoDB支持外鍵,MyIAm不支持
InnoDB是聚簇索引,MyIAm是非聚簇索引
InnoDB支持行鎖和表鎖,MyIAm只支持表鎖
InnoDB不支持全文索引,MyIAm支持
InnoDB支持自增和MVCC模式的讀寫,MyIAm不支持
2.mysql事務(wù)特性
原子性:一個事務(wù)內(nèi)的操作統(tǒng)一成功或失敗
一致性:事務(wù)前后的數(shù)據(jù)總量不變
隔離性:事務(wù)與事務(wù)之間相互不影響
持久性:事務(wù)一旦提交發(fā)生的改變不可逆
3.事務(wù)靠什么保證
原子性:由undolog日志保證,他記錄了需要回滾的日志信息,回滾時撤銷已執(zhí)行的sql
一致性:由其他三大特性共同保證,是事務(wù)的目的
隔離性:由MVCC保證
持久性:由redolog日志和內(nèi)存保證,mysql修改數(shù)據(jù)時內(nèi)存和redolog會記錄操作,宕機時可恢復(fù)
4.事務(wù)的隔離級別
在高并發(fā)情況下,并發(fā)事務(wù)會產(chǎn)生臟讀、不可重復(fù)讀、幻讀問題,這時需要用隔離級別來控制
讀未提交: 允許一個事務(wù)讀取另一個事務(wù)已提交的數(shù)據(jù),可能出現(xiàn)不可重復(fù)讀,幻讀。
讀提交: ? ?只允許事務(wù)讀取另一個事務(wù)沒有提交的數(shù)據(jù)可能出現(xiàn)不可重復(fù)讀,幻讀。
可重復(fù)讀: 確保同一字段多次讀取結(jié)果一致,可能出現(xiàn)歡幻讀。
可串行化: 所有事務(wù)逐次執(zhí)行,沒有并發(fā)問日
Inno DB 默認隔離級別為可重復(fù)讀級別,分為快照度和當(dāng)前讀,并且通過間隙鎖解決了幻讀問題。
5.什么是快照讀和當(dāng)前讀
*快照讀讀取的是當(dāng)前數(shù)據(jù)的可見版本,可能是會過期數(shù)據(jù),不加鎖的select就是快照都
*當(dāng)前讀讀取的是數(shù)據(jù)的最新版本,并且當(dāng)前讀返回的記錄都會上鎖,保證其他事務(wù)不會并發(fā)修改這條記錄。如update、insert、delete、select for undate(排他鎖)、select lockin share mode(共享鎖) 都是當(dāng)前讀
6.MVCC是什么
MVCC是多版本并發(fā)控制,為每次事務(wù)生成一個新版本數(shù)據(jù),每個事務(wù)都由自己的版本,從而不加鎖就決絕讀寫沖突,這種讀叫做快照讀。只在讀已提交和可重復(fù)讀中生效。
實現(xiàn)原理由四個東西保證,他們是
undolog日志:記錄了數(shù)據(jù)歷史版本
readView:事務(wù)進行快照讀時產(chǎn)生的視圖,記錄了當(dāng)前系統(tǒng)中活躍的事務(wù)id,控制哪個歷史版本對當(dāng)前事務(wù)可見
隱藏字段DB_TRC_ID: 最近修改記錄的事務(wù)ID
隱藏字段DB_Roll_PTR: 回滾指針,配合undolog指向數(shù)據(jù)的上一個版本
7.MySQL有哪些索引
主鍵索引:一張表只能有一個主鍵索引,主鍵索引列不能有空值和重復(fù)值
唯一索引:唯一索引不能有相同值,但允許為空
普通索引:允許出現(xiàn)重復(fù)值
組合索引:對多個字段建立一個聯(lián)合索引,減少索引開銷,遵循最左匹配原則
全文索引:myisam引擎支持,通過建立倒排索引提升檢索效率,廣泛用于搜索引擎
8.聚簇索引和非聚簇索引的區(qū)別
聚簇索引:將索引和值放在了一起,根據(jù)索引可以直接獲取值,如果主鍵值很大的話,輔助索引也會變得很大
非聚簇索引:葉子節(jié)點存放的是數(shù)據(jù)行地址,先根據(jù)索引找到數(shù)據(jù)地址,再根據(jù)地址去找數(shù)據(jù)
他們都是b+數(shù)結(jié)構(gòu)
9.B和B+數(shù)的區(qū)別,為什么使用B+數(shù)
二叉樹:索引字段有序,極端情況會變成鏈表形式
AVL數(shù):樹的高度不可控
B數(shù):控制了樹的高度,但是索引值和data都分布在每個具體的節(jié)點當(dāng)中,若要進行范圍查詢,要進行多次回溯,IO開銷大
B+樹:非葉子節(jié)點只存儲索引值,葉子節(jié)點再存儲索引+具體數(shù)據(jù),從小到大用鏈表連接在一起,范圍查詢可直接遍歷不需要回溯7
10.MySQL有哪些鎖
基于粒度:
*表級鎖:對整張表加鎖,粒度大并發(fā)小
*行級鎖:對行加鎖,粒度小并發(fā)大
*間隙鎖:間隙鎖,鎖住表的一個區(qū)間,間隙鎖之間不會沖突只在可重復(fù)讀下才生效,解決了幻讀
基于屬性:
*共享鎖:又稱讀鎖,一個事務(wù)為表加了讀鎖,其它事務(wù)只能加讀鎖,不能加寫鎖
*排他鎖:又稱寫鎖,一個事務(wù)加寫鎖之后,其他事務(wù)不能再加任何鎖,避免臟讀問題
11.MySQL如果做慢查詢優(yōu)化
(1)分析sql語句,是否加載了不需要的數(shù)據(jù)列
(2)分析sql執(zhí)行計劃,字段有沒有索引,索引是否失效,是否用對索引
(3)表中數(shù)據(jù)是否太大,是不是要分庫分表
12.哪些情況索引會失效
(1)where條件中有or,除非所有查詢條件都有索引,否則失效
(2)like查詢用%開頭,索引失效
(3)索引列參與計算,索引失效
(4)違背最左匹配原則,索引失效
(5)索引字段發(fā)生類型轉(zhuǎn)換,索引失效
(6)mysql覺得全表掃描更快時(數(shù)據(jù)少),索引失效
13.Mysql內(nèi)連接、左連接、右連接的區(qū)別
內(nèi)連接取量表交集部分,左連接取左表全部右表匹部分,右連接取右表全部坐表匹部分
五.Spring系列(spring全家桶)
1.Bean 的作用域
(1)Singleton:一個IOC容器只有一個
(2)Prototype:每次調(diào)用getBean()都會生成一個新的對象
(3)request:每個http請求都會創(chuàng)建一個自己的bean
(4)session:同一個session共享一個實例
(5)application:整個serverContext只有一個bean
(6)webSocket:一個websocket只有一個bean
2.Bean 生命周期
實例化 Instantiation->屬性賦值 Populate->初始化 Initialization->銷毀 Destruction
在這四步的基礎(chǔ)上面,Spring 提供了一些拓展點:
*Bean 自身的方法: 包括了 Bean 本身調(diào)用的方法和通過配置文件中的 init-method 和 destroy-method 指定的方法
*Bean 級生命周期接口方法:包括了 BeanNameAware、BeanFactoryAware、InitializingBean 和 DiposableBean 這些接口的方法
*容器級生命周期接口方法:包括了 InstantiationAwareBeanPostProcessor 和 BeanPostProcessor 這兩個接口實現(xiàn),一般稱它們的實現(xiàn)類為“后處理器”。
*工廠后處理器接口方法: 包括了 AspectJWeavingEnabler, ConfigurationClassPostProcessor, CustomAutowireConfigurer 等等非常有用的工廠后處理器接口的方法。工廠后處理器也是容器級的。在應(yīng)用上下文裝配配置文件之后立即調(diào)用。
3.Spring 事務(wù)原理?
spring事務(wù)有編程式和聲明式,我們一般使用聲明式,在某個方法上增加@Transactional注解,這個方法中的sql會統(tǒng)一成功或失敗。
原理是:
當(dāng)一個方法加上@Transactional注解,spring會基于這個類生成一個代理對象并將這個代理對象作為bean,當(dāng)使用這個bean中的方法時,如果存在@Transactional注解,就會將事務(wù)自動提交設(shè)為false,然后執(zhí)行方法,執(zhí)行過程沒有異常則提交,有異常則回滾、
4.spring事務(wù)失效場景
(1)事務(wù)方法所在的類沒有加載到容器中
(2)事務(wù)方法不是public類型
(3)同一類中,一個沒有添加事務(wù)的方法調(diào)用另外以一個添加事務(wù)的方法,事務(wù)不生效
(4)spring事務(wù)默認只回滾運行時異常,可以用rollbackfor屬性設(shè)置
(5)業(yè)務(wù)自己捕獲了異常,事務(wù)會認為程序正常秩序
5.spring事務(wù)的隔離級別
default:默認級別,使用數(shù)據(jù)庫自定義的隔離級別
其它四種隔離級別與mysql一樣
6.spring事務(wù)的傳播行為
(1)支持當(dāng)前事務(wù),如果不存在,則新啟一個事務(wù)
(2)支持當(dāng)前事務(wù),如果不存在,則拋出異常
(3)支持當(dāng)前事務(wù),如果不存在,則以非事務(wù)方式執(zhí)行
(4)不支持當(dāng)前事務(wù),創(chuàng)建一個新事物
(5)不支持當(dāng)前事務(wù),如果已存在事務(wù)就拋異常
(6)不支持當(dāng)前事務(wù),始終以非事務(wù)方式執(zhí)行
7.Spring IoC
8.spring用了哪些設(shè)計模式
BeanFactory用了工廠模式,AOP用了動態(tài)代理模式,RestTemplate用來模板方法模式,SpringMVC中handlerAdaper用來適配器模式,Spring里的監(jiān)聽器用了觀察者模式
9.SpringMV工作原理
SpringMVC工作過程圍繞著前端控制器DispatchServerlet,幾個重要組件有HandleMapping(處理器映射器)、HandleAdapter(處理器適配器)、ViewReslover(試圖解析器)
工作流程:
(1)DispatchServerlet接收用戶請求將請求發(fā)送給HandleMapping
(2)HandleMapping根據(jù)請求url找到具體的handle和攔截器,返回給DispatchServerlet
(3)DispatchServerlet調(diào)用HandleAdapter,HandleAdapter執(zhí)行具體的controller,并將controller返回的ModelAndView返回給DispatchServler
(4)DispatchServerlet將ModelAndView傳給ViewReslover,ViewReslover解析后返回具體view
(5)DispatchServerlet根據(jù)view進行視圖渲染,返回給用戶
10.springboot自動配置原理
啟動類@SpringbootApplication注解下,有三個關(guān)鍵注解
(1)@springbootConfiguration:表示啟動類是一個自動配置類
(2)@CompontScan:掃描啟動類所在包外的組件到容器中
(3)@EnableConfigutarion:最關(guān)鍵的一個注解,他擁有兩個子注解,其中@AutoConfigurationpackageu會將啟動類所在包下的所有組件到容器中,@Import會導(dǎo)入一個自動配置文件選擇器,他會去加載META_INF目錄下的spring.factories文件,這個文件中存放很大自動配置類的全類名,這些類會根據(jù)元注解的裝配條件生效,生效的類就會被實例化,加載到ioc容器中
11.springboot常用注解
@RestController ? ? ? ? ? ? ? ? ?:修飾類,該控制器會返回Json數(shù)據(jù)
@RequestMapping("/path") :修飾類,該控制器的請求路徑
@Autowired ? ? ? ? ? ? ? ? ? ? ? ? : ?修飾屬性,按照類型進行依賴注入
@PathVariable ? ? ? ? ? ? ? ? ? ? : ?修飾參數(shù),將路徑值映射到參數(shù)上
@ResponseBody ? ? ? ? ? ? ? ? :修飾方法,該方法會返回Json數(shù)據(jù)
@RequestBody(需要使用Post提交方式) :修飾參數(shù),將Json數(shù)據(jù)封裝到對應(yīng)參數(shù)中
@Controller@Service@Compont: ?將類注冊到ioc容器
六.Redis系列
1.redis為什么快?
(1)完全基于內(nèi)存操作,數(shù)據(jù)都存在內(nèi)存中
(2)采用單線程,避免了不必要的上下文切換帶來的性能問題,也不用考慮鎖的問題
(3)基于非阻塞的io多路復(fù)用機制
(4)數(shù)據(jù)結(jié)構(gòu)簡單,對數(shù)據(jù)操作簡單
2.redis持久化機制
(1)快照持久化RDB
redis的默認持久化機制,通過父進程fork一個子進程,子進程將redis的數(shù)據(jù)快照寫入一個臨時文件,等待持久化完畢后替換上一次的rdb文件。整個過程主進程不進行任何的io操作。持久化策略可以通過save配置單位時間內(nèi)執(zhí)行多少次操作觸發(fā)持久化。所以RDB的優(yōu)點是保證redis性能最大化,恢復(fù)速度數(shù)據(jù)較快,缺點是可能會丟失兩次持久化之間的數(shù)據(jù)
(2)追加持久化AOF
以日志形式記錄每一次的寫入和刪除操作,策略有每秒同步、每次操作同步、不同步,優(yōu)點是數(shù)據(jù)完整性高,缺點是運行效率低,恢復(fù)時間長
3.Redis如何實現(xiàn)key的過期刪除?
采用的定期過期+惰性過期
定期刪除 :Redis 每隔一段時間從設(shè)置過期時間的 key 集合中,隨機抽取一些 key ,檢查是否過期,如果已經(jīng)過期做刪除處理。
惰性刪除 :Redis 在 key 被訪問的時候檢查 key 是否過期,如果過期則刪除。
4.Redis數(shù)據(jù)類型
String ?常用命令: set,get,decr,incr,mget等
Hash ? 常用命令: hget,hset,hgetall 等
List ? ? 常用命令: lpush,rpush,lpop,rpop,lrange 等
Set ? ? 常用命令: sadd,spop,smembers,sunion 等
SortSet 常用命令: zadd,zrange,zrem,zcard 等
5.Redis緩存穿透如何解決?
緩存穿透是指頻繁請求客戶端和緩存中都不存在的數(shù)據(jù),緩存永遠不生效,請求都到達了數(shù)據(jù)庫。
解決方案:
(1)在接口上做基礎(chǔ)校驗,比如id<=0就攔截
(2)緩存空對象:找不到的數(shù)據(jù)也緩存起來,并設(shè)置過期時間,可能會造成短期不一致
(3)布隆過濾器:在客戶端和緩存之間添加一個過濾器,攔截掉一定不存在的數(shù)據(jù)請求
6.Redis如何解決緩存擊穿?
緩存擊穿是值一個key非常熱點,key在某一瞬間失效,導(dǎo)致大量請求到達數(shù)據(jù)庫
解決方案:
(1)設(shè)置熱點數(shù)據(jù)永不過期
(2)給緩存重建的業(yè)務(wù)加上互斥鎖,缺點是性能低
7.Redis如何解決緩存雪崩?
緩存雪崩是值某一時間Key同時失效或redis宕機,導(dǎo)致大量請求到達數(shù)據(jù)庫
解決方案:
(1)搭建集群保證高可用
(2)進行數(shù)據(jù)預(yù)熱,給不同的key設(shè)置隨機的過期時間
(3)給緩存業(yè)務(wù)添加限流降級,通過加鎖或隊列控制操作redis的線程數(shù)量
(4)給業(yè)務(wù)添加多級緩存
8.Redis分布式鎖的實現(xiàn)原理
原理是使用setnx+setex命令來實現(xiàn),但是會有一系列問題:
(1)任務(wù)時常超過緩存時間,鎖自動釋放。可以使用Redision看門狗解決
(2)加鎖和釋放鎖的不是同一線程??梢栽赩alue中存入uuid,刪除時進行驗證。但是要注意驗證鎖和刪除鎖也不是一個原子性操作,可以用lua腳本使之成為原子性操作
(3)不可重入??梢允褂肦edision解決(實現(xiàn)機制類似AQS,計數(shù))
(4)redis集群下主節(jié)點宕機導(dǎo)致鎖丟失。使用紅鎖解決
9.Redis集群方案
(1)主從模式:個master節(jié)點,多個slave節(jié)點,master節(jié)點宕機slave自動變成主節(jié)點
(2)哨兵模式:在主從集群基礎(chǔ)上添加哨兵節(jié)點或哨兵集群,用于監(jiān)控master節(jié)點健康狀態(tài),通過投票機制選擇slave成為主節(jié)點
(3)分片集群:主從模式和哨兵模式解決了并發(fā)讀的問題,但沒有解決并發(fā)寫的問題,因此有了分片集群。分片集群有多個master節(jié)點并且不同master保存不同的數(shù)據(jù),master之間通過ping相互監(jiān)測健康狀態(tài)??蛻舳苏埱笕我庖粋€節(jié)點都會轉(zhuǎn)發(fā)到正確節(jié)點,因為每個master都被映射到0-16384個插槽上,集群的key是根據(jù)key的hash值與插槽綁定
10.Redis集群主從同步原理
主從同步第一次是全量同步:slave第一次請求master節(jié)點會根據(jù)replid判斷是否是第一次同步,是的話master會生成RDB發(fā)送給slave。
后續(xù)為增量同步:在發(fā)送RDB期間,會產(chǎn)生一個緩存區(qū)間記錄發(fā)送RDB期間產(chǎn)生的新的命令,slave節(jié)點在加載完后,會持續(xù)讀取緩存區(qū)間中的數(shù)據(jù)
11.Redis緩存一致性解決方案
更新數(shù)據(jù)庫時把緩存給刪除是最優(yōu)方案,可以更大概率避免并發(fā)問題,但是依舊會有緩存刪除失敗的問題。可以使用分布式事務(wù),或者在刪除失敗后把key發(fā)送到rabbitMQ中進行異步刪除重試
七.計算機網(wǎng)絡(luò)系列

1.TCP/IP模型
2.瀏覽器輸入地址后做了什么?

3.TCP三次握手

4.為什么TCP不能兩次握手
假設(shè)是兩次握手,若客戶端發(fā)起的連接請求阻塞在網(wǎng)絡(luò)中,會造成該報文的重傳,這時服務(wù)收到連接請求后會立刻進入連接狀態(tài),當(dāng)雙方傳輸完數(shù)據(jù)結(jié)束連接后,第一次阻塞的請求突然又到達了服務(wù)端,此時服務(wù)端又進入連接狀態(tài),而客戶端不會響應(yīng)服務(wù)端的連接確認報文
5.TCP四次揮手

6.為什么要進入時間等待狀態(tài)?
若客戶端發(fā)送確認釋放包后直接關(guān)閉,而服務(wù)端因為某種原因沒有收到客戶端的確認釋放包,就會一直發(fā)送確認請求,而客戶端永遠不會再響應(yīng)該請求。
7.TCP 滑動窗口
TCP 流量控制,主要使用滑動窗口協(xié)議,滑動窗口是接受數(shù)據(jù)端使用的窗口大小,用來告訴發(fā)送端接收端的緩存大小,以此可以控制發(fā)送端發(fā)送數(shù)據(jù)的大小,從而達到流量控制的目的。如果TCP發(fā)送方收到接收方的零窗口通知后,會啟動持續(xù)計時器。計時器超時后向接收方發(fā)送零窗口探測報文,如果響應(yīng)仍為0,就重新計時,不為0就打破死鎖
8.TCP擁塞控制
發(fā)送方會維護一個擁塞窗口大小的狀態(tài)變量,大小取決于網(wǎng)絡(luò)的擁塞程度。發(fā)送方的發(fā)送窗口大小是取接收方接收窗口和擁塞窗口中較小的一個
擁塞控制有四種算法:
慢開始:從小到大主鍵發(fā)送窗口,每收到一個確認報文窗口大小指數(shù)增長
擁塞避免:當(dāng)窗口大小到達一定閾值時,轉(zhuǎn)為擁塞避免,每收到一個確認報文窗口大小+1。若此時網(wǎng)絡(luò)超時,就把閾值調(diào)小一半,重新慢開始
快重傳:要求接收方收到請求后要立即回復(fù)
快恢復(fù):發(fā)送方連續(xù)收到多個確認時,就把擁塞避免閾值減小,然后直接開始擁塞避免
9.TCP超時重傳
發(fā)送方在發(fā)送按數(shù)據(jù)后一定時間內(nèi)沒有收到接收方響應(yīng)報文,就會重新發(fā)送剛剛的報文,接收到收到報文后會對該報文的序列號進行檢驗,已存在就拋棄
10.TCP可靠傳輸?shù)膶崿F(xiàn)
TCP是靠滑動窗口協(xié)議和連續(xù)ARQ協(xié)議配合流量控制和擁塞控制來保證的可靠傳輸。
ARQ是停止等待協(xié)議和自動重傳請求,它規(guī)定TCP要為每一次傳輸?shù)陌幪枺堪l(fā)送一個包,要等待對方確認后才能發(fā)送下一個分組,若一段時間對方?jīng)]有確認,就重新發(fā)送剛剛的報文。接收方會對數(shù)據(jù)包排序,把有序數(shù)據(jù)傳給應(yīng)用層,返回缺失的第一個ACK確認序列號給發(fā)送方,接收到收到報文后會對該報文的序列號進行檢驗,重復(fù)就丟棄。
流量控制是.....擁塞窗口上......(上面已經(jīng)說了)
11.TCP報頭有哪些信息

12.狀態(tài)碼
1xx:請求正在處理
2xx:請求成功處理
3xx:請求重定向 ? 301:永久重定向 ? ? ?302:臨時重定向 ? ? ? 304:使用本地緩存
4xx:客戶端錯誤 ? 400:請求格式錯誤 ? 403:沒有訪問權(quán)限 ? 404:請求資源不存在
5xx:服務(wù)端錯誤
13.socket通信流程
(1)服務(wù)端創(chuàng)建socket并調(diào)用bind()方法綁定ip和端口號
(2)服務(wù)端調(diào)用listen()方法建立監(jiān)聽,此時服務(wù)的scoket還沒有打開
(3)客戶端創(chuàng)建socket并調(diào)用connect()方法像服務(wù)端請求連接
(4)服務(wù)端socket監(jiān)聽到客戶端請求后,被動打開,調(diào)用accept()方法接收客戶端連接請求,當(dāng)accept()方法接收到客戶端connect()方法返回的響應(yīng)成功的信息后,連接成功
(5)客戶端向socket寫入請求信息,服務(wù)端讀取信息
(6)客戶端調(diào)用close()結(jié)束鏈接,服務(wù)端監(jiān)聽到釋放連接請求后,也結(jié)束鏈接
八.linux系列
1.linux常用命令
ifconfig:查看網(wǎng)絡(luò)接口詳情
ping:查看與某主機是否能聯(lián)通
ps -ef|grep 進程名稱:查看進程號
lost -i 端口 :查看端口占用情況
top:查看系統(tǒng)負載情況,包括系統(tǒng)時間、系統(tǒng)所有進程狀態(tài)、cpu情況
free:查看內(nèi)存占用情況
kill:正常殺死進程,發(fā)出的信號可能會被阻塞
kill -9:強制殺死進程,發(fā)送的是exit命令,不會被阻塞
2.linux的io模型
IO是數(shù)據(jù)的讀取和寫入,用戶進程讀取一次IO請求分為兩個階段:等待數(shù)據(jù)到達內(nèi)核緩沖區(qū)和將內(nèi)核空間數(shù)據(jù)拷貝到進程空間,當(dāng)用戶去內(nèi)核中拷貝數(shù)據(jù)時,要從用戶態(tài)轉(zhuǎn)為核心態(tài)
5中io模型:
(1)同步阻塞IO模型
用戶進程發(fā)起io調(diào)用后會被阻塞,等待內(nèi)核數(shù)據(jù)準(zhǔn)備完畢時就被喚醒,將內(nèi)核數(shù)據(jù)復(fù)制到用戶進程。這兩個階段都是阻塞的
(2)同步非阻塞IO模型
用戶進程發(fā)起IO調(diào)用后,若內(nèi)核數(shù)據(jù)還未準(zhǔn)備好,進程不會被阻塞,而是給用戶進程返回一個error,進程會繼續(xù)干別的事,每隔一段時間就去看看內(nèi)核數(shù)據(jù)是否準(zhǔn)備好。不過將內(nèi)核數(shù)據(jù)復(fù)制到用戶進程這個階段依舊是阻塞的
(3)IO多路復(fù)用模型
同步非阻塞IO要不停的查看內(nèi)核中數(shù)據(jù)是否準(zhǔn)備好,十分消耗cpu。IO多路復(fù)用讓一個線程去監(jiān)控一個fd文件,每個用戶進程都有一個fd文件描述符,將自己的文件描述符寫入這個fd文件,當(dāng)某個用戶進程需要的數(shù)據(jù)準(zhǔn)備好后,這個線程就去通知用戶進程。
(4)信號IO模型
當(dāng)用戶進程發(fā)起IO調(diào)用后,會向內(nèi)核注冊一個信號處理函數(shù),進程不會被阻塞,當(dāng)內(nèi)核數(shù)據(jù)準(zhǔn)備就緒時就返回一個信號給用戶進程,進程就可以直接在這個信號處理函數(shù)中獲取內(nèi)核數(shù)據(jù)進行拷貝??截愡@個階段依舊是阻塞的
(5)異步非阻塞模型
前面四種全是同步的。進程在發(fā)起IO調(diào)用后,無論數(shù)據(jù)是否到達,都直接返回結(jié)果。內(nèi)核數(shù)據(jù)準(zhǔn)備好時,由內(nèi)核將數(shù)據(jù)復(fù)制給用戶進程。兩個階段都是非阻塞的