2023年Java經(jīng)典面試題及答案
2023年Java經(jīng)典面試題及答案(200道)
Java面試題涵蓋的范圍非常廣泛,根據(jù)不同的崗位和公司要求,面試題可能會(huì)有所不同。為了讓面試者能順利應(yīng)對(duì)面試官的問(wèn)題,下面小編為大家收集整理了關(guān)于2023年Java經(jīng)典面試題及答案(200道)的相關(guān)內(nèi)容,希望對(duì)大家有所幫助!
2023年Java經(jīng)典面試題及答案(200道)
Java 基礎(chǔ)
1. JDK 和 JRE 有什么區(qū)別?
JDK:Java Development Kit 的簡(jiǎn)稱,Java 開發(fā)工具包,提供了 Java 的開發(fā)環(huán)境和運(yùn)行環(huán)境。
JRE:Java Runtime Environment 的簡(jiǎn)稱,Java 運(yùn)行環(huán)境,為 Java 的運(yùn)行提供了所需環(huán)境。
具體來(lái)說(shuō) JDK 其實(shí)包含了 JRE,同時(shí)還包含了編譯 Java 源碼的編譯器 Javac,還包含了很多 Java 程序調(diào)試和分析的工具。簡(jiǎn)單來(lái)說(shuō):如果你需要運(yùn)行 Java 程序,只需安裝 JRE 就可以了,如果你需要編寫 Java 程序,需要安裝 JDK。
2. == 和 equals 的區(qū)別是什么?
「== 解讀」
對(duì)于基本類型和引用類型 == 的作用效果是不同的,如下所示:
基本類型:比較的是值是否相同;
引用類型:比較的是引用是否相同;
代碼示例:
String x = "string";
String y = "string";
String z = new String("string");
System.out.println(x==y); // true
System.out.println(x==z); // false
System.out.println(x.equals(y)); // true
System.out.println(x.equals(z)); // true
代碼解讀:因?yàn)?x 和 y 指向的是同一個(gè)引用,所以 == 也是 true,而 new String()方法則重寫開辟了內(nèi)存空間,所以 == 結(jié)果為 false,而 equals 比較的一直是值,所以結(jié)果都為 true。
「equals 解讀」
equals 本質(zhì)上就是 ==,只不過(guò) String 和 Integer 等重寫了 equals 方法,把它變成了值比較??聪旅娴拇a就明白了。
首先來(lái)看默認(rèn)情況下 equals 比較一個(gè)有相同值的對(duì)象,代碼如下:
classCat{
publicCat(String name){
this.name = name;
}
private String name;
public String getName(){
return name;
}
publicvoidsetName(String name){
this.name = name;
}
}
Cat c1 = new Cat("王磊");
Cat c2 = new Cat("王磊");
System.out.println(c1.equals(c2)); // false
輸出結(jié)果出乎我們的意料,竟然是 false?這是怎么回事,看了 equals 源碼就知道了,源碼如下:
publicbooleanequals(Object obj){
return (this == obj);
}
原來(lái) equals 本質(zhì)上就是 ==。
那問(wèn)題來(lái)了,兩個(gè)相同值的 String 對(duì)象,為什么返回的是 true?代碼如下:
String s1 = new String("老王");
String s2 = new String("老王");
System.out.println(s1.equals(s2)); // true
同樣的,當(dāng)我們進(jìn)入 String 的 equals 方法,找到了答案,代碼如下:
publicbooleanequals(Object anObject){
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
原來(lái)是 String 重寫了 Object 的 equals 方法,把引用比較改成了值比較。
「總結(jié)」 :== 對(duì)于基本類型來(lái)說(shuō)是值比較,對(duì)于引用類型來(lái)說(shuō)是比較的是引用;而 equals 默認(rèn)情況下是引用比較,只是很多類重新了 equals 方法,比如 String、Integer 等把它變成了值比較,所以一般情況下 equals 比較的是值是否相等。
3. 兩個(gè)對(duì)象的 hashCode() 相同,則 equals() 也一定為 true,對(duì)嗎?
不對(duì),兩個(gè)對(duì)象的 hashCode() 相同,equals() 不一定 true。
代碼示例:
String str1 = "通話";
String str2 = "重地";
System. out. println(String. format("str1:%d | str2:%d", str1. hashCode(),str2. hashCode()));
System. out. println(str1. equals(str2));
執(zhí)行的結(jié)果:
str1:1179395 | str2:1179395
false
代碼解讀:很顯然“通話”和“重地”的 hashCode() 相同,然而 equals() 則為 false,因?yàn)樵谏⒘斜碇校琱ashCode() 相等即兩個(gè)鍵值對(duì)的哈希值相等,然而哈希值相等,并不一定能得出鍵值對(duì)相等。
4. final 在 Java 中有什么作用?
final 修飾的類叫最終類,該類不能被繼承。
final 修飾的方法不能被重寫。
final 修飾的變量叫常量,常量必須初始化,初始化之后值就不能被修改。
5. Java 中的 Math. round(-1. 5) 等于多少?
等于 -1,因?yàn)樵跀?shù)軸上取值時(shí),中間值(0.5)向右取整,所以正 0.5 是往上取整,負(fù) 0.5 是直接舍棄。
6. String 屬于基礎(chǔ)的數(shù)據(jù)類型嗎?
String 不屬于基礎(chǔ)類型,基礎(chǔ)類型有 8 種:byte、boolean、char、short、int、float、long、double,而 String 屬于對(duì)象。
7. Java 中操作字符串都有哪些類?它們之間有什么區(qū)別?
操作字符串的類有:String、StringBuffer、StringBuilder。
String 和 StringBuffer、StringBuilder 的區(qū)別在于 String 聲明的是不可變的對(duì)象,每次操作都會(huì)生成新的 String 對(duì)象,然后將指針指向新的 String 對(duì)象,而 StringBuffer、StringBuilder 可以在原有對(duì)象的基礎(chǔ)上進(jìn)行操作,所以在經(jīng)常改變字符串內(nèi)容的情況下最好不要使用 String。
StringBuffer 和 StringBuilder 最大的區(qū)別在于,StringBuffer 是線程安全的,而 StringBuilder 是非線程安全的,但 StringBuilder 的性能卻高于 StringBuffer,所以在單線程環(huán)境下推薦使用 StringBuilder,多線程環(huán)境下推薦使用 StringBuffer。
8. String str="i"與 String str=new String("i")一樣嗎?
不一樣,因?yàn)閮?nèi)存的分配方式不一樣。String str="i"的方式,Java 虛擬機(jī)會(huì)將其分配到常量池中;而 String str=new String("i") 則會(huì)被分到堆內(nèi)存中。
9. 如何將字符串反轉(zhuǎn)?
使用 StringBuilder 或者 stringBuffer 的 reverse() 方法。
示例代碼:
// StringBuffer reverse
StringBuffer stringBuffer = new StringBuffer();
stringBuffer. append("abcdefg");
System. out. println(stringBuffer. reverse()); // gfedcba
// StringBuilder reverse
StringBuilder stringBuilder = new StringBuilder();
stringBuilder. append("abcdefg");
System. out. println(stringBuilder. reverse()); // gfedcba
10. String 類的常用方法都有那些?
indexOf():返回指定字符的索引。
charAt():返回指定索引處的字符。
replace():字符串替換。
trim():去除字符串兩端空白。
split():分割字符串,返回一個(gè)分割后的字符串?dāng)?shù)組。
getBytes():返回字符串的 byte 類型數(shù)組。
length():返回字符串長(zhǎng)度。
toLowerCase():將字符串轉(zhuǎn)成小寫字母。
toUpperCase():將字符串轉(zhuǎn)成大寫字符。
substring():截取字符串。
equals():字符串比較。
11. 抽象類必須要有抽象方法嗎?
不需要,抽象類不一定非要有抽象方法。
示例代碼:
abstract classCat{
publicstaticvoidsayHi(){
System. out. println("hi~");
}
}
上面代碼,抽象類并沒有抽象方法但完全可以正常運(yùn)行。
12. 普通類和抽象類有哪些區(qū)別?
普通類不能包含抽象方法,抽象類可以包含抽象方法。
抽象類不能直接實(shí)例化,普通類可以直接實(shí)例化。
13. 抽象類能使用 final 修飾嗎?
不能,定義抽象類就是讓其他類繼承的,如果定義為 final 該類就不能被繼承,這樣彼此就會(huì)產(chǎn)生矛盾,所以 final 不能修飾抽象類,如下圖所示,編輯器也會(huì)提示錯(cuò)誤信息:
圖片
14. 接口和抽象類有什么區(qū)別?
實(shí)現(xiàn):抽象類的子類使用 extends 來(lái)繼承;接口必須使用 implements 來(lái)實(shí)現(xiàn)接口。
構(gòu)造函數(shù):抽象類可以有構(gòu)造函數(shù);接口不能有。
實(shí)現(xiàn)數(shù)量:類可以實(shí)現(xiàn)很多個(gè)接口;但是只能繼承一個(gè)抽象類。
訪問(wèn)修飾符:接口中的方法默認(rèn)使用 public 修飾;抽象類中的方法可以是任意訪問(wèn)修飾符。
15. Java 中 IO 流分為幾種?
按功能來(lái)分:輸入流(input)、輸出流(output)。
按類型來(lái)分:字節(jié)流和字符流。
字節(jié)流和字符流的區(qū)別是:字節(jié)流按 8 位傳輸以字節(jié)為單位輸入輸出數(shù)據(jù),字符流按 16 位傳輸以字符為單位輸入輸出數(shù)據(jù)。
16. BIO、NIO、AIO 有什么區(qū)別?
BIO:Block IO 同步阻塞式 IO,就是我們平常使用的傳統(tǒng) IO,它的特點(diǎn)是模式簡(jiǎn)單使用方便,并發(fā)處理能力低。
NIO:Non IO 同步非阻塞 IO,是傳統(tǒng) IO 的升級(jí),客戶端和服務(wù)器端通過(guò) Channel(通道)通訊,實(shí)現(xiàn)了多路復(fù)用。
AIO:Asynchronous IO 是 NIO 的升級(jí),也叫 NIO2,實(shí)現(xiàn)了異步非堵塞 IO ,異步 IO 的操作基于事件和回調(diào)機(jī)制。
17. Files的常用方法都有哪些?
Files. exists():檢測(cè)文件路徑是否存在。
Files. createFile():創(chuàng)建文件。
Files. createDirectory():創(chuàng)建文件夾。
Files. delete():刪除一個(gè)文件或目錄。
Files. copy():復(fù)制文件。
Files. move():移動(dòng)文件。
Files. size():查看文件個(gè)數(shù)。
Files. read():讀取文件。
Files. write():寫入文件。
容器
18. Java 容器都有哪些?
Java 容器分為 Collection 和 Map 兩大類,其下又有很多子類,如下所示:
Collection
List
ArrayList
LinkedList
Vector
Stack
Set
HashSet
LinkedHashSet
TreeSet
Map
HashMap
LinkedHashMap
TreeMap
ConcurrentHashMap
Hashtable
19. Collection 和 Collections 有什么區(qū)別?
Collection 是一個(gè)集合接口,它提供了對(duì)集合對(duì)象進(jìn)行基本操作的通用接口方法,所有集合都是它的子類,比如 List、Set 等。
Collections 是一個(gè)包裝類,包含了很多靜態(tài)方法,不能被實(shí)例化,就像一個(gè)工具類,比如提供的排序方法:Collections. sort(list)。
20. List、Set、Map 之間的區(qū)別是什么?
List、Set、Map 的區(qū)別主要體現(xiàn)在兩個(gè)方面:元素是否有序、是否允許元素重復(fù)。
三者之間的區(qū)別,如下表:
圖片
21. HashMap 和 Hashtable 有什么區(qū)別?
存儲(chǔ):HashMap 運(yùn)行 key 和 value 為 null,而 Hashtable 不允許。
線程安全:Hashtable 是線程安全的,而 HashMap 是非線程安全的。
推薦使用:在 Hashtable 的類注釋可以看到,Hashtable 是保留類不建議使用,推薦在單線程環(huán)境下使用 HashMap 替代,如果需要多線程使用則用 ConcurrentHashMap 替代。
22. 如何決定使用 HashMap 還是 TreeMap?
對(duì)于在 Map 中插入、刪除、定位一個(gè)元素這類操作,HashMap 是最好的選擇,因?yàn)橄鄬?duì)而言 HashMap 的插入會(huì)更快,但如果你要對(duì)一個(gè) key 集合進(jìn)行有序的遍歷,那 TreeMap 是更好的選擇。
23. 說(shuō)一下 HashMap 的實(shí)現(xiàn)原理?
HashMap 基于 Hash 算法實(shí)現(xiàn)的,我們通過(guò) put(key,value)存儲(chǔ),get(key)來(lái)獲取。當(dāng)傳入 key 時(shí),HashMap 會(huì)根據(jù) key. hashCode() 計(jì)算出 hash 值,根據(jù) hash 值將 value 保存在 bucket 里。當(dāng)計(jì)算出的 hash 值相同時(shí),我們稱之為 hash 沖突,HashMap 的做法是用鏈表和紅黑樹存儲(chǔ)相同 hash 值的 value。當(dāng) hash 沖突的個(gè)數(shù)比較少時(shí),使用鏈表否則使用紅黑樹。
24. 說(shuō)一下 HashSet 的實(shí)現(xiàn)原理?
HashSet 是基于 HashMap 實(shí)現(xiàn)的,HashSet 底層使用 HashMap 來(lái)保存所有元素,因此 HashSet 的實(shí)現(xiàn)比較簡(jiǎn)單,相關(guān) HashSet 的操作,基本上都是直接調(diào)用底層 HashMap 的相關(guān)方法來(lái)完成,HashSet 不允許重復(fù)的值。
25. ArrayList 和 LinkedList 的區(qū)別是什么?
數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn):ArrayList 是動(dòng)態(tài)數(shù)組的數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn),而 LinkedList 是雙向鏈表的數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)。
隨機(jī)訪問(wèn)效率:ArrayList 比 LinkedList 在隨機(jī)訪問(wèn)的時(shí)候效率要高,因?yàn)?LinkedList 是線性的數(shù)據(jù)存儲(chǔ)方式,所以需要移動(dòng)指針從前往后依次查找。
增加和刪除效率:在非首尾的增加和刪除操作,LinkedList 要比 ArrayList 效率要高,因?yàn)?ArrayList 增刪操作要影響數(shù)組內(nèi)的其他數(shù)據(jù)的下標(biāo)。
綜合來(lái)說(shuō),在需要頻繁讀取集合中的元素時(shí),更推薦使用 ArrayList,而在插入和刪除操作較多時(shí),更推薦使用 LinkedList。
26. 如何實(shí)現(xiàn)數(shù)組和 List 之間的轉(zhuǎn)換?
數(shù)組轉(zhuǎn) List:使用 Arrays. asList(array) 進(jìn)行轉(zhuǎn)換。
List 轉(zhuǎn)數(shù)組:使用 List 自帶的 toArray() 方法。
代碼示例:
// list to array
List list = new ArrayList();
list. add("王磊");
list. add("的博客");
list. toArray();
// array to list
String[] array = new String[]{"王磊","的博客"};
Arrays. asList(array);
27. ArrayList 和 Vector 的區(qū)別是什么?
線程安全:Vector 使用了 Synchronized 來(lái)實(shí)現(xiàn)線程同步,是線程安全的,而 ArrayList 是非線程安全的。
性能:ArrayList 在性能方面要優(yōu)于 Vector。
擴(kuò)容:ArrayList 和 Vector 都會(huì)根據(jù)實(shí)際的需要?jiǎng)討B(tài)的調(diào)整容量,只不過(guò)在 Vector 擴(kuò)容每次會(huì)增加 1 倍,而 ArrayList 只會(huì)增加 50%。
28. Array 和 ArrayList 有何區(qū)別?
Array 可以存儲(chǔ)基本數(shù)據(jù)類型和對(duì)象,ArrayList 只能存儲(chǔ)對(duì)象。
Array 是指定固定大小的,而 ArrayList 大小是自動(dòng)擴(kuò)展的。
Array 內(nèi)置方法沒有 ArrayList 多,比如 addAll、removeAll、iteration 等方法只有 ArrayList 有。
29. 在 Queue 中 poll()和 remove()有什么區(qū)別?
相同點(diǎn):都是返回第一個(gè)元素,并在隊(duì)列中刪除返回的對(duì)象。
不同點(diǎn):如果沒有元素 poll()會(huì)返回 null,而 remove()會(huì)直接拋出 NoSuchElementException 異常。
代碼示例:
Queue queue = new LinkedList();
queue. offer("string"); // add
System. out. println(queue. poll());
System. out. println(queue. remove());
System. out. println(queue. size());
30. 哪些集合類是線程安全的?
Vector、Hashtable、Stack 都是線程安全的,而像 HashMap 則是非線程安全的,不過(guò)在 JDK 1.5 之后隨著 Java. util. concurrent 并發(fā)包的出現(xiàn),它們也有了自己對(duì)應(yīng)的線程安全類,比如 HashMap 對(duì)應(yīng)的線程安全類就是 ConcurrentHashMap。
31. 迭代器 Iterator 是什么?
Iterator 接口提供遍歷任何 Collection 的接口。我們可以從一個(gè) Collection 中使用迭代器方法來(lái)獲取迭代器實(shí)例。迭代器取代了 Java 集合框架中的 Enumeration,迭代器允許調(diào)用者在迭代過(guò)程中移除元素。
32. Iterator 怎么使用?有什么特點(diǎn)?
Iterator 使用代碼如下:
List list = new ArrayList<>();
Iterator it = list. iterator();
while(it. hasNext()){
String obj = it. next();
System. out. println(obj);
}
Iterator 的特點(diǎn)是更加安全,因?yàn)樗梢源_保,在當(dāng)前遍歷的集合元素被更改的時(shí)候,就會(huì)拋出 ConcurrentModificationException 異常。
33. Iterator 和 ListIterator 有什么區(qū)別?
Iterator 可以遍歷 Set 和 List 集合,而 ListIterator 只能遍歷 List。
Iterator 只能單向遍歷,而 ListIterator 可以雙向遍歷(向前/后遍歷)。
ListIterator 從 Iterator 接口繼承,然后添加了一些額外的功能,比如添加一個(gè)元素、替換一個(gè)元素、獲取前面或后面元素的索引位置。
34. 怎么確保一個(gè)集合不能被修改?
可以使用 Collections. unmodifiableCollection(Collection c) 方法來(lái)創(chuàng)建一個(gè)只讀集合,這樣改變集合的任何操作都會(huì)拋出 Java. lang. UnsupportedOperationException 異常。
示例代碼如下:
List list = new ArrayList<>();
list. add("x");
Collection clist = Collections. unmodifiableCollection(list);
clist. add("y"); // 運(yùn)行時(shí)此行報(bào)錯(cuò)
System. out. println(list. size());
多線程
35. 并行和并發(fā)有什么區(qū)別?
并行:多個(gè)處理器或多核處理器同時(shí)處理多個(gè)任務(wù)。
并發(fā):多個(gè)任務(wù)在同一個(gè) CPU 核上,按細(xì)分的時(shí)間片輪流(交替)執(zhí)行,從邏輯上來(lái)看那些任務(wù)是同時(shí)執(zhí)行。
如下圖:
圖片
并發(fā) = 兩個(gè)隊(duì)列和一臺(tái)咖啡機(jī)。
并行 = 兩個(gè)隊(duì)列和兩臺(tái)咖啡機(jī)。
36. 線程和進(jìn)程的區(qū)別?
一個(gè)程序下至少有一個(gè)進(jìn)程,一個(gè)進(jìn)程下至少有一個(gè)線程,一個(gè)進(jìn)程下也可以有多個(gè)線程來(lái)增加程序的執(zhí)行速度。
37. 守護(hù)線程是什么?
守護(hù)線程是運(yùn)行在后臺(tái)的一種特殊進(jìn)程。它獨(dú)立于控制終端并且周期性地執(zhí)行某種任務(wù)或等待處理某些發(fā)生的事件。在 Java 中垃圾回收線程就是特殊的守護(hù)線程。
38. 創(chuàng)建線程有哪幾種方式?
創(chuàng)建線程有三種方式:
繼承 Thread 重寫 run 方法;
實(shí)現(xiàn) Runnable 接口;
實(shí)現(xiàn) Callable 接口。
39. 說(shuō)一下 runnable 和 callable 有什么區(qū)別?
runnable 沒有返回值,callable 可以拿到有返回值,callable 可以看作是 runnable 的補(bǔ)充。
40. 線程有哪些狀態(tài)?
線程的狀態(tài):
NEW 尚未啟動(dòng)
RUNNABLE 正在執(zhí)行中
BLOCKED 阻塞的(被同步鎖或者IO鎖阻塞)
WAITING 永久等待狀態(tài)
TIMED_WAITING 等待指定的時(shí)間重新被喚醒的狀態(tài)
TERMINATED 執(zhí)行完成
圖片
41. sleep() 和 wait() 有什么區(qū)別?
類的不同:sleep() 來(lái)自 Thread,wait() 來(lái)自 Object。
釋放鎖:sleep() 不釋放鎖;wait() 釋放鎖。
用法不同:sleep() 時(shí)間到會(huì)自動(dòng)恢復(fù);wait() 可以使用 notify()/notifyAll()直接喚醒。
42. notify()和 notifyAll()有什么區(qū)別?
notifyAll()會(huì)喚醒所有的線程,notify()之后喚醒一個(gè)線程。notifyAll() 調(diào)用后,會(huì)將全部線程由等待池移到鎖池,然后參與鎖的競(jìng)爭(zhēng),競(jìng)爭(zhēng)成功則繼續(xù)執(zhí)行,如果不成功則留在鎖池等待鎖被釋放后再次參與競(jìng)爭(zhēng)。而 notify()只會(huì)喚醒一個(gè)線程,具體喚醒哪一個(gè)線程由虛擬機(jī)控制。
43. 線程的 run() 和 start() 有什么區(qū)別?
start() 方法用于啟動(dòng)線程,run() 方法用于執(zhí)行線程的運(yùn)行時(shí)代碼。run() 可以重復(fù)調(diào)用,而 start() 只能調(diào)用一次。
44. 創(chuàng)建線程池有哪幾種方式?
線程池創(chuàng)建有七種方式,最核心的是最后一種:
newSingleThreadExecutor():它的特點(diǎn)在于工作線程數(shù)目被限制為 1,操作一個(gè)無(wú)界的工作隊(duì)列,所以它保證了所有任務(wù)的都是被順序執(zhí)行,最多會(huì)有一個(gè)任務(wù)處于活動(dòng)狀態(tài),并且不允許使用者改動(dòng)線程池實(shí)例,因此可以避免其改變線程數(shù)目;
newCachedThreadPool():它是一種用來(lái)處理大量短時(shí)間工作任務(wù)的線程池,具有幾個(gè)鮮明特點(diǎn):它會(huì)試圖緩存線程并重用,當(dāng)無(wú)緩存線程可用時(shí),就會(huì)創(chuàng)建新的工作線程;如果線程閑置的時(shí)間超過(guò) 60 秒,則被終止并移出緩存;長(zhǎng)時(shí)間閑置時(shí),這種線程池,不會(huì)消耗什么資源。其內(nèi)部使用 SynchronousQueue 作為工作隊(duì)列;
newFixedThreadPool(int nThreads):重用指定數(shù)目(nThreads)的線程,其背后使用的是無(wú)界的工作隊(duì)列,任何時(shí)候最多有 nThreads 個(gè)工作線程是活動(dòng)的。這意味著,如果任務(wù)數(shù)量超過(guò)了活動(dòng)隊(duì)列數(shù)目,將在工作隊(duì)列中等待空閑線程出現(xiàn);如果有工作線程退出,將會(huì)有新的工作線程被創(chuàng)建,以補(bǔ)足指定的數(shù)目 nThreads;
newSingleThreadScheduledExecutor():創(chuàng)建單線程池,返回 ScheduledExecutorService,可以進(jìn)行定時(shí)或周期性的工作調(diào)度;
newScheduledThreadPool(int corePoolSize):和newSingleThreadScheduledExecutor()類似,創(chuàng)建的是個(gè) ScheduledExecutorService,可以進(jìn)行定時(shí)或周期性的工作調(diào)度,區(qū)別在于單一工作線程還是多個(gè)工作線程;
newWorkStealingPool(int parallelism):這是一個(gè)經(jīng)常被人忽略的線程池,Java 8 才加入這個(gè)創(chuàng)建方法,其內(nèi)部會(huì)構(gòu)建ForkJoinPool,利用Work-Stealing算法,并行地處理任務(wù),不保證處理順序;
ThreadPoolExecutor():是最原始的線程池創(chuàng)建,上面1-3創(chuàng)建方式都是對(duì)ThreadPoolExecutor的封裝。
45. 線程池都有哪些狀態(tài)?
RUNNING:這是最正常的狀態(tài),接受新的任務(wù),處理等待隊(duì)列中的任務(wù)。
SHUTDOWN:不接受新的任務(wù)提交,但是會(huì)繼續(xù)處理等待隊(duì)列中的任務(wù)。
STOP:不接受新的任務(wù)提交,不再處理等待隊(duì)列中的任務(wù),中斷正在執(zhí)行任務(wù)的線程。
TIDYING:所有的任務(wù)都銷毀了,workCount 為 0,線程池的狀態(tài)在轉(zhuǎn)換為 TIDYING 狀態(tài)時(shí),會(huì)執(zhí)行鉤子方法 terminated()。
TERMINATED:terminated()方法結(jié)束后,線程池的狀態(tài)就會(huì)變成這個(gè)。
46. 線程池中 submit() 和 execute() 方法有什么區(qū)別?
execute():只能執(zhí)行 Runnable 類型的任務(wù)。
submit():可以執(zhí)行 Runnable 和 Callable 類型的任務(wù)。
Callable 類型的任務(wù)可以獲取執(zhí)行的返回值,而 Runnable 執(zhí)行無(wú)返回值。
47. 在 Java 程序中怎么保證多線程的運(yùn)行安全?
方法一:使用安全類,比如 Java. util. concurrent 下的類。
方法二:使用自動(dòng)鎖 synchronized。
方法三:使用手動(dòng)鎖 Lock。
手動(dòng)鎖 Java 示例代碼如下:
Lock lock = new ReentrantLock();
lock. lock();
try {
System. out. println("獲得鎖");
} catch (Exception e) {
// TODO: handle exception
} finally {
System. out. println("釋放鎖");
lock. unlock();
}
48. 多線程中 synchronized 鎖升級(jí)的原理是什么?
synchronized 鎖升級(jí)原理:在鎖對(duì)象的對(duì)象頭里面有一個(gè) threadid 字段,在第一次訪問(wèn)的時(shí)候 threadid 為空,jvm 讓其持有偏向鎖,并將 threadid 設(shè)置為其線程 id,再次進(jìn)入的時(shí)候會(huì)先判斷 threadid 是否與其線程 id 一致,如果一致則可以直接使用此對(duì)象,如果不一致,則升級(jí)偏向鎖為輕量級(jí)鎖,通過(guò)自旋循環(huán)一定次數(shù)來(lái)獲取鎖,執(zhí)行一定次數(shù)之后,如果還沒有正常獲取到要使用的對(duì)象,此時(shí)就會(huì)把鎖從輕量級(jí)升級(jí)為重量級(jí)鎖,此過(guò)程就構(gòu)成了 synchronized 鎖的升級(jí)。
鎖的升級(jí)的目的:鎖升級(jí)是為了減低了鎖帶來(lái)的性能消耗。在 Java 6 之后優(yōu)化 synchronized 的實(shí)現(xiàn)方式,使用了偏向鎖升級(jí)為輕量級(jí)鎖再升級(jí)到重量級(jí)鎖的方式,從而減低了鎖帶來(lái)的性能消耗。
49. 什么是死鎖?
當(dāng)線程 A 持有獨(dú)占鎖a,并嘗試去獲取獨(dú)占鎖 b 的同時(shí),線程 B 持有獨(dú)占鎖 b,并嘗試獲取獨(dú)占鎖 a 的情況下,就會(huì)發(fā)生 AB 兩個(gè)線程由于互相持有對(duì)方需要的鎖,而發(fā)生的阻塞現(xiàn)象,我們稱為死鎖。
50. 怎么防止死鎖?
盡量使用 tryLock(long timeout, TimeUnit unit)的方法(ReentrantLock、ReentrantReadWriteLock),設(shè)置超時(shí)時(shí)間,超時(shí)可以退出防止死鎖。
盡量使用 Java. util. concurrent 并發(fā)類代替自己手寫鎖。
盡量降低鎖的使用粒度,盡量不要幾個(gè)功能用同一把鎖。
盡量減少同步的代碼塊。
51. ThreadLocal 是什么?有哪些使用場(chǎng)景?
ThreadLocal 為每個(gè)使用該變量的線程提供獨(dú)立的變量副本,所以每一個(gè)線程都可以獨(dú)立地改變自己的副本,而不會(huì)影響其它線程所對(duì)應(yīng)的副本。
ThreadLocal 的經(jīng)典使用場(chǎng)景是數(shù)據(jù)庫(kù)連接和 session 管理等。
52. 說(shuō)一下 synchronized 底層實(shí)現(xiàn)原理?
synchronized 是由一對(duì) monitorenter/monitorexit 指令實(shí)現(xiàn)的,monitor 對(duì)象是同步的基本實(shí)現(xiàn)單元。在 Java 6 之前,monitor 的實(shí)現(xiàn)完全是依靠操作系統(tǒng)內(nèi)部的互斥鎖,因?yàn)樾枰M(jìn)行用戶態(tài)到內(nèi)核態(tài)的切換,所以同步操作是一個(gè)無(wú)差別的重量級(jí)操作,性能也很低。但在 Java 6 的時(shí)候,Java 虛擬機(jī) 對(duì)此進(jìn)行了大刀闊斧地改進(jìn),提供了三種不同的 monitor 實(shí)現(xiàn),也就是常說(shuō)的三種不同的鎖:偏向鎖(Biased Locking)、輕量級(jí)鎖和重量級(jí)鎖,大大改進(jìn)了其性能。
53. synchronized 和 volatile 的區(qū)別是什么?
volatile 是變量修飾符;synchronized 是修飾類、方法、代碼段。
volatile 僅能實(shí)現(xiàn)變量的修改可見性,不能保證原子性;而 synchronized 則可以保證變量的修改可見性和原子性。
volatile 不會(huì)造成線程的阻塞;synchronized 可能會(huì)造成線程的阻塞。
54. synchronized 和 Lock 有什么區(qū)別?
synchronized 可以給類、方法、代碼塊加鎖;而 lock 只能給代碼塊加鎖。
synchronized 不需要手動(dòng)獲取鎖和釋放鎖,使用簡(jiǎn)單,發(fā)生異常會(huì)自動(dòng)釋放鎖,不會(huì)造成死鎖;而 lock 需要自己加鎖和釋放鎖,如果使用不當(dāng)沒有 unLock()去釋放鎖就會(huì)造成死鎖。
通過(guò) Lock 可以知道有沒有成功獲取鎖,而 synchronized 卻無(wú)法辦到。
55. synchronized 和 ReentrantLock 區(qū)別是什么?
synchronized 早期的實(shí)現(xiàn)比較低效,對(duì)比 ReentrantLock,大多數(shù)場(chǎng)景性能都相差較大,但是在 Java 6 中對(duì) synchronized 進(jìn)行了非常多的改進(jìn)。
主要區(qū)別如下:
ReentrantLock 使用起來(lái)比較靈活,但是必須有釋放鎖的配合動(dòng)作;
ReentrantLock 必須手動(dòng)獲取與釋放鎖,而 synchronized 不需要手動(dòng)釋放和開啟鎖;
ReentrantLock 只適用于代碼塊鎖,而 synchronized 可用于修飾方法、代碼塊等。
56. 說(shuō)一下 atomic 的原理?
atomic 主要利用 CAS (Compare And Wwap) 和 volatile 和 native 方法來(lái)保證原子操作,從而避免 synchronized 的高開銷,執(zhí)行效率大為提升。
反射
57. 什么是反射?
反射是在運(yùn)行狀態(tài)中,對(duì)于任意一個(gè)類,都能夠知道這個(gè)類的所有屬性和方法;對(duì)于任意一個(gè)對(duì)象,都能夠調(diào)用它的任意一個(gè)方法和屬性;這種動(dòng)態(tài)獲取的信息以及動(dòng)態(tài)調(diào)用對(duì)象的方法的功能稱為 Java 語(yǔ)言的反射機(jī)制。
58. 什么是 Java 序列化?什么情況下需要序列化?
Java 序列化是為了保存各種對(duì)象在內(nèi)存中的狀態(tài),并且可以把保存的對(duì)象狀態(tài)再讀出來(lái)。
以下情況需要使用 Java 序列化:
想把的內(nèi)存中的對(duì)象狀態(tài)保存到一個(gè)文件中或者數(shù)據(jù)庫(kù)中時(shí)候;
想用套接字在網(wǎng)絡(luò)上傳送對(duì)象的時(shí)候;
想通過(guò)RMI(遠(yuǎn)程方法調(diào)用)傳輸對(duì)象的時(shí)候。
59. 動(dòng)態(tài)代理是什么?有哪些應(yīng)用?
動(dòng)態(tài)代理是運(yùn)行時(shí)動(dòng)態(tài)生成代理類。
動(dòng)態(tài)代理的應(yīng)用有 spring aop、hibernate 數(shù)據(jù)查詢、測(cè)試框架的后端 mock、rpc,Java注解對(duì)象獲取等。
60. 怎么實(shí)現(xiàn)動(dòng)態(tài)代理?
JDK 原生動(dòng)態(tài)代理和 cglib 動(dòng)態(tài)代理。JDK 原生動(dòng)態(tài)代理是基于接口實(shí)現(xiàn)的,而 cglib 是基于繼承當(dāng)前類的子類實(shí)現(xiàn)的。
對(duì)象拷貝
61. 為什么要使用克隆?
克隆的對(duì)象可能包含一些已經(jīng)修改過(guò)的屬性,而 new 出來(lái)的對(duì)象的屬性都還是初始化時(shí)候的值,所以當(dāng)需要一個(gè)新的對(duì)象來(lái)保存當(dāng)前對(duì)象的“狀態(tài)”就靠克隆方法了。
62. 如何實(shí)現(xiàn)對(duì)象克隆?
實(shí)現(xiàn) Cloneable 接口并重寫 Object 類中的 clone() 方法。
實(shí)現(xiàn) Serializable 接口,通過(guò)對(duì)象的序列化和反序列化實(shí)現(xiàn)克隆,可以實(shí)現(xiàn)真正的深度克隆。
63. 深拷貝和淺拷貝區(qū)別是什么?
淺克隆:當(dāng)對(duì)象被復(fù)制時(shí)只復(fù)制它本身和其中包含的值類型的成員變量,而引用類型的成員對(duì)象并沒有復(fù)制。
深克隆:除了對(duì)象本身被復(fù)制外,對(duì)象所包含的所有成員變量也將復(fù)制。
Java Web
64. JSP 和 servlet 有什么區(qū)別?
JSP 是 servlet 技術(shù)的擴(kuò)展,本質(zhì)上就是 servlet 的簡(jiǎn)易方式。servlet 和 JSP 最主要的不同點(diǎn)在于,servlet 的應(yīng)用邏輯是在 Java 文件中,并且完全從表示層中的 html 里分離開來(lái),而 JSP 的情況是 Java 和 html 可以組合成一個(gè)擴(kuò)展名為 JSP 的文件。JSP 側(cè)重于視圖,servlet 主要用于控制邏輯。
65. JSP 有哪些內(nèi)置對(duì)象?作用分別是什么?
JSP 有 9 大內(nèi)置對(duì)象:
request:封裝客戶端的請(qǐng)求,其中包含來(lái)自 get 或 post 請(qǐng)求的參數(shù);
response:封裝服務(wù)器對(duì)客戶端的響應(yīng);
pageContext:通過(guò)該對(duì)象可以獲取其他對(duì)象;
session:封裝用戶會(huì)話的對(duì)象;
application:封裝服務(wù)器運(yùn)行環(huán)境的對(duì)象;
out:輸出服務(wù)器響應(yīng)的輸出流對(duì)象;
config:Web 應(yīng)用的配置對(duì)象;
page:JSP 頁(yè)面本身(相當(dāng)于 Java 程序中的 this);
exception:封裝頁(yè)面拋出異常的對(duì)象。
66. 說(shuō)一下 JSP 的 4 種作用域?
page:代表與一個(gè)頁(yè)面相關(guān)的對(duì)象和屬性。
request:代表與客戶端發(fā)出的一個(gè)請(qǐng)求相關(guān)的對(duì)象和屬性。一個(gè)請(qǐng)求可能跨越多個(gè)頁(yè)面,涉及多個(gè) Web 組件;需要在頁(yè)面顯示的臨時(shí)數(shù)據(jù)可以置于此作用域。
session:代表與某個(gè)用戶與服務(wù)器建立的一次會(huì)話相關(guān)的對(duì)象和屬性。跟某個(gè)用戶相關(guān)的數(shù)據(jù)應(yīng)該放在用戶自己的 session 中。
application:代表與整個(gè) Web 應(yīng)用程序相關(guān)的對(duì)象和屬性,它實(shí)質(zhì)上是跨越整個(gè) Web 應(yīng)用程序,包括多個(gè)頁(yè)面、請(qǐng)求和會(huì)話的一個(gè)全局作用域。
67. session 和 cookie 有什么區(qū)別?
存儲(chǔ)位置不同:session 存儲(chǔ)在服務(wù)器端;cookie 存儲(chǔ)在瀏覽器端。
安全性不同:cookie 安全性一般,在瀏覽器存儲(chǔ),可以被偽造和修改。
容量和個(gè)數(shù)限制:cookie 有容量限制,每個(gè)站點(diǎn)下的 cookie 也有個(gè)數(shù)限制。
存儲(chǔ)的多樣性:session 可以存儲(chǔ)在 Redis 中、數(shù)據(jù)庫(kù)中、應(yīng)用程序中;而 cookie 只能存儲(chǔ)在瀏覽器中。
68. 說(shuō)一下 session 的工作原理?
session 的工作原理是客戶端登錄完成之后,服務(wù)器會(huì)創(chuàng)建對(duì)應(yīng)的 session,session 創(chuàng)建完之后,會(huì)把 session 的 id 發(fā)送給客戶端,客戶端再存儲(chǔ)到瀏覽器中。這樣客戶端每次訪問(wèn)服務(wù)器時(shí),都會(huì)帶著 sessionid,服務(wù)器拿到 sessionid 之后,在內(nèi)存找到與之對(duì)應(yīng)的 session 這樣就可以正常工作了。
69. 如果客戶端禁止 cookie 能實(shí)現(xiàn) session 還能用嗎?
可以用,session 只是依賴 cookie 存儲(chǔ) sessionid,如果 cookie 被禁用了,可以使用 url 中添加 sessionid 的方式保證 session 能正常使用。
70. spring mvc 和 struts 的區(qū)別是什么?
攔截級(jí)別:struts2 是類級(jí)別的攔截;spring mvc 是方法級(jí)別的攔截。
數(shù)據(jù)獨(dú)立性:spring mvc 的方法之間基本上獨(dú)立的,獨(dú)享 request 和 response 數(shù)據(jù),請(qǐng)求數(shù)據(jù)通過(guò)參數(shù)獲取,處理結(jié)果通過(guò) ModelMap 交回給框架,方法之間不共享變量;而 struts2 雖然方法之間也是獨(dú)立的,但其所有 action 變量是共享的,這不會(huì)影響程序運(yùn)行,卻給我們編碼和讀程序時(shí)帶來(lái)了一定的麻煩。
攔截機(jī)制:struts2 有以自己的 interceptor 機(jī)制,spring mvc 用的是獨(dú)立的 aop 方式,這樣導(dǎo)致struts2 的配置文件量比 spring mvc 大。
對(duì) ajax 的支持:spring mvc 集成了ajax,所有 ajax 使用很方便,只需要一個(gè)注解 @ResponseBody 就可以實(shí)現(xiàn)了;而 struts2 一般需要安裝插件或者自己寫代碼才行。
71. 如何避免 SQL 注入?
使用預(yù)處理 PreparedStatement。
使用正則表達(dá)式過(guò)濾掉字符中的特殊字符。
72. 什么是 XSS 攻擊,如何避免?
XSS 攻擊:即跨站腳本攻擊,它是 Web 程序中常見的漏洞。原理是攻擊者往 Web 頁(yè)面里插入惡意的腳本代碼(css 代碼、Javascript 代碼等),當(dāng)用戶瀏覽該頁(yè)面時(shí),嵌入其中的腳本代碼會(huì)被執(zhí)行,從而達(dá)到惡意攻擊用戶的目的,如盜取用戶 cookie、破壞頁(yè)面結(jié)構(gòu)、重定向到其他網(wǎng)站等。
預(yù)防 XSS 的核心是必須對(duì)輸入的數(shù)據(jù)做過(guò)濾處理。
73. 什么是 CSRF 攻擊,如何避免?
CSRF:Cross-Site Request Forgery(中文:跨站請(qǐng)求偽造),可以理解為攻擊者盜用了你的身份,以你的名義發(fā)送惡意請(qǐng)求,比如:以你名義發(fā)送郵件、發(fā)消息、購(gòu)買商品,虛擬貨幣轉(zhuǎn)賬等。
防御手段:
驗(yàn)證請(qǐng)求來(lái)源地址;
關(guān)鍵操作添加驗(yàn)證碼;
在請(qǐng)求地址添加 token 并驗(yàn)證。
異常
74. throw 和 throws 的區(qū)別?
throw:是真實(shí)拋出一個(gè)異常。
throws:是聲明可能會(huì)拋出一個(gè)異常。
75. final、finally、finalize 有什么區(qū)別?
final:是修飾符,如果修飾類,此類不能被繼承;如果修飾方法和變量,則表示此方法和此變量不能在被改變,只能使用。
finally:是 try{} catch{} finally{} 最后一部分,表示不論發(fā)生任何情況都會(huì)執(zhí)行,finally 部分可以省略,但如果 finally 部分存在,則一定會(huì)執(zhí)行 finally 里面的代碼。
finalize:是 Object 類的一個(gè)方法,在垃圾收集器執(zhí)行的時(shí)候會(huì)調(diào)用被回收對(duì)象的此方法。
76. try-catch-finally 中哪個(gè)部分可以省略?
try-catch-finally 其中 catch 和 finally 都可以被省略,但是不能同時(shí)省略,也就是說(shuō)有 try 的時(shí)候,必須后面跟一個(gè) catch 或者 finally。
77. try-catch-finally 中,如果 catch 中 return 了,finally 還會(huì)執(zhí)行嗎?
finally 一定會(huì)執(zhí)行,即使是 catch 中 return 了,catch 中的 return 會(huì)等 finally 中的代碼執(zhí)行完之后,才會(huì)執(zhí)行。
78. 常見的異常類有哪些?
NullPointerException 空指針異常
ClassNotFoundException 指定類不存在
NumberFormatException 字符串轉(zhuǎn)換為數(shù)字異常
IndexOutOfBoundsException 數(shù)組下標(biāo)越界異常
ClassCastException 數(shù)據(jù)類型轉(zhuǎn)換異常
FileNotFoundException 文件未找到異常
NoSuchMethodException 方法不存在異常
IOException IO 異常
SocketException Socket 異常
網(wǎng)絡(luò)
79. http 響應(yīng)碼 301 和 302 代表的是什么?有什么區(qū)別?
301:永久重定向。
302:暫時(shí)重定向。
它們的區(qū)別是,301 對(duì)搜索引擎優(yōu)化(SEO)更加有利;302 有被提示為網(wǎng)絡(luò)攔截的風(fēng)險(xiǎn)。
80. forward 和 redirect 的區(qū)別?
forward 是轉(zhuǎn)發(fā) 和 redirect 是重定向:
地址欄 url 顯示:foward url 不會(huì)發(fā)生改變,redirect url 會(huì)發(fā)生改變;
數(shù)據(jù)共享:forward 可以共享 request 里的數(shù)據(jù),redirect 不能共享;
效率:forward 比 redirect 效率高。
81. 簡(jiǎn)述 tcp 和 udp的區(qū)別?
tcp 和 udp 是 OSI 模型中的運(yùn)輸層中的協(xié)議。tcp 提供可靠的通信傳輸,而 udp 則常被用于讓廣播和細(xì)節(jié)控制交給應(yīng)用的通信傳輸。
兩者的區(qū)別大致如下:
tcp 面向連接,udp 面向非連接即發(fā)送數(shù)據(jù)前不需要建立鏈接;
tcp 提供可靠的服務(wù)(數(shù)據(jù)傳輸),udp 無(wú)法保證;
tcp 面向字節(jié)流,udp 面向報(bào)文;
tcp 數(shù)據(jù)傳輸慢,udp 數(shù)據(jù)傳輸快;
圖片
82. tcp 為什么要三次握手,兩次不行嗎?為什么?
如果采用兩次握手,那么只要服務(wù)器發(fā)出確認(rèn)數(shù)據(jù)包就會(huì)建立連接,但由于客戶端此時(shí)并未響應(yīng)服務(wù)器端的請(qǐng)求,那此時(shí)服務(wù)器端就會(huì)一直在等待客戶端,這樣服務(wù)器端就白白浪費(fèi)了一定的資源。若采用三次握手,服務(wù)器端沒有收到來(lái)自客戶端的再此確認(rèn),則就會(huì)知道客戶端并沒有要求建立請(qǐng)求,就不會(huì)浪費(fèi)服務(wù)器的資源。
83. 說(shuō)一下 tcp 粘包是怎么產(chǎn)生的?
tcp 粘包可能發(fā)生在發(fā)送端或者接收端,分別來(lái)看兩端各種產(chǎn)生粘包的原因:
發(fā)送端粘包:發(fā)送端需要等緩沖區(qū)滿才發(fā)送出去,造成粘包;
接收方粘包:接收方不及時(shí)接收緩沖區(qū)的包,造成多個(gè)包接收。
84. OSI 的七層模型都有哪些?
物理層:利用傳輸介質(zhì)為數(shù)據(jù)鏈路層提供物理連接,實(shí)現(xiàn)比特流的透明傳輸。
數(shù)據(jù)鏈路層:負(fù)責(zé)建立和管理節(jié)點(diǎn)間的鏈路。
網(wǎng)絡(luò)層:通過(guò)路由選擇算法,為報(bào)文或分組通過(guò)通信子網(wǎng)選擇最適當(dāng)?shù)穆窂健?/p>
傳輸層:向用戶提供可靠的端到端的差錯(cuò)和流量控制,保證報(bào)文的正確傳輸。
會(huì)話層:向兩個(gè)實(shí)體的表示層提供建立和使用連接的方法。
表示層:處理用戶信息的表示問(wèn)題,如編碼、數(shù)據(jù)格式轉(zhuǎn)換和加密解密等。
應(yīng)用層:直接向用戶提供服務(wù),完成用戶希望在網(wǎng)絡(luò)上完成的各種工作。
85. get 和 post 請(qǐng)求有哪些區(qū)別?
get 請(qǐng)求會(huì)被瀏覽器主動(dòng)緩存,而 post 不會(huì)。
get 傳遞參數(shù)有大小限制,而 post 沒有。
post 參數(shù)傳輸更安全,get 的參數(shù)會(huì)明文限制在 url 上,post 不會(huì)。
86. 如何實(shí)現(xiàn)跨域?
實(shí)現(xiàn)跨域有以下幾種方案:
服務(wù)器端運(yùn)行跨域 設(shè)置 CORS 等于 __;
在單個(gè)接口使用注解 @CrossOrigin 運(yùn)行跨域;
使用 jsonp 跨域;
87. 說(shuō)一下 JSONP 實(shí)現(xiàn)原理?
jsonp:JSON with Padding,它是利用script標(biāo)簽的 src 連接可以訪問(wèn)不同源的特性,加載遠(yuǎn)程返回的“JS 函數(shù)”來(lái)執(zhí)行的。
設(shè)計(jì)模式
88. 說(shuō)一下你熟悉的設(shè)計(jì)模式?
單例模式:保證被創(chuàng)建一次,節(jié)省系統(tǒng)開銷。
工廠模式(簡(jiǎn)單工廠、抽象工廠):解耦代碼。
觀察者模式:定義了對(duì)象之間的一對(duì)多的依賴,這樣一來(lái),當(dāng)一個(gè)對(duì)象改變時(shí),它的所有的依賴者都會(huì)收到通知并自動(dòng)更新。
外觀模式:提供一個(gè)統(tǒng)一的接口,用來(lái)訪問(wèn)子系統(tǒng)中的一群接口,外觀定義了一個(gè)高層的接口,讓子系統(tǒng)更容易使用。
模版方法模式:定義了一個(gè)算法的骨架,而將一些步驟延遲到子類中,模版方法使得子類可以在不改變算法結(jié)構(gòu)的情況下,重新定義算法的步驟。
狀態(tài)模式:允許對(duì)象在內(nèi)部狀態(tài)改變時(shí)改變它的行為,對(duì)象看起來(lái)好像修改了它的類。
89. 簡(jiǎn)單工廠和抽象工廠有什么區(qū)別?
簡(jiǎn)單工廠:用來(lái)生產(chǎn)同一等級(jí)結(jié)構(gòu)中的任意產(chǎn)品,對(duì)于增加新的產(chǎn)品,無(wú)能為力。
工廠方法:用來(lái)生產(chǎn)同一等級(jí)結(jié)構(gòu)中的固定產(chǎn)品,支持增加任意產(chǎn)品。
抽象工廠:用來(lái)生產(chǎn)不同產(chǎn)品族的全部產(chǎn)品,對(duì)于增加新的產(chǎn)品,無(wú)能為力;支持增加產(chǎn)品族。
Spring/Spring MVC
90. 為什么要使用 spring?
spring 提供 ioc 技術(shù),容器會(huì)幫你管理依賴的對(duì)象,從而不需要自己創(chuàng)建和管理依賴對(duì)象了,更輕松的實(shí)現(xiàn)了程序的解耦。
spring 提供了事務(wù)支持,使得事務(wù)操作變的更加方便。
spring 提供了面向切片編程,這樣可以更方便的處理某一類的問(wèn)題。
更方便的框架集成,spring 可以很方便的集成其他框架,比如 MyBatis、hibernate 等。
91. 解釋一下什么是 aop?
aop 是面向切面編程,通過(guò)預(yù)編譯方式和運(yùn)行期動(dòng)態(tài)代理實(shí)現(xiàn)程序功能的統(tǒng)一維護(hù)的一種技術(shù)。
簡(jiǎn)單來(lái)說(shuō)就是統(tǒng)一處理某一“切面”(類)的問(wèn)題的編程思想,比如統(tǒng)一處理日志、異常等。
92. 解釋一下什么是 ioc?
ioc:Inversionof Control(中文:控制反轉(zhuǎn))是 spring 的核心,對(duì)于 spring 框架來(lái)說(shuō),就是由 spring 來(lái)負(fù)責(zé)控制對(duì)象的生命周期和對(duì)象間的關(guān)系。
簡(jiǎn)單來(lái)說(shuō),控制指的是當(dāng)前對(duì)象對(duì)內(nèi)部成員的控制權(quán);控制反轉(zhuǎn)指的是,這種控制權(quán)不由當(dāng)前對(duì)象管理了,由其他(類,第三方容器)來(lái)管理。
93. spring 有哪些主要模塊?
spring core:框架的最基礎(chǔ)部分,提供 ioc 和依賴注入特性。
spring context:構(gòu)建于 core 封裝包基礎(chǔ)上的 context 封裝包,提供了一種框架式的對(duì)象訪問(wèn)方法。
spring dao:Data Access Object 提供了JDBC的抽象層。
spring aop:提供了面向切面的編程實(shí)現(xiàn),讓你可以自定義攔截器、切點(diǎn)等。
spring Web:提供了針對(duì) Web 開發(fā)的集成特性,例如文件上傳,利用 servlet listeners 進(jìn)行 ioc 容器初始化和針對(duì) Web 的 ApplicationContext。
spring Web mvc:spring 中的 mvc 封裝包提供了 Web 應(yīng)用的 Model-View-Controller(MVC)的實(shí)現(xiàn)。
94. spring 常用的注入方式有哪些?
setter 屬性注入
構(gòu)造方法注入
注解方式注入
95. spring 中的 bean 是線程安全的嗎?
spring 中的 bean 默認(rèn)是單例模式,spring 框架并沒有對(duì)單例 bean 進(jìn)行多線程的封裝處理。
實(shí)際上大部分時(shí)候 spring bean 無(wú)狀態(tài)的(比如 dao 類),所有某種程度上來(lái)說(shuō) bean 也是安全的,但如果 bean 有狀態(tài)的話(比如 view model 對(duì)象),那就要開發(fā)者自己去保證線程安全了,最簡(jiǎn)單的就是改變 bean 的作用域,把“singleton”變更為“prototype”,這樣請(qǐng)求 bean 相當(dāng)于 new Bean()了,所以就可以保證線程安全了。
有狀態(tài)就是有數(shù)據(jù)存儲(chǔ)功能。
無(wú)狀態(tài)就是不會(huì)保存數(shù)據(jù)。
96. spring 支持幾種 bean 的作用域?
spring 支持 5 種作用域,如下:
singleton:spring ioc 容器中只存在一個(gè) bean 實(shí)例,bean 以單例模式存在,是系統(tǒng)默認(rèn)值;
prototype:每次從容器調(diào)用 bean 時(shí)都會(huì)創(chuàng)建一個(gè)新的示例,既每次 getBean()相當(dāng)于執(zhí)行 new Bean()操作;
Web 環(huán)境下的作用域:
request:每次 http 請(qǐng)求都會(huì)創(chuàng)建一個(gè) bean;
session:同一個(gè) http session 共享一個(gè) bean 實(shí)例;
global-session:用于 portlet 容器,因?yàn)槊總€(gè) portlet 有單獨(dú)的 session,globalsession 提供一個(gè)全局性的 http session。
「注意:」 使用 prototype 作用域需要慎重的思考,因?yàn)轭l繁創(chuàng)建和銷毀 bean 會(huì)帶來(lái)很大的性能開銷。
97. spring 自動(dòng)裝配 bean 有哪些方式?
no:默認(rèn)值,表示沒有自動(dòng)裝配,應(yīng)使用顯式 bean 引用進(jìn)行裝配。
byName:它根據(jù) bean 的名稱注入對(duì)象依賴項(xiàng)。
byType:它根據(jù)類型注入對(duì)象依賴項(xiàng)。
構(gòu)造函數(shù):通過(guò)構(gòu)造函數(shù)來(lái)注入依賴項(xiàng),需要設(shè)置大量的參數(shù)。
autodetect:容器首先通過(guò)構(gòu)造函數(shù)使用 autowire 裝配,如果不能,則通過(guò) byType 自動(dòng)裝配。
98. spring 事務(wù)實(shí)現(xiàn)方式有哪些?
聲明式事務(wù):聲明式事務(wù)也有兩種實(shí)現(xiàn)方式,基于 xml 配置文件的方式和注解方式(在類上添加 @Transaction 注解)。
編碼方式:提供編碼的形式管理和維護(hù)事務(wù)。
99. 說(shuō)一下 spring 的事務(wù)隔離?
spring 有五大隔離級(jí)別,默認(rèn)值為 ISOLATION_DEFAULT(使用數(shù)據(jù)庫(kù)的設(shè)置),其他四個(gè)隔離級(jí)別和數(shù)據(jù)庫(kù)的隔離級(jí)別一致:
ISOLATION_DEFAULT:用底層數(shù)據(jù)庫(kù)的設(shè)置隔離級(jí)別,數(shù)據(jù)庫(kù)設(shè)置的是什么我就用什么;
ISOLATIONREADUNCOMMITTED:未提交讀,最低隔離級(jí)別、事務(wù)未提交前,就可被其他事務(wù)讀取(會(huì)出現(xiàn)幻讀、臟讀、不可重復(fù)讀);
ISOLATIONREADCOMMITTED:提交讀,一個(gè)事務(wù)提交后才能被其他事務(wù)讀取到(會(huì)造成幻讀、不可重復(fù)讀),SQL server 的默認(rèn)級(jí)別;
ISOLATIONREPEATABLEREAD:可重復(fù)讀,保證多次讀取同一個(gè)數(shù)據(jù)時(shí),其值都和事務(wù)開始時(shí)候的內(nèi)容是一致,禁止讀取到別的事務(wù)未提交的數(shù)據(jù)(會(huì)造成幻讀),MySQL 的默認(rèn)級(jí)別;
ISOLATION_SERIALIZABLE:序列化,代價(jià)最高最可靠的隔離級(jí)別,該隔離級(jí)別能防止臟讀、不可重復(fù)讀、幻讀。
「臟讀」 :表示一個(gè)事務(wù)能夠讀取另一個(gè)事務(wù)中還未提交的數(shù)據(jù)。比如,某個(gè)事務(wù)嘗試插入記錄 A,此時(shí)該事務(wù)還未提交,然后另一個(gè)事務(wù)嘗試讀取到了記錄 A。
「不可重復(fù)讀」 :是指在一個(gè)事務(wù)內(nèi),多次讀同一數(shù)據(jù)。
「幻讀」 :指同一個(gè)事務(wù)內(nèi)多次查詢返回的結(jié)果集不一樣。比如同一個(gè)事務(wù) A 第一次查詢時(shí)候有 n 條記錄,但是第二次同等條件下查詢卻有 n+1 條記錄,這就好像產(chǎn)生了幻覺。發(fā)生幻讀的原因也是另外一個(gè)事務(wù)新增或者刪除或者修改了第一個(gè)事務(wù)結(jié)果集里面的數(shù)據(jù),同一個(gè)記錄的數(shù)據(jù)內(nèi)容被修改了,所有數(shù)據(jù)行的記錄就變多或者變少了。
100. 說(shuō)一下 spring mvc 運(yùn)行流程?
spring mvc 先將請(qǐng)求發(fā)送給 DispatcherServlet。
DispatcherServlet 查詢一個(gè)或多個(gè) HandlerMapping,找到處理請(qǐng)求的 Controller。
DispatcherServlet 再把請(qǐng)求提交到對(duì)應(yīng)的 Controller。
Controller 進(jìn)行業(yè)務(wù)邏輯處理后,會(huì)返回一個(gè)ModelAndView。
Dispathcher 查詢一個(gè)或多個(gè) ViewResolver 視圖解析器,找到 ModelAndView 對(duì)象指定的視圖對(duì)象。
視圖對(duì)象負(fù)責(zé)渲染返回給客戶端。
101. spring mvc 有哪些組件?
前置控制器 DispatcherServlet。
映射控制器 HandlerMapping。
處理器 Controller。
模型和視圖 ModelAndView。
視圖解析器 ViewResolver。
102. @RequestMapping 的作用是什么?
將 http 請(qǐng)求映射到相應(yīng)的類/方法上。
103. @Autowired 的作用是什么?
@Autowired 它可以對(duì)類成員變量、方法及構(gòu)造函數(shù)進(jìn)行標(biāo)注,完成自動(dòng)裝配的工作,通過(guò)@Autowired 的使用來(lái)消除 set/get 方法。
Spring Boot/Spring Cloud
104. 什么是 spring boot?
spring boot 是為 spring 服務(wù)的,是用來(lái)簡(jiǎn)化新 spring 應(yīng)用的初始搭建以及開發(fā)過(guò)程的。
105. 為什么要用 spring boot?
配置簡(jiǎn)單
獨(dú)立運(yùn)行
自動(dòng)裝配
無(wú)代碼生成和 xml 配置
提供應(yīng)用監(jiān)控
易上手
提升開發(fā)效率
106. spring boot 核心配置文件是什么?
spring boot 核心的兩個(gè)配置文件:
bootstrap (. yml 或者 . properties):boostrap 由父 ApplicationContext 加載的,比 applicaton 優(yōu)先加載,且 boostrap 里面的屬性不能被覆蓋;
application (. yml 或者 . properties):用于 spring boot 項(xiàng)目的自動(dòng)化配置。
107. spring boot 配置文件有哪幾種類型?它們有什么區(qū)別?
配置文件有 . properties 格式和 . yml 格式,它們主要的區(qū)別是書法風(fēng)格不同。
. properties 配置如下:
spring.RabbitMQ.port=5672
. yml 配置如下:
spring:
RabbitMQ:
port: 5672
yml 格式不支持 @PropertySource 注解導(dǎo)入。
108. spring boot 有哪些方式可以實(shí)現(xiàn)熱部署?
使用 devtools 啟動(dòng)熱部署,添加 devtools 庫(kù),在配置文件中把 spring. devtools. restart. enabled 設(shè)置為 true;
使用 Intellij Idea 編輯器,勾上自動(dòng)編譯或手動(dòng)重新編譯。
109. jpa 和 hibernate 有什么區(qū)別?
jpa 全稱 Java Persistence API,是 Java 持久化接口規(guī)范,hibernate 屬于 jpa 的具體實(shí)現(xiàn)。
110. 什么是 spring cloud?
spring cloud 是一系列框架的有序集合。它利用 spring boot 的開發(fā)便利性巧妙地簡(jiǎn)化了分布式系統(tǒng)基礎(chǔ)設(shè)施的開發(fā),如服務(wù)發(fā)現(xiàn)注冊(cè)、配置中心、消息總線、負(fù)載均衡、斷路器、數(shù)據(jù)監(jiān)控等,都可以用 spring boot 的開發(fā)風(fēng)格做到一鍵啟動(dòng)和部署。
111. spring cloud 斷路器的作用是什么?
在分布式架構(gòu)中,斷路器模式的作用也是類似的,當(dāng)某個(gè)服務(wù)單元發(fā)生故障(類似用電器發(fā)生短路)之后,通過(guò)斷路器的故障監(jiān)控(類似熔斷保險(xiǎn)絲),向調(diào)用方返回一個(gè)錯(cuò)誤響應(yīng),而不是長(zhǎng)時(shí)間的等待。這樣就不會(huì)使得線程因調(diào)用故障服務(wù)被長(zhǎng)時(shí)間占用不釋放,避免了故障在分布式系統(tǒng)中的蔓延。
112. spring cloud 的核心組件有哪些?
Eureka:服務(wù)注冊(cè)于發(fā)現(xiàn)。
Feign:基于動(dòng)態(tài)代理機(jī)制,根據(jù)注解和選擇的機(jī)器,拼接請(qǐng)求 url 地址,發(fā)起請(qǐng)求。
Ribbon:實(shí)現(xiàn)負(fù)載均衡,從一個(gè)服務(wù)的多臺(tái)機(jī)器中選擇一臺(tái)。
Hystrix:提供線程池,不同的服務(wù)走不同的線程池,實(shí)現(xiàn)了不同服務(wù)調(diào)用的隔離,避免了服務(wù)雪崩的問(wèn)題。
Zuul:網(wǎng)關(guān)管理,由 Zuul 網(wǎng)關(guān)轉(zhuǎn)發(fā)請(qǐng)求給對(duì)應(yīng)的服務(wù)。
Hibernate
113. 為什么要使用 hibernate?
hibernate 是對(duì) jdbc 的封裝,大大簡(jiǎn)化了數(shù)據(jù)訪問(wèn)層的繁瑣的重復(fù)性代碼。
hibernate 是一個(gè)優(yōu)秀的 ORM 實(shí)現(xiàn),很多程度上簡(jiǎn)化了 DAO 層的編碼功能。
可以很方便的進(jìn)行數(shù)據(jù)庫(kù)的移植工作。
提供了緩存機(jī)制,是程序執(zhí)行更改的高效。
114. 什么是 ORM 框架?
ORM(Object Relation Mapping)對(duì)象關(guān)系映射,是把數(shù)據(jù)庫(kù)中的關(guān)系數(shù)據(jù)映射成為程序中的對(duì)象。
使用 ORM 的優(yōu)點(diǎn):提高了開發(fā)效率降低了開發(fā)成本、開發(fā)更簡(jiǎn)單更對(duì)象化、可移植更強(qiáng)。
115. hibernate 中如何在控制臺(tái)查看打印的 SQL 語(yǔ)句?
在 Config 里面把 hibernate. show_SQL 設(shè)置為 true 就可以。但不建議開啟,開啟之后會(huì)降低程序的運(yùn)行效率。
116. hibernate 有幾種查詢方式?
三種:hql、原生 SQL、條件查詢 Criteria。
117. hibernate 實(shí)體類可以被定義為 final 嗎?
實(shí)體類可以定義為 final 類,但這樣的話就不能使用 hibernate 代理模式下的延遲關(guān)聯(lián)提供性能了,所以不建議定義實(shí)體類為 final。
118. 在 hibernate 中使用 Integer 和 int 做映射有什么區(qū)別?
Integer 類型為對(duì)象,它的值允許為 null,而 int 屬于基礎(chǔ)數(shù)據(jù)類型,值不能為 null。
119. hibernate 是如何工作的?
讀取并解析配置文件。
讀取并解析映射文件,創(chuàng)建 SessionFactory。
打開 Session。
創(chuàng)建事務(wù)。
進(jìn)行持久化操作。
提交事務(wù)。
關(guān)閉 Session。
關(guān)閉 SessionFactory。
120. get()和 load()的區(qū)別?
數(shù)據(jù)查詢時(shí),沒有 OID 指定的對(duì)象,get() 返回 null;load() 返回一個(gè)代理對(duì)象。
load()支持延遲加載;get() 不支持延遲加載。
121. 說(shuō)一下 hibernate 的緩存機(jī)制?
hibernate 常用的緩存有一級(jí)緩存和二級(jí)緩存:
一級(jí)緩存:也叫 Session 緩存,只在 Session 作用范圍內(nèi)有效,不需要用戶干涉,由 hibernate 自身維護(hù),可以通過(guò):evict(object)清除 object 的緩存;clear()清除一級(jí)緩存中的所有緩存;flush()刷出緩存;
二級(jí)緩存:應(yīng)用級(jí)別的緩存,在所有 Session 中都有效,支持配置第三方的緩存,如:EhCache。
122. hibernate 對(duì)象有哪些狀態(tài)?
臨時(shí)/瞬時(shí)狀態(tài):直接 new 出來(lái)的對(duì)象,該對(duì)象還沒被持久化(沒保存在數(shù)據(jù)庫(kù)中),不受 Session 管理。
持久化狀態(tài):當(dāng)調(diào)用 Session 的 save/saveOrupdate/get/load/list 等方法的時(shí)候,對(duì)象就是持久化狀態(tài)。
游離狀態(tài):Session 關(guān)閉之后對(duì)象就是游離狀態(tài)。
123. 在 hibernate 中 getCurrentSession 和 openSession 的區(qū)別是什么?
getCurrentSession 會(huì)綁定當(dāng)前線程,而 openSession 則不會(huì)。
getCurrentSession 事務(wù)是 Spring 控制的,并且不需要手動(dòng)關(guān)閉,而 openSession 需要我們自己手動(dòng)開啟和提交事務(wù)。
124. hibernate 實(shí)體類必須要有無(wú)參構(gòu)造函數(shù)嗎?為什么?
hibernate 中每個(gè)實(shí)體類必須提供一個(gè)無(wú)參構(gòu)造函數(shù),因?yàn)?hibernate 框架要使用 reflection api,通過(guò)調(diào)用 ClassnewInstance() 來(lái)創(chuàng)建實(shí)體類的實(shí)例,如果沒有無(wú)參的構(gòu)造函數(shù)就會(huì)拋出異常。
MyBatis
125. MyBatis 中 #{}和 ${}的區(qū)別是什么?
\#{}是預(yù)編譯處理,${}是字符替換。在使用 #{}時(shí),MyBatis 會(huì)將 SQL 中的 #{}替換成“?”,配合 PreparedStatement 的 set 方法賦值,這樣可以有效的防止 SQL 注入,保證程序的運(yùn)行安全。
126. MyBatis 有幾種分頁(yè)方式?
分頁(yè)方式:邏輯分頁(yè)和物理分頁(yè)。
「邏輯分頁(yè):」 使用 MyBatis 自帶的 RowBounds 進(jìn)行分頁(yè),它是一次性查詢很多數(shù)據(jù),然后在數(shù)據(jù)中再進(jìn)行檢索。
「物理分頁(yè):」 自己手寫 SQL 分頁(yè)或使用分頁(yè)插件 PageHelper,去數(shù)據(jù)庫(kù)查詢指定條數(shù)的分頁(yè)數(shù)據(jù)的形式。
127. RowBounds 是一次性查詢?nèi)拷Y(jié)果嗎?為什么?
RowBounds 表面是在“所有”數(shù)據(jù)中檢索數(shù)據(jù),其實(shí)并非是一次性查詢出所有數(shù)據(jù),因?yàn)?MyBatis 是對(duì) jdbc 的封裝,在 jdbc 驅(qū)動(dòng)中有一個(gè) Fetch Size 的配置,它規(guī)定了每次最多從數(shù)據(jù)庫(kù)查詢多少條數(shù)據(jù),假如你要查詢更多數(shù)據(jù),它會(huì)在你執(zhí)行 next()的時(shí)候,去查詢更多的數(shù)據(jù)。就好比你去自動(dòng)取款機(jī)取 10000 元,但取款機(jī)每次最多能取 2500 元,所以你要取 4 次才能把錢取完。只是對(duì)于 jdbc 來(lái)說(shuō),當(dāng)你調(diào)用 next()的時(shí)候會(huì)自動(dòng)幫你完成查詢工作。這樣做的好處可以有效的防止內(nèi)存溢出。
128. MyBatis 邏輯分頁(yè)和物理分頁(yè)的區(qū)別是什么?
邏輯分頁(yè)是一次性查詢很多數(shù)據(jù),然后再在結(jié)果中檢索分頁(yè)的數(shù)據(jù)。這樣做弊端是需要消耗大量的內(nèi)存、有內(nèi)存溢出的風(fēng)險(xiǎn)、對(duì)數(shù)據(jù)庫(kù)壓力較大。
物理分頁(yè)是從數(shù)據(jù)庫(kù)查詢指定條數(shù)的數(shù)據(jù),彌補(bǔ)了一次性全部查出的所有數(shù)據(jù)的種種缺點(diǎn),比如需要大量的內(nèi)存,對(duì)數(shù)據(jù)庫(kù)查詢壓力較大等問(wèn)題。
129. MyBatis 是否支持延遲加載?延遲加載的原理是什么?
MyBatis 支持延遲加載,設(shè)置 lazyLoadingEnabled=true 即可。
延遲加載的原理的是調(diào)用的時(shí)候觸發(fā)加載,而不是在初始化的時(shí)候就加載信息。比如調(diào)用 a. getB(). getName(),這個(gè)時(shí)候發(fā)現(xiàn) a. getB() 的值為 null,此時(shí)會(huì)單獨(dú)觸發(fā)事先保存好的關(guān)聯(lián) B 對(duì)象的 SQL,先查詢出來(lái) B,然后再調(diào)用 a. setB(b),而這時(shí)候再調(diào)用 a. getB(). getName() 就有值了,這就是延遲加載的基本原理。
130. 說(shuō)一下 MyBatis 的一級(jí)緩存和二級(jí)緩存?
一級(jí)緩存:基于 PerpetualCache 的 HashMap 本地緩存,它的聲明周期是和 SQLSession 一致的,有多個(gè) SQLSession 或者分布式的環(huán)境中數(shù)據(jù)庫(kù)操作,可能會(huì)出現(xiàn)臟數(shù)據(jù)。當(dāng) Session flush 或 close 之后,該 Session 中的所有 Cache 就將清空,默認(rèn)一級(jí)緩存是開啟的。
二級(jí)緩存:也是基于 PerpetualCache 的 HashMap 本地緩存,不同在于其存儲(chǔ)作用域?yàn)?Mapper 級(jí)別的,如果多個(gè)SQLSession之間需要共享緩存,則需要使用到二級(jí)緩存,并且二級(jí)緩存可自定義存儲(chǔ)源,如 Ehcache。默認(rèn)不打開二級(jí)緩存,要開啟二級(jí)緩存,使用二級(jí)緩存屬性類需要實(shí)現(xiàn) Serializable 序列化接口(可用來(lái)保存對(duì)象的狀態(tài))。
開啟二級(jí)緩存數(shù)據(jù)查詢流程:二級(jí)緩存 -> 一級(jí)緩存 -> 數(shù)據(jù)庫(kù)。
緩存更新機(jī)制:當(dāng)某一個(gè)作用域(一級(jí)緩存 Session/二級(jí)緩存 Mapper)進(jìn)行了C/U/D 操作后,默認(rèn)該作用域下所有 select 中的緩存將被 clear。
131. MyBatis 和 hibernate 的區(qū)別有哪些?
靈活性:MyBatis 更加靈活,自己可以寫 SQL 語(yǔ)句,使用起來(lái)比較方便。
可移植性:MyBatis 有很多自己寫的 SQL,因?yàn)槊總€(gè)數(shù)據(jù)庫(kù)的 SQL 可以不相同,所以可移植性比較差。
學(xué)習(xí)和使用門檻:MyBatis 入門比較簡(jiǎn)單,使用門檻也更低。
二級(jí)緩存:hibernate 擁有更好的二級(jí)緩存,它的二級(jí)緩存可以自行更換為第三方的二級(jí)緩存。
132. MyBatis 有哪些執(zhí)行器(Executor)?
MyBatis 有三種基本的Executor執(zhí)行器:
SimpleExecutor:每執(zhí)行一次 update 或 select 就開啟一個(gè) Statement 對(duì)象,用完立刻關(guān)閉 Statement 對(duì)象;
ReuseExecutor:執(zhí)行 update 或 select,以 SQL 作為 key 查找 Statement 對(duì)象,存在就使用,不存在就創(chuàng)建,用完后不關(guān)閉 Statement 對(duì)象,而是放置于 Map 內(nèi)供下一次使用。簡(jiǎn)言之,就是重復(fù)使用 Statement 對(duì)象;
BatchExecutor:執(zhí)行 update(沒有 select,jdbc 批處理不支持 select),將所有 SQL 都添加到批處理中(addBatch()),等待統(tǒng)一執(zhí)行(executeBatch()),它緩存了多個(gè) Statement 對(duì)象,每個(gè) Statement 對(duì)象都是 addBatch()完畢后,等待逐一執(zhí)行 executeBatch()批處理,與 jdbc 批處理相同。
133. MyBatis 分頁(yè)插件的實(shí)現(xiàn)原理是什么?
分頁(yè)插件的基本原理是使用 MyBatis 提供的插件接口,實(shí)現(xiàn)自定義插件,在插件的攔截方法內(nèi)攔截待執(zhí)行的 SQL,然后重寫 SQL,根據(jù) dialect 方言,添加對(duì)應(yīng)的物理分頁(yè)語(yǔ)句和物理分頁(yè)參數(shù)。
134. MyBatis 如何編寫一個(gè)自定義插件?
「自定義插件實(shí)現(xiàn)原理」
MyBatis 自定義插件針對(duì) MyBatis 四大對(duì)象(Executor、StatementHandler、ParameterHandler、ResultSetHandler)進(jìn)行攔截:
Executor:攔截內(nèi)部執(zhí)行器,它負(fù)責(zé)調(diào)用 StatementHandler 操作數(shù)據(jù)庫(kù),并把結(jié)果集通過(guò) ResultSetHandler 進(jìn)行自動(dòng)映射,另外它還處理了二級(jí)緩存的操作;
StatementHandler:攔截 SQL 語(yǔ)法構(gòu)建的處理,它是 MyBatis 直接和數(shù)據(jù)庫(kù)執(zhí)行 SQL 腳本的對(duì)象,另外它也實(shí)現(xiàn)了 MyBatis 的一級(jí)緩存;
ParameterHandler:攔截參數(shù)的處理;
ResultSetHandler:攔截結(jié)果集的處理。
「自定義插件實(shí)現(xiàn)關(guān)鍵」
MyBatis 插件要實(shí)現(xiàn) Interceptor 接口,接口包含的方法,如下:
public interfaceInterceptor{
Object intercept(Invocation invocation)throws Throwable;
Object plugin(Object target);
voidsetProperties(Properties properties);
}
setProperties 方法是在 MyBatis 進(jìn)行配置插件的時(shí)候可以配置自定義相關(guān)屬性,即:接口實(shí)現(xiàn)對(duì)象的參數(shù)配置;
plugin 方法是插件用于封裝目標(biāo)對(duì)象的,通過(guò)該方法我們可以返回目標(biāo)對(duì)象本身,也可以返回一個(gè)它的代理,可以決定是否要進(jìn)行攔截進(jìn)而決定要返回一個(gè)什么樣的目標(biāo)對(duì)象,官方提供了示例:return Plugin. wrap(target, this);
intercept 方法就是要進(jìn)行攔截的時(shí)候要執(zhí)行的方法。
「自定義插件實(shí)現(xiàn)示例」
官方插件實(shí)現(xiàn):
@Intercepts({@Signature(type = Executor. class, method= "query",
args = {MappedStatement. class, Object. class, RowBounds. class, ResultHandler. class})})
publicclassTestInterceptorimplementsInterceptor{
public Object intercept(Invocation invocation)throws Throwable {
Object target = invocation. getTarget(); //被代理對(duì)象
Method method = invocation. getMethod(); //代理方法
Object[] args = invocation. getArgs(); //方法參數(shù)
// do something . . . . . . 方法攔截前執(zhí)行代碼塊
Object result = invocation. proceed();
// do something . . . . . . . 方法攔截后執(zhí)行代碼塊
return result;
}
public Object plugin(Object target){
return Plugin. wrap(target, this);
}
}
RabbitMQ
135. RabbitMQ 的使用場(chǎng)景有哪些?
搶購(gòu)活動(dòng),削峰填谷,防止系統(tǒng)崩塌。
延遲信息處理,比如 10 分鐘之后給下單未付款的用戶發(fā)送郵件提醒。
解耦系統(tǒng),對(duì)于新增的功能可以單獨(dú)寫模塊擴(kuò)展,比如用戶確認(rèn)評(píng)價(jià)之后,新增了給用戶返積分的功能,這個(gè)時(shí)候不用在業(yè)務(wù)代碼里添加新增積分的功能,只需要把新增積分的接口訂閱確認(rèn)評(píng)價(jià)的消息隊(duì)列即可,后面再添加任何功能只需要訂閱對(duì)應(yīng)的消息隊(duì)列即可。
136. RabbitMQ 有哪些重要的角色?
RabbitMQ 中重要的角色有:生產(chǎn)者、消費(fèi)者和代理:
生產(chǎn)者:消息的創(chuàng)建者,負(fù)責(zé)創(chuàng)建和推送數(shù)據(jù)到消息服務(wù)器;
消費(fèi)者:消息的接收方,用于處理數(shù)據(jù)和確認(rèn)消息;
代理:就是 RabbitMQ 本身,用于扮演“快遞”的角色,本身不生產(chǎn)消息,只是扮演“快遞”的角色。
137. RabbitMQ 有哪些重要的組件?
ConnectionFactory(連接管理器):應(yīng)用程序與Rabbit之間建立連接的管理器,程序代碼中使用。
Channel(信道):消息推送使用的通道。
Exchange(交換器):用于接受、分配消息。
Queue(隊(duì)列):用于存儲(chǔ)生產(chǎn)者的消息。
RoutingKey(路由鍵):用于把生成者的數(shù)據(jù)分配到交換器上。
BindingKey(綁定鍵):用于把交換器的消息綁定到隊(duì)列上。
138. RabbitMQ 中 vhost 的作用是什么?
vhost:每個(gè) RabbitMQ 都能創(chuàng)建很多 vhost,我們稱之為虛擬主機(jī),每個(gè)虛擬主機(jī)其實(shí)都是 mini 版的RabbitMQ,它擁有自己的隊(duì)列,交換器和綁定,擁有自己的權(quán)限機(jī)制。
139. RabbitMQ 的消息是怎么發(fā)送的?
首先客戶端必須連接到 RabbitMQ 服務(wù)器才能發(fā)布和消費(fèi)消息,客戶端和 rabbit server 之間會(huì)創(chuàng)建一個(gè) tcp 連接,一旦 tcp 打開并通過(guò)了認(rèn)證(認(rèn)證就是你發(fā)送給 rabbit 服務(wù)器的用戶名和密碼),你的客戶端和 RabbitMQ 就創(chuàng)建了一條 amqp 信道(channel),信道是創(chuàng)建在“真實(shí)” tcp 上的虛擬連接,amqp 命令都是通過(guò)信道發(fā)送出去的,每個(gè)信道都會(huì)有一個(gè)唯一的 id,不論是發(fā)布消息,訂閱隊(duì)列都是通過(guò)這個(gè)信道完成的。
140. RabbitMQ 怎么保證消息的穩(wěn)定性?
提供了事務(wù)的功能。
通過(guò)將 channel 設(shè)置為 confirm(確認(rèn))模式。
141. RabbitMQ 怎么避免消息丟失?
把消息持久化磁盤,保證服務(wù)器重啟消息不丟失。
每個(gè)集群中至少有一個(gè)物理磁盤,保證消息落入磁盤。
142. 要保證消息持久化成功的條件有哪些?
聲明隊(duì)列必須設(shè)置持久化 durable 設(shè)置為 true.
消息推送投遞模式必須設(shè)置持久化,deliveryMode 設(shè)置為 2(持久)。
消息已經(jīng)到達(dá)持久化交換器。
消息已經(jīng)到達(dá)持久化隊(duì)列。
以上四個(gè)條件都滿足才能保證消息持久化成功。
143. RabbitMQ 持久化有什么缺點(diǎn)?
持久化的缺地就是降低了服務(wù)器的吞吐量,因?yàn)槭褂玫氖谴疟P而非內(nèi)存存儲(chǔ),從而降低了吞吐量??杀M量使用 ssd 硬盤來(lái)緩解吞吐量的問(wèn)題。
144. RabbitMQ 有幾種廣播類型?
direct(默認(rèn)方式):最基礎(chǔ)最簡(jiǎn)單的模式,發(fā)送方把消息發(fā)送給訂閱方,如果有多個(gè)訂閱者,默認(rèn)采取輪詢的方式進(jìn)行消息發(fā)送。
headers:與 direct 類似,只是性能很差,此類型幾乎用不到。
fanout:分發(fā)模式,把消費(fèi)分發(fā)給所有訂閱者。
topic:匹配訂閱模式,使用正則匹配到消息隊(duì)列,能匹配到的都能接收到。
145. RabbitMQ 怎么實(shí)現(xiàn)延遲消息隊(duì)列?
延遲隊(duì)列的實(shí)現(xiàn)有兩種方式:
通過(guò)消息過(guò)期后進(jìn)入死信交換器,再由交換器轉(zhuǎn)發(fā)到延遲消費(fèi)隊(duì)列,實(shí)現(xiàn)延遲功能;
使用 RabbitMQ-delayed-message-exchange 插件實(shí)現(xiàn)延遲功能。
146. RabbitMQ 集群有什么用?
集群主要有以下兩個(gè)用途:
高可用:某個(gè)服務(wù)器出現(xiàn)問(wèn)題,整個(gè) RabbitMQ 還可以繼續(xù)使用;
高容量:集群可以承載更多的消息量。
147. RabbitMQ 節(jié)點(diǎn)的類型有哪些?
磁盤節(jié)點(diǎn):消息會(huì)存儲(chǔ)到磁盤。
內(nèi)存節(jié)點(diǎn):消息都存儲(chǔ)在內(nèi)存中,重啟服務(wù)器消息丟失,性能高于磁盤類型。
148. RabbitMQ 集群搭建需要注意哪些問(wèn)題?
各節(jié)點(diǎn)之間使用“--link”連接,此屬性不能忽略。
各節(jié)點(diǎn)使用的 erlang cookie 值必須相同,此值相當(dāng)于“秘鑰”的功能,用于各節(jié)點(diǎn)的認(rèn)證。
整個(gè)集群中必須包含一個(gè)磁盤節(jié)點(diǎn)。
149. RabbitMQ 每個(gè)節(jié)點(diǎn)是其他節(jié)點(diǎn)的完整拷貝嗎?為什么?
不是,原因有以下兩個(gè):
存儲(chǔ)空間的考慮:如果每個(gè)節(jié)點(diǎn)都擁有所有隊(duì)列的完全拷貝,這樣新增節(jié)點(diǎn)不但沒有新增存儲(chǔ)空間,反而增加了更多的冗余數(shù)據(jù);
性能的考慮:如果每條消息都需要完整拷貝到每一個(gè)集群節(jié)點(diǎn),那新增節(jié)點(diǎn)并沒有提升處理消息的能力,最多是保持和單節(jié)點(diǎn)相同的性能甚至是更糟。
150. RabbitMQ 集群中唯一一個(gè)磁盤節(jié)點(diǎn)崩潰了會(huì)發(fā)生什么情況?
如果唯一磁盤的磁盤節(jié)點(diǎn)崩潰了,不能進(jìn)行以下操作:
不能創(chuàng)建隊(duì)列
不能創(chuàng)建交換器
不能創(chuàng)建綁定
不能添加用戶
不能更改權(quán)限
不能添加和刪除集群節(jié)點(diǎn)
唯一磁盤節(jié)點(diǎn)崩潰了,集群是可以保持運(yùn)行的,但你不能更改任何東西。
151. RabbitMQ 對(duì)集群節(jié)點(diǎn)停止順序有要求嗎?
RabbitMQ 對(duì)集群的停止的順序是有要求的,應(yīng)該先關(guān)閉內(nèi)存節(jié)點(diǎn),最后再關(guān)閉磁盤節(jié)點(diǎn)。如果順序恰好相反的話,可能會(huì)造成消息的丟失。
Kafka
152. kafka 可以脫離 zookeeper 單獨(dú)使用嗎?為什么?
kafka 不能脫離 zookeeper 單獨(dú)使用,因?yàn)?kafka 使用 zookeeper 管理和協(xié)調(diào) kafka 的節(jié)點(diǎn)服務(wù)器。
153. kafka 有幾種數(shù)據(jù)保留的策略?
kafka 有兩種數(shù)據(jù)保存策略:按照過(guò)期時(shí)間保留和按照存儲(chǔ)的消息大小保留。
154. kafka 同時(shí)設(shè)置了 7 天和 10G 清除數(shù)據(jù),到第五天的時(shí)候消息達(dá)到了 10G,這個(gè)時(shí)候 kafka 將如何處理?
這個(gè)時(shí)候 kafka 會(huì)執(zhí)行數(shù)據(jù)清除工作,時(shí)間和大小不論那個(gè)滿足條件,都會(huì)清空數(shù)據(jù)。
155. 什么情況會(huì)導(dǎo)致 kafka 運(yùn)行變慢?
cpu 性能瓶頸
磁盤讀寫瓶頸
網(wǎng)絡(luò)瓶頸
156. 使用 kafka 集群需要注意什么?
集群的數(shù)量不是越多越好,最好不要超過(guò) 7 個(gè),因?yàn)楣?jié)點(diǎn)越多,消息復(fù)制需要的時(shí)間就越長(zhǎng),整個(gè)群組的吞吐量就越低。
集群數(shù)量最好是單數(shù),因?yàn)槌^(guò)一半故障集群就不能用了,設(shè)置為單數(shù)容錯(cuò)率更高。
Zookeeper
157. zookeeper 是什么?
zookeeper 是一個(gè)分布式的,開放源碼的分布式應(yīng)用程序協(xié)調(diào)服務(wù),是 google chubby 的開源實(shí)現(xiàn),是 hadoop 和 hbase 的重要組件。它是一個(gè)為分布式應(yīng)用提供一致性服務(wù)的軟件,提供的功能包括:配置維護(hù)、域名服務(wù)、分布式同步、組服務(wù)等。
158. zookeeper 都有哪些功能?
集群管理:監(jiān)控節(jié)點(diǎn)存活狀態(tài)、運(yùn)行請(qǐng)求等。
主節(jié)點(diǎn)選舉:主節(jié)點(diǎn)掛掉了之后可以從備用的節(jié)點(diǎn)開始新一輪選主,主節(jié)點(diǎn)選舉說(shuō)的就是這個(gè)選舉的過(guò)程,使用 zookeeper 可以協(xié)助完成這個(gè)過(guò)程。
分布式鎖:zookeeper 提供兩種鎖:獨(dú)占鎖、共享鎖。獨(dú)占鎖即一次只能有一個(gè)線程使用資源,共享鎖是讀鎖共享,讀寫互斥,即可以有多線線程同時(shí)讀同一個(gè)資源,如果要使用寫鎖也只能有一個(gè)線程使用。zookeeper可以對(duì)分布式鎖進(jìn)行控制。
命名服務(wù):在分布式系統(tǒng)中,通過(guò)使用命名服務(wù),客戶端應(yīng)用能夠根據(jù)指定名字來(lái)獲取資源或服務(wù)的地址,提供者等信息。
159. zookeeper 有幾種部署模式?
zookeeper 有三種部署模式:
單機(jī)部署:一臺(tái)集群上運(yùn)行;
集群部署:多臺(tái)集群運(yùn)行;
偽集群部署:一臺(tái)集群?jiǎn)?dòng)多個(gè) zookeeper 實(shí)例運(yùn)行。
160. zookeeper 怎么保證主從節(jié)點(diǎn)的狀態(tài)同步?
zookeeper 的核心是原子廣播,這個(gè)機(jī)制保證了各個(gè) server 之間的同步。實(shí)現(xiàn)這個(gè)機(jī)制的協(xié)議叫做 zab 協(xié)議。zab 協(xié)議有兩種模式,分別是恢復(fù)模式(選主)和廣播模式(同步)。當(dāng)服務(wù)啟動(dòng)或者在領(lǐng)導(dǎo)者崩潰后,zab 就進(jìn)入了恢復(fù)模式,當(dāng)領(lǐng)導(dǎo)者被選舉出來(lái),且大多數(shù) server 完成了和 leader 的狀態(tài)同步以后,恢復(fù)模式就結(jié)束了。狀態(tài)同步保證了 leader 和 server 具有相同的系統(tǒng)狀態(tài)。
161. 集群中為什么要有主節(jié)點(diǎn)?
在分布式環(huán)境中,有些業(yè)務(wù)邏輯只需要集群中的某一臺(tái)機(jī)器進(jìn)行執(zhí)行,其他的機(jī)器可以共享這個(gè)結(jié)果,這樣可以大大減少重復(fù)計(jì)算,提高性能,所以就需要主節(jié)點(diǎn)。
162. 集群中有 3 臺(tái)服務(wù)器,其中一個(gè)節(jié)點(diǎn)宕機(jī),這個(gè)時(shí)候 zookeeper 還可以使用嗎?
可以繼續(xù)使用,單數(shù)服務(wù)器只要沒超過(guò)一半的服務(wù)器宕機(jī)就可以繼續(xù)使用。
163. 說(shuō)一下 zookeeper 的通知機(jī)制?
客戶端端會(huì)對(duì)某個(gè) znode 建立一個(gè) watcher 事件,當(dāng)該 znode 發(fā)生變化時(shí),這些客戶端會(huì)收到 zookeeper 的通知,然后客戶端可以根據(jù) znode 變化來(lái)做出業(yè)務(wù)上的改變。
MySQL
164. 數(shù)據(jù)庫(kù)的三范式是什么?
第一范式:強(qiáng)調(diào)的是列的原子性,即數(shù)據(jù)庫(kù)表的每一列都是不可分割的原子數(shù)據(jù)項(xiàng)。
第二范式:要求實(shí)體的屬性完全依賴于主關(guān)鍵字。所謂完全依賴是指不能存在僅依賴主關(guān)鍵字一部分的屬性。
第三范式:任何非主屬性不依賴于其它非主屬性。
165. 一張自增表里面總共有 7 條數(shù)據(jù),刪除了最后 2 條數(shù)據(jù),重啟 MySQL 數(shù)據(jù)庫(kù),又插入了一條數(shù)據(jù),此時(shí) id 是幾?
表類型如果是 MyISAM ,那 id 就是 8。
表類型如果是 InnoDB,那 id 就是 6。
InnoDB 表只會(huì)把自增主鍵的最大 id 記錄在內(nèi)存中,所以重啟之后會(huì)導(dǎo)致最大 id 丟失。
166. 如何獲取當(dāng)前數(shù)據(jù)庫(kù)版本?
使用 select version() 獲取當(dāng)前 MySQL 數(shù)據(jù)庫(kù)版本。
167. 說(shuō)一下 ACID 是什么?
Atomicity(原子性):一個(gè)事務(wù)(transaction)中的所有操作,或者全部完成,或者全部不完成,不會(huì)結(jié)束在中間某個(gè)環(huán)節(jié)。事務(wù)在執(zhí)行過(guò)程中發(fā)生錯(cuò)誤,會(huì)被恢復(fù)(Rollback)到事務(wù)開始前的狀態(tài),就像這個(gè)事務(wù)從來(lái)沒有執(zhí)行過(guò)一樣。即,事務(wù)不可分割、不可約簡(jiǎn)。
Consistency(一致性):在事務(wù)開始之前和事務(wù)結(jié)束以后,數(shù)據(jù)庫(kù)的完整性沒有被破壞。這表示寫入的資料必須完全符合所有的預(yù)設(shè)約束、觸發(fā)器、級(jí)聯(lián)回滾等。
Isolation(隔離性):數(shù)據(jù)庫(kù)允許多個(gè)并發(fā)事務(wù)同時(shí)對(duì)其數(shù)據(jù)進(jìn)行讀寫和修改的能力,隔離性可以防止多個(gè)事務(wù)并發(fā)執(zhí)行時(shí)由于交叉執(zhí)行而導(dǎo)致數(shù)據(jù)的不一致。事務(wù)隔離分為不同級(jí)別,包括讀未提交(Read uncommitted)、讀提交(read committed)、可重復(fù)讀(repeatable read)和串行化(Serializable)。
Durability(持久性):事務(wù)處理結(jié)束后,對(duì)數(shù)據(jù)的修改就是永久的,即便系統(tǒng)故障也不會(huì)丟失。
168. char 和 varchar 的區(qū)別是什么?
「char(n)」 :固定長(zhǎng)度類型,比如訂閱 char(10),當(dāng)你輸入"abc"三個(gè)字符的時(shí)候,它們占的空間還是 10 個(gè)字節(jié),其他 7 個(gè)是空字節(jié)。
chat 優(yōu)點(diǎn):效率高;缺點(diǎn):占用空間;適用場(chǎng)景:存儲(chǔ)密碼的 md5 值,固定長(zhǎng)度的,使用 char 非常合適。
「varchar(n)」 :可變長(zhǎng)度,存儲(chǔ)的值是每個(gè)值占用的字節(jié)再加上一個(gè)用來(lái)記錄其長(zhǎng)度的字節(jié)的長(zhǎng)度。
所以,從空間上考慮 varcahr 比較合適;從效率上考慮 char 比較合適,二者使用需要權(quán)衡。
169. float 和 double 的區(qū)別是什么?
float 最多可以存儲(chǔ) 8 位的十進(jìn)制數(shù),并在內(nèi)存中占 4 字節(jié)。
double 最可可以存儲(chǔ) 16 位的十進(jìn)制數(shù),并在內(nèi)存中占 8 字節(jié)。
圖片
170. MySQL 的內(nèi)連接、左連接、右連接有什么區(qū)別?
內(nèi)連接關(guān)鍵字:inner join;左連接:left join;右連接:right join。
內(nèi)連接是把匹配的關(guān)聯(lián)數(shù)據(jù)顯示出來(lái);左連接是左邊的表全部顯示出來(lái),右邊的表顯示出符合條件的數(shù)據(jù);右連接正好相反。
171. MySQL 索引是怎么實(shí)現(xiàn)的?
索引是滿足某種特定查找算法的數(shù)據(jù)結(jié)構(gòu),而這些數(shù)據(jù)結(jié)構(gòu)會(huì)以某種方式指向數(shù)據(jù),從而實(shí)現(xiàn)高效查找數(shù)據(jù)。
具體來(lái)說(shuō) MySQL 中的索引,不同的數(shù)據(jù)引擎實(shí)現(xiàn)有所不同,但目前主流的數(shù)據(jù)庫(kù)引擎的索引都是 B+ 樹實(shí)現(xiàn)的,B+ 樹的搜索效率,可以到達(dá)二分法的性能,找到數(shù)據(jù)區(qū)域之后就找到了完整的數(shù)據(jù)結(jié)構(gòu)了,所有索引的性能也是更好的。
172. 怎么驗(yàn)證 MySQL 的索引是否滿足需求?
使用 explain 查看 SQL 是如何執(zhí)行查詢語(yǔ)句的,從而分析你的索引是否滿足需求。
explain 語(yǔ)法:explain select __ from table where type=1。
173. 說(shuō)一下數(shù)據(jù)庫(kù)的事務(wù)隔離?
MySQL 的事務(wù)隔離是在 MySQL. ini 配置文件里添加的,在文件的最后添加:
?transaction-isolation = REPEATABLE-READ?
可用的配置值:READ-UNCOMMITTED、READ-COMMITTED、REPEATABLE-READ、SERIALIZABLE。
READ-UNCOMMITTED:未提交讀,最低隔離級(jí)別、事務(wù)未提交前,就可被其他事務(wù)讀取(會(huì)出現(xiàn)幻讀、臟讀、不可重復(fù)讀)。
READ-COMMITTED:提交讀,一個(gè)事務(wù)提交后才能被其他事務(wù)讀取到(會(huì)造成幻讀、不可重復(fù)讀)。
REPEATABLE-READ:可重復(fù)讀,默認(rèn)級(jí)別,保證多次讀取同一個(gè)數(shù)據(jù)時(shí),其值都和事務(wù)開始時(shí)候的內(nèi)容是一致,禁止讀取到別的事務(wù)未提交的數(shù)據(jù)(會(huì)造成幻讀)。
SERIALIZABLE:序列化,代價(jià)最高最可靠的隔離級(jí)別,該隔離級(jí)別能防止臟讀、不可重復(fù)讀、幻讀。
「臟讀」 :表示一個(gè)事務(wù)能夠讀取另一個(gè)事務(wù)中還未提交的數(shù)據(jù)。比如,某個(gè)事務(wù)嘗試插入記錄 A,此時(shí)該事務(wù)還未提交,然后另一個(gè)事務(wù)嘗試讀取到了記錄 A。
「不可重復(fù)讀」 :是指在一個(gè)事務(wù)內(nèi),多次讀同一數(shù)據(jù)。
「幻讀」 :指同一個(gè)事務(wù)內(nèi)多次查詢返回的結(jié)果集不一樣。比如同一個(gè)事務(wù) A 第一次查詢時(shí)候有 n 條記錄,但是第二次同等條件下查詢卻有 n+1 條記錄,這就好像產(chǎn)生了幻覺。發(fā)生幻讀的原因也是另外一個(gè)事務(wù)新增或者刪除或者修改了第一個(gè)事務(wù)結(jié)果集里面的數(shù)據(jù),同一個(gè)記錄的數(shù)據(jù)內(nèi)容被修改了,所有數(shù)據(jù)行的記錄就變多或者變少了。
174. 說(shuō)一下 MySQL 常用的引擎?
InnoDB 引擎:mysql 5.1 后默認(rèn)的數(shù)據(jù)庫(kù)引擎,提供了對(duì)數(shù)據(jù)庫(kù) acid 事務(wù)的支持,并且還提供了行級(jí)鎖和外鍵的約束,它的設(shè)計(jì)的目標(biāo)就是處理大數(shù)據(jù)容量的數(shù)據(jù)庫(kù)系統(tǒng)。MySQL 運(yùn)行的時(shí)候,InnoDB 會(huì)在內(nèi)存中建立緩沖池,用于緩沖數(shù)據(jù)和索引。但是該引擎是不支持全文搜索,同時(shí)啟動(dòng)也比較的慢,它是不會(huì)保存表的行數(shù)的,所以當(dāng)進(jìn)行 select count(__) from table 指令的時(shí)候,需要進(jìn)行掃描全表。由于鎖的粒度小,寫操作是不會(huì)鎖定全表的,所以在并發(fā)度較高的場(chǎng)景下使用會(huì)提升效率的。
MyIASM 引擎:不提供事務(wù)的支持,也不支持行級(jí)鎖和外鍵。因此當(dāng)執(zhí)行插入和更新語(yǔ)句時(shí),即執(zhí)行寫操作的時(shí)候需要鎖定這個(gè)表,所以會(huì)導(dǎo)致效率會(huì)降低。不過(guò)和 InnoDB 不同的是,MyIASM 引擎是保存了表的行數(shù),于是當(dāng)進(jìn)行 select count(__) from table 語(yǔ)句時(shí),可以直接的讀取已經(jīng)保存的值而不需要進(jìn)行掃描全表。所以,如果表的讀操作遠(yuǎn)遠(yuǎn)多于寫操作時(shí),并且不需要事務(wù)的支持的,可以將 MyIASM 作為數(shù)據(jù)庫(kù)引擎的首選。
175. 說(shuō)一下 MySQL 的行鎖和表鎖?
MyISAM 只支持表鎖,InnoDB 支持表鎖和行鎖,默認(rèn)為行鎖。
表級(jí)鎖:開銷小,加鎖快,不會(huì)出現(xiàn)死鎖。鎖定粒度大,發(fā)生鎖沖突的概率最高,并發(fā)量最低。
行級(jí)鎖:開銷大,加鎖慢,會(huì)出現(xiàn)死鎖。鎖力度小,發(fā)生鎖沖突的概率小,并發(fā)度最高。
176. 說(shuō)一下樂觀鎖和悲觀鎖?
樂觀鎖:每次去拿數(shù)據(jù)的時(shí)候都認(rèn)為別人不會(huì)修改,所以不會(huì)上鎖,但是在提交更新的時(shí)候會(huì)判斷一下在此期間別人有沒有去更新這個(gè)數(shù)據(jù)。
悲觀鎖:每次去拿數(shù)據(jù)的時(shí)候都認(rèn)為別人會(huì)修改,所以每次在拿數(shù)據(jù)的時(shí)候都會(huì)上鎖,這樣別人想拿這個(gè)數(shù)據(jù)就會(huì)阻止,直到這個(gè)鎖被釋放。
數(shù)據(jù)庫(kù)的樂觀鎖需要自己實(shí)現(xiàn),在表里面添加一個(gè) version 字段,每次修改成功值加 1,這樣每次修改的時(shí)候先對(duì)比一下,自己擁有的 version 和數(shù)據(jù)庫(kù)現(xiàn)在的 version 是否一致,如果不一致就不修改,這樣就實(shí)現(xiàn)了樂觀鎖。
177. MySQL 問(wèn)題排查都有哪些手段?
使用 show processlist 命令查看當(dāng)前所有連接信息。
使用 explain 命令查詢 SQL 語(yǔ)句執(zhí)行計(jì)劃。
開啟慢查詢?nèi)罩?,查看慢查詢?SQL。
178. 如何做 MySQL 的性能優(yōu)化?
為搜索字段創(chuàng)建索引。
避免使用 select __,列出需要查詢的字段。
垂直分割分表。
選擇正確的存儲(chǔ)引擎。
Redis
179. Redis 是什么?都有哪些使用場(chǎng)景?
Redis 是一個(gè)使用 C 語(yǔ)言開發(fā)的高速緩存數(shù)據(jù)庫(kù)。
Redis 使用場(chǎng)景:
記錄帖子點(diǎn)贊數(shù)、點(diǎn)擊數(shù)、評(píng)論數(shù);
緩存近期熱帖;
緩存文章詳情信息;
記錄用戶會(huì)話信息。
180. Redis 有哪些功能?
數(shù)據(jù)緩存功能
分布式鎖的功能
支持?jǐn)?shù)據(jù)持久化
支持事務(wù)
支持消息隊(duì)列
181. Redis 和 memcache 有什么區(qū)別?
存儲(chǔ)方式不同:memcache 把數(shù)據(jù)全部存在內(nèi)存之中,斷電后會(huì)掛掉,數(shù)據(jù)不能超過(guò)內(nèi)存大小;Redis 有部份存在硬盤上,這樣能保證數(shù)據(jù)的持久性。
數(shù)據(jù)支持類型:memcache 對(duì)數(shù)據(jù)類型支持相對(duì)簡(jiǎn)單;Redis 有復(fù)雜的數(shù)據(jù)類型。
使用底層模型不同:它們之間底層實(shí)現(xiàn)方式,以及與客戶端之間通信的應(yīng)用協(xié)議不一樣,Redis 自己構(gòu)建了 vm 機(jī)制,因?yàn)橐话愕南到y(tǒng)調(diào)用系統(tǒng)函數(shù)的話,會(huì)浪費(fèi)一定的時(shí)間去移動(dòng)和請(qǐng)求。
value 值大小不同:Redis 最大可以達(dá)到 512mb;memcache 只有 1mb。
182. Redis 為什么是單線程的?
因?yàn)?cpu 不是 Redis 的瓶頸,Redis 的瓶頸最有可能是機(jī)器內(nèi)存或者網(wǎng)絡(luò)帶寬。既然單線程容易實(shí)現(xiàn),而且 cpu 又不會(huì)成為瓶頸,那就順理成章地采用單線程的方案了。
關(guān)于 Redis 的性能,官方網(wǎng)站也有,普通筆記本輕松處理每秒幾十萬(wàn)的請(qǐng)求。
而且單線程并不代表就慢 nginx 和 nodejs 也都是高性能單線程的代表。
183. 什么是緩存穿透?怎么解決?
緩存穿透:指查詢一個(gè)一定不存在的數(shù)據(jù),由于緩存是不命中時(shí)需要從數(shù)據(jù)庫(kù)查詢,查不到數(shù)據(jù)則不寫入緩存,這將導(dǎo)致這個(gè)不存在的數(shù)據(jù)每次請(qǐng)求都要到數(shù)據(jù)庫(kù)去查詢,造成緩存穿透。
解決方案:最簡(jiǎn)單粗暴的方法如果一個(gè)查詢返回的數(shù)據(jù)為空(不管是數(shù)據(jù)不存在,還是系統(tǒng)故障),我們就把這個(gè)空結(jié)果進(jìn)行緩存,但它的過(guò)期時(shí)間會(huì)很短,最長(zhǎng)不超過(guò)五分鐘。
184. Redis 支持的數(shù)據(jù)類型有哪些?
Redis 支持的數(shù)據(jù)類型:string(字符串)、list(列表)、hash(字典)、set(集合)、zset(有序集合)。
185. Redis 支持的 Java 客戶端都有哪些?
支持的 Java 客戶端有 Redisson、jedis、lettuce 等。
186. jedis 和 Redisson 有哪些區(qū)別?
jedis:提供了比較全面的 Redis 命令的支持。
Redisson:實(shí)現(xiàn)了分布式和可擴(kuò)展的 Java 數(shù)據(jù)結(jié)構(gòu),與 jedis 相比 Redisson 的功能相對(duì)簡(jiǎn)單,不支持排序、事務(wù)、管道、分區(qū)等 Redis 特性。
187. 怎么保證緩存和數(shù)據(jù)庫(kù)數(shù)據(jù)的一致性?
合理設(shè)置緩存的過(guò)期時(shí)間。
新增、更改、刪除數(shù)據(jù)庫(kù)操作時(shí)同步更新 Redis,可以使用事物機(jī)制來(lái)保證數(shù)據(jù)的一致性。
188. Redis 持久化有幾種方式?
Redis 的持久化有兩種方式,或者說(shuō)有兩種策略:
RDB(Redis Database):指定的時(shí)間間隔能對(duì)你的數(shù)據(jù)進(jìn)行快照存儲(chǔ)。
AOF(Append Only File):每一個(gè)收到的寫命令都通過(guò)write函數(shù)追加到文件中。
189. Redis 怎么實(shí)現(xiàn)分布式鎖?
Redis 分布式鎖其實(shí)就是在系統(tǒng)里面占一個(gè)“坑”,其他程序也要占“坑”的時(shí)候,占用成功了就可以繼續(xù)執(zhí)行,失敗了就只能放棄或稍后重試。
占坑一般使用 setnx(set if not exists)指令,只允許被一個(gè)程序占有,使用完調(diào)用 del 釋放鎖。
190. Redis 分布式鎖有什么缺陷?
Redis 分布式鎖不能解決超時(shí)的問(wèn)題,分布式鎖有一個(gè)超時(shí)時(shí)間,程序的執(zhí)行如果超出了鎖的超時(shí)時(shí)間就會(huì)出現(xiàn)問(wèn)題。
191. Redis 如何做內(nèi)存優(yōu)化?
盡量使用 Redis 的散列表,把相關(guān)的信息放到散列表里面存儲(chǔ),而不是把每個(gè)字段單獨(dú)存儲(chǔ),這樣可以有效的減少內(nèi)存使用。比如將 Web 系統(tǒng)的用戶對(duì)象,應(yīng)該放到散列表里面再整體存儲(chǔ)到 Redis,而不是把用戶的姓名、年齡、密碼、郵箱等字段分別設(shè)置 key 進(jìn)行存儲(chǔ)。
192. Redis 淘汰策略有哪些?
volatile-lru:從已設(shè)置過(guò)期時(shí)間的數(shù)據(jù)集(server. db[i]. expires)中挑選最近最少使用的數(shù)據(jù)淘汰。
volatile-ttl:從已設(shè)置過(guò)期時(shí)間的數(shù)據(jù)集(server. db[i]. expires)中挑選將要過(guò)期的數(shù)據(jù)淘汰。
volatile-random:從已設(shè)置過(guò)期時(shí)間的數(shù)據(jù)集(server. db[i]. expires)中任意選擇數(shù)據(jù)淘汰。
allkeys-lru:從數(shù)據(jù)集(server. db[i]. dict)中挑選最近最少使用的數(shù)據(jù)淘汰。
allkeys-random:從數(shù)據(jù)集(server. db[i]. dict)中任意選擇數(shù)據(jù)淘汰。
no-enviction(驅(qū)逐):禁止驅(qū)逐數(shù)據(jù)。
193. Redis 常見的性能問(wèn)題有哪些?該如何解決?
主服務(wù)器寫內(nèi)存快照,會(huì)阻塞主線程的工作,當(dāng)快照比較大時(shí)對(duì)性能影響是非常大的,會(huì)間斷性暫停服務(wù),所以主服務(wù)器最好不要寫內(nèi)存快照。
Redis 主從復(fù)制的性能問(wèn)題,為了主從復(fù)制的速度和連接的穩(wěn)定性,主從庫(kù)最好在同一個(gè)局域網(wǎng)內(nèi)。
JVM
194. 說(shuō)一下 JVM 的主要組成部分?及其作用?
類加載器(ClassLoader)
運(yùn)行時(shí)數(shù)據(jù)區(qū)(Runtime Data Area)
執(zhí)行引擎(Execution Engine)
本地庫(kù)接口(Native Interface)
「組件的作用:」 首先通過(guò)類加載器(ClassLoader)會(huì)把 Java 代碼轉(zhuǎn)換成字節(jié)碼,運(yùn)行時(shí)數(shù)據(jù)區(qū)(Runtime Data Area)再把字節(jié)碼加載到內(nèi)存中,而字節(jié)碼文件只是 JVM 的一套指令集規(guī)范,并不能直接交給底層操作系統(tǒng)去執(zhí)行,因此需要特定的命令解析器執(zhí)行引擎(Execution Engine),將字節(jié)碼翻譯成底層系統(tǒng)指令,再交由 CPU 去執(zhí)行,而這個(gè)過(guò)程中需要調(diào)用其他語(yǔ)言的本地庫(kù)接口(Native Interface)來(lái)實(shí)現(xiàn)整個(gè)程序的功能。
195. 說(shuō)一下 JVM 運(yùn)行時(shí)數(shù)據(jù)區(qū)?
不同虛擬機(jī)的運(yùn)行時(shí)數(shù)據(jù)區(qū)可能略微有所不同,但都會(huì)遵從 Java 虛擬機(jī)規(guī)范, Java 虛擬機(jī)規(guī)范規(guī)定的區(qū)域分為以下 5 個(gè)部分:
程序計(jì)數(shù)器(Program Counter Register):當(dāng)前線程所執(zhí)行的字節(jié)碼的行號(hào)指示器,字節(jié)碼解析器的工作是通過(guò)改變這個(gè)計(jì)數(shù)器的值,來(lái)選取下一條需要執(zhí)行的字節(jié)碼指令,分支、循環(huán)、跳轉(zhuǎn)、異常處理、線程恢復(fù)等基礎(chǔ)功能,都需要依賴這個(gè)計(jì)數(shù)器來(lái)完成;
Java 虛擬機(jī)棧(Java Virtual Machine Stacks):用于存儲(chǔ)局部變量表、操作數(shù)棧、動(dòng)態(tài)鏈接、方法出口等信息;
本地方法棧(Native Method Stack):與虛擬機(jī)棧的作用是一樣的,只不過(guò)虛擬機(jī)棧是服務(wù) Java 方法的,而本地方法棧是為虛擬機(jī)調(diào)用 Native 方法服務(wù)的;
Java 堆(Java Heap):Java 虛擬機(jī)中內(nèi)存最大的一塊,是被所有線程共享的,幾乎所有的對(duì)象實(shí)例都在這里分配內(nèi)存;
方法區(qū)(Methed Area):用于存儲(chǔ)已被虛擬機(jī)加載的類信息、常量、靜態(tài)變量、即時(shí)編譯后的代碼等數(shù)據(jù)。
196. 說(shuō)一下堆棧的區(qū)別?
功能方面:堆是用來(lái)存放對(duì)象的,棧是用來(lái)執(zhí)行程序的。
共享性:堆是線程共享的,棧是線程私有的。
空間大?。憾汛笮∵h(yuǎn)遠(yuǎn)大于棧。
197. 隊(duì)列和棧是什么?有什么區(qū)別?
隊(duì)列和棧都是被用來(lái)預(yù)存儲(chǔ)數(shù)據(jù)的。
隊(duì)列允許先進(jìn)先出檢索元素,但也有例外的情況,Deque 接口允許從兩端檢索元素。
棧和隊(duì)列很相似,但它運(yùn)行對(duì)元素進(jìn)行后進(jìn)先出進(jìn)行檢索。
198. 什么是雙親委派模型?
在介紹雙親委派模型之前先說(shuō)下類加載器。對(duì)于任意一個(gè)類,都需要由加載它的類加載器和這個(gè)類本身一同確立在 JVM 中的唯一性,每一個(gè)類加載器,都有一個(gè)獨(dú)立的類名稱空間。類加載器就是根據(jù)指定全限定名稱將 class 文件加載到 JVM 內(nèi)存,然后再轉(zhuǎn)化為 class 對(duì)象。
類加載器分類:
啟動(dòng)類加載器(Bootstrap ClassLoader),是虛擬機(jī)自身的一部分,用來(lái)加載Java_HOME/lib/目錄中的,或者被 -Xbootclasspath 參數(shù)所指定的路徑中并且被虛擬機(jī)識(shí)別的類庫(kù);
其他類加載器:
擴(kuò)展類加載器(Extension ClassLoader):負(fù)責(zé)加載libext目錄或Java. ext. dirs系統(tǒng)變量指定的路徑中的所有類庫(kù);
應(yīng)用程序類加載器(Application ClassLoader)。負(fù)責(zé)加載用戶類路徑(classpath)上的指定類庫(kù),我們可以直接使用這個(gè)類加載器。一般情況,如果我們沒有自定義類加載器默認(rèn)就是用這個(gè)加載器。
雙親委派模型:如果一個(gè)類加載器收到了類加載的請(qǐng)求,它首先不會(huì)自己去加載這個(gè)類,而是把這個(gè)請(qǐng)求委派給父類加載器去完成,每一層的類加載器都是如此,這樣所有的加載請(qǐng)求都會(huì)被傳送到頂層的啟動(dòng)類加載器中,只有當(dāng)父加載無(wú)法完成加載請(qǐng)求(它的搜索范圍中沒找到所需的類)時(shí),子加載器才會(huì)嘗試去加載類。
199. 說(shuō)一下類裝載的執(zhí)行過(guò)程?
類裝載分為以下 5 個(gè)步驟:
加載:根據(jù)查找路徑找到相應(yīng)的 class 文件然后導(dǎo)入;
檢查:檢查加載的 class 文件的正確性;
準(zhǔn)備:給類中的靜態(tài)變量分配內(nèi)存空間;
解析:虛擬機(jī)將常量池中的符號(hào)引用替換成直接引用的過(guò)程。符號(hào)引用就理解為一個(gè)標(biāo)示,而在直接引用直接指向內(nèi)存中的地址;
初始化:對(duì)靜態(tài)變量和靜態(tài)代碼塊執(zhí)行初始化工作。
200. 怎么判斷對(duì)象是否可以被回收?
一般有兩種方法來(lái)判斷:
引用計(jì)數(shù)器:為每個(gè)對(duì)象創(chuàng)建一個(gè)引用計(jì)數(shù),有對(duì)象引用時(shí)計(jì)數(shù)器 +1,引用被釋放時(shí)計(jì)數(shù) -1,當(dāng)計(jì)數(shù)器為 0 時(shí)就可以被回收。它有一個(gè)缺點(diǎn)不能解決循環(huán)引用的問(wèn)題;
可達(dá)性分析:從 GC Roots 開始向下搜索,搜索所走過(guò)的路徑稱為引用鏈。當(dāng)一個(gè)對(duì)象到 GC Roots 沒有任何引用鏈相連時(shí),則證明此對(duì)象是可以被回收的。
JAVA面試需要準(zhǔn)備什么?
簡(jiǎn)歷:簡(jiǎn)歷的編寫要真實(shí)可信,突出個(gè)人亮點(diǎn),格式無(wú)誤,排版干凈,控制簡(jiǎn)歷頁(yè)數(shù)。
面試準(zhǔn)備:了解公司的基本情況、業(yè)務(wù)范圍、市場(chǎng)占比等。
技術(shù)準(zhǔn)備:熟悉Java開發(fā)崗位所需知識(shí),包括Java基礎(chǔ)、JVM、多線程、Mysql、Spring、Spring Boot、Spring Cloud等。
非技術(shù)準(zhǔn)備:了解非技術(shù)性方面的問(wèn)題,如知識(shí)結(jié)構(gòu)、溝通能力、學(xué)習(xí)能力等。
JAVA是做什么的?
Java是一種廣泛使用的計(jì)算機(jī)編程語(yǔ)言,擁有跨平臺(tái)、面向?qū)ο?、泛型編程的特性,廣泛應(yīng)用于企業(yè)級(jí)Web應(yīng)用開發(fā)和移動(dòng)應(yīng)用開發(fā)。Java可以編寫桌面應(yīng)用程序、Web應(yīng)用程序、分布式系統(tǒng)和嵌入式系統(tǒng)應(yīng)用程序等。
Java不僅吸收了C++語(yǔ)言的各種優(yōu)點(diǎn),還摒棄了C++里難以理解的多繼承、指針等概念,因此Java語(yǔ)言具有功能強(qiáng)大和簡(jiǎn)單易用兩個(gè)特征。
Java語(yǔ)言作為靜態(tài)面向?qū)ο缶幊陶Z(yǔ)言的代表,極好地實(shí)現(xiàn)了面向?qū)ο罄碚?,允許程序員以優(yōu)雅的思維方式進(jìn)行復(fù)雜的編程。