2019年100道經典JAVA面試真題解析

2019年快結束了,給你們整理了今年來最經典的面試真題100道,每一個題目都有詳細的解答,收集了java基礎,容器,多線程,反射,對象拷貝,Java Web,異常,網絡,設計模式,Spring / Spring MVC,等專題的經典面試真題,和詳細分析。沒道題目都詳細講解,文章過長,你們必定要耐心的看完哦。php

1、Java 基礎

1. JDK 和 JRE 有什麼區別?

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

具體來講 JDK 其實包含了 JRE,同時還包含了編譯 java 源碼的編譯器 javac,還包含了不少 java 程序調試和分析的工具。簡單來講:若是你須要運行 java 程序,只需安裝 JRE 就能夠了,若是你須要編寫 java 程序,須要安裝 JDK。html

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

== 解讀前端

對於基本類型和引用類型 == 的做用效果是不一樣的,以下所示:java

  • 基本類型:比較的是值是否相同;
  • 引用類型:比較的是引用是否相同;

代碼示例:程序員

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

代碼解讀:由於 x 和 y 指向的是同一個引用,因此 == 也是 true,而 new String()方法則重寫開闢了內存空間,因此 == 結果爲 false,而 equals 比較的一直是值,因此結果都爲 true。web

equals 解讀面試

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

首先來看默認狀況下 equals 比較一個有相同值的對象,代碼以下:算法

class Cat {
 public Cat(String name) {
 this.name = name;
 }
 
 private String name;
 
 public String getName() {
 return name;
 }
 
 public void setName(String name) {
 this.name = name;
 }
}
 
Cat c1 = new Cat;
Cat c2 = new Cat;
System.out.println(c1.equals(c2)); // false複製代碼

輸出結果出乎咱們的意料,居然是 false?這是怎麼回事,看了 equals 源碼就知道了,源碼以下:spring

public boolean equals(Object obj) {
 return (this == obj);
}複製代碼

原來 equals 本質上就是 ==。

那問題來了,兩個相同值的 String 對象,爲何返回的是 true?代碼以下:

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

一樣的,當咱們進入 String 的 equals 方法,找到了答案,代碼以下:

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

原來是 String 重寫了 Object 的 equals 方法,把引用比較改爲了值比較。

總結 :== 對於基本類型來講是值比較,對於引用類型來講是比較的是引用;而 equals 默認狀況下是引用比較,只是不少類從新了 equals 方法,好比 String、Integer 等把它變成了值比較,因此通常狀況下 equals 比較的是值是否相等。

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

不對,兩個對象的 hashCode()相同,equals()不必定 true。

代碼示例:

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

執行的結果:

str1:1179395 | str2:1179395

false複製代碼

代碼解讀:很顯然「通話」和「重地」的 hashCode() 相同,然而 equals() 則爲 false,由於在散列表中,hashCode()相等即兩個鍵值對的哈希值相等,然而哈希值相等,並不必定能得出鍵值對相等。

4. final 在 java 中有什麼做用?

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

5. java 中的 Math.round(-1.5) 等於多少?

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

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

String 不屬於基礎類型,基礎類型有 8 種:byte、boolean、char、short、int、float、long、double,而 String 屬於對象。

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

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

String 和 StringBuffer、StringBuilder 的區別在於 String 聲明的是不可變的對象,每次操做都會生成新的 String 對象,而後將指針指向新的 String 對象,而 StringBuffer、StringBuilder 能夠在原有對象的基礎上進行操做,因此在常常改變字符串內容的狀況下最好不要使用 String。

StringBuffer 和 StringBuilder 最大的區別在於,StringBuffer 是線程安全的,而 StringBuilder 是非線程安全的,但 StringBuilder 的性能卻高於 StringBuffer,因此在單線程環境下推薦使用 StringBuilder,多線程環境下推薦使用 StringBuffer。

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

不同,由於內存的分配方式不同。String str="i"的方式,java 虛擬機會將其分配到常量池中;而 String str=new String("i") 則會被分到堆內存中。

9. 如何將字符串反轉?

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

示例代碼:

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

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

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

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

不須要,抽象類不必定非要有抽象方法。

示例代碼:

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

上面代碼,抽象類並無抽象方法但徹底能夠正常運行。

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

  • 普通類不能包含抽象方法,抽象類能夠包含抽象方法。
  • 抽象類不能直接實例化,普通類能夠直接實例化。

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

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

14. 接口和抽象類有什麼區別?

  • 實現:抽象類的子類使用 extends 來繼承;接口必須使用 implements 來實現接口。
  • 構造函數:抽象類能夠有構造函數;接口不能有。
  • main 方法:抽象類能夠有 main 方法,而且咱們能運行它;接口不能有 main 方法。
  • 實現數量:類能夠實現不少個接口;可是隻能繼承一個抽象類。
  • 訪問修飾符:接口中的方法默認使用 public 修飾;抽象類中的方法能夠是任意訪問修飾符。

15. java 中 IO 流分爲幾種?

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

按類型來分:字節流和字符流。

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

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

  • BIO:Block IO 同步阻塞式 IO,就是咱們日常使用的傳統 IO,它的特色是模式簡單使用方便,併發處理能力低。
  • NIO:New IO 同步非阻塞 IO,是傳統 IO 的升級,客戶端和服務器端經過 Channel(通道)通信,實現了多路複用。
  • AIO:Asynchronous IO 是 NIO 的升級,也叫 NIO2,實現了異步非堵塞 IO ,異步 IO 的操做基於事件和回調機制。

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

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

2、容器

18. java 容器都有哪些?

經常使用容器的圖錄:

19. Collection 和 Collections 有什麼區別?

  • java.util.Collection 是一個集合接口(集合類的一個頂級接口)。它提供了對集合對象進行基本操做的通用接口方法。Collection接口在Java 類庫中有不少具體的實現。Collection接口的意義是爲各類具體的集合提供了最大化的統一操做方式,其直接繼承接口有List與Set。
  • Collections則是集合類的一個工具類/幫助類,其中提供了一系列靜態方法,用於對集合中元素進行排序、搜索以及線程安全等各類操做。

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

21. HashMap 和 Hashtable 有什麼區別?

  • hashMap去掉了HashTable 的contains方法,可是加上了containsValue()和containsKey()方法。
  • hashTable同步的,而HashMap是非同步的,效率上逼hashTable要高。
  • hashMap容許空鍵值,而hashTable不容許。

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

對於在Map中插入、刪除和定位元素這類操做,HashMap是最好的選擇。然而,假如你須要對一個有序的key集合進行遍歷,TreeMap是更好的選擇。基於你的collection的大小,也許向HashMap中添加元素會更快,將map換爲TreeMap進行有序key的遍歷。

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

HashMap概述: HashMap是基於哈希表的Map接口的非同步實現。此實現提供全部可選的映射操做,並容許使用null值和null鍵。此類不保證映射的順序,特別是它不保證該順序恆久不變。

HashMap的數據結構: 在java編程語言中,最基本的結構就是兩種,一個是數組,另一個是模擬指針(引用),全部的數據結構均可以用這兩個基本結構來構造的,HashMap也不例外。HashMap其實是一個「鏈表散列」的數據結構,即數組和鏈表的結合體。

當咱們往Hashmap中put元素時,首先根據key的hashcode從新計算hash值,根絕hash值獲得這個元素在數組中的位置(下標),若是該數組在該位置上已經存放了其餘元素,那麼在這個位置上的元素將以鏈表的形式存放,新加入的放在鏈頭,最早加入的放入鏈尾.若是數組中該位置沒有元素,就直接將該元素放到數組的該位置上。

須要注意Jdk 1.8中對HashMap的實現作了優化,當鏈表中的節點數據超過八個以後,該鏈表會轉爲紅黑樹來提升查詢效率,從原來的O(n)到O(logn)

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

  • HashSet底層由HashMap實現
  • HashSet的值存放於HashMap的key上
  • HashMap的value統一爲PRESENT

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

最明顯的區別是 ArrrayList底層的數據結構是數組,支持隨機訪問,而 LinkedList 的底層數據結構是雙向循環鏈表,不支持隨機訪問。使用下標訪問一個元素,ArrayList 的時間複雜度是 O(1),而 LinkedList 是 O(n)。

26. 如何實現數組和 List 之間的轉換?

  • List轉換成爲數組:調用ArrayList的toArray方法。
  • 數組轉換成爲List:調用Arrays的asList方法。

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

  • Vector是同步的,而ArrayList不是。然而,若是你尋求在迭代的時候對列表進行改變,你應該使用CopyOnWriteArrayList。
  • ArrayList比Vector快,它由於有同步,不會過載。
  • ArrayList更加通用,由於咱們能夠使用Collections工具類輕易地獲取同步列表和只讀列表。

28. Array 和 ArrayList 有何區別?

  • Array能夠容納基本類型和對象,而ArrayList只能容納對象。
  • Array是指定大小的,而ArrayList大小是固定的。
  • Array沒有提供ArrayList那麼多功能,好比addAll、removeAll和iterator等。

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

poll() 和 remove() 都是從隊列中取出一個元素,可是 poll() 在獲取元素失敗的時候會返回空,可是 remove() 失敗的時候會拋出異常。

30. 哪些集合類是線程安全的?

  • vector:就比arraylist多了個同步化機制(線程安全),由於效率較低,如今已經不太建議使用。在web應用中,特別是前臺頁面,每每效率(頁面響應速度)是優先考慮的。
  • statck:堆棧類,先進後出。
  • hashtable:就比hashmap多了個線程安全。
  • enumeration:枚舉,至關於迭代器。

31. 迭代器 Iterator 是什麼?

迭代器是一種設計模式,它是一個對象,它能夠遍歷並選擇序列中的對象,而開發人員不須要了解該序列的底層結構。迭代器一般被稱爲「輕量級」對象,由於建立它的代價小。

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

Java中的Iterator功能比較簡單,而且只能單向移動:

(1) 使用方法iterator()要求容器返回一個Iterator。第一次調用Iterator的next()方法時,它返回序列的第一個元素。注意:iterator()方法是java.lang.Iterable接口,被Collection繼承。

(2) 使用next()得到序列中的下一個元素。

(3) 使用hasNext()檢查序列中是否還有元素。

(4) 使用remove()將迭代器新返回的元素刪除。

Iterator是Java迭代器最簡單的實現,爲List設計的ListIterator具備更多的功能,它能夠從兩個方向遍歷List,也能夠從List中插入和刪除元素。

33. Iterator 和 ListIterator 有什麼區別?

  • Iterator可用來遍歷Set和List集合,可是ListIterator只能用來遍歷List。
  • Iterator對集合只能是前向遍歷,ListIterator既能夠前向也能夠後向。
  • ListIterator實現了Iterator接口,幷包含其餘的功能,好比:增長元素,替換元素,獲取前一個和後一個元素的索引,等等。

3、多線程

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

  • 並行是指兩個或者多個事件在同一時刻發生;而併發是指兩個或多個事件在同一時間間隔發生。
  • 並行是在不一樣實體上的多個事件,併發是在同一實體上的多個事件。
  • 在一臺處理器上「同時」處理多個任務,在多臺處理器上同時處理多個任務。如hadoop分佈式集羣。

因此併發編程的目標是充分的利用處理器的每個核,以達到最高的處理性能。

36. 線程和進程的區別?

簡而言之,進程是程序運行和資源分配的基本單位,一個程序至少有一個進程,一個進程至少有一個線程。進程在執行過程當中擁有獨立的內存單元,而多個線程共享內存資源,減小切換次數,從而效率更高。線程是進程的一個實體,是cpu調度和分派的基本單位,是比程序更小的能獨立運行的基本單位。同一進程中的多個線程之間能夠併發執行。

37. 守護線程是什麼?

守護線程(即daemon thread),是個服務線程,準確地來講就是服務其餘的線程。

38. 建立線程有哪幾種方式?

①. 繼承Thread類建立線程類

  • 定義Thread類的子類,並重寫該類的run方法,該run方法的方法體就表明了線程要完成的任務。所以把run()方法稱爲執行體。
  • 建立Thread子類的實例,即建立了線程對象。
  • 調用線程對象的start()方法來啓動該線程。

②. 經過Runnable接口建立線程類

  • 定義runnable接口的實現類,並重寫該接口的run()方法,該run()方法的方法體一樣是該線程的線程執行體。
  • 建立 Runnable實現類的實例,並依此實例做爲Thread的target來建立Thread對象,該Thread對象纔是真正的線程對象。
  • 調用線程對象的start()方法來啓動該線程。

③. 經過Callable和Future建立線程

  • 建立Callable接口的實現類,並實現call()方法,該call()方法將做爲線程執行體,而且有返回值。
  • 建立Callable實現類的實例,使用FutureTask類來包裝Callable對象,該FutureTask對象封裝了該Callable對象的call()方法的返回值。
  • 使用FutureTask對象做爲Thread對象的target建立並啓動新線程。
  • 調用FutureTask對象的get()方法來得到子線程執行結束後的返回值。

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

有點深的問題了,也看出一個Java程序員學習知識的廣度。

  • Runnable接口中的run()方法的返回值是void,它作的事情只是純粹地去執行run()方法中的代碼而已;
  • Callable接口中的call()方法是有返回值的,是一個泛型,和Future、FutureTask配合能夠用來獲取異步執行的結果。

40. 線程有哪些狀態?

線程一般都有五種狀態,建立、就緒、運行、阻塞和死亡。

  • 建立狀態。在生成線程對象,並無調用該對象的start方法,這是線程處於建立狀態。
  • 就緒狀態。當調用了線程對象的start方法以後,該線程就進入了就緒狀態,可是此時線程調度程序尚未把該線程設置爲當前線程,此時處於就緒狀態。在線程運行以後,從等待或者睡眠中回來以後,也會處於就緒狀態。
  • 運行狀態。線程調度程序將處於就緒狀態的線程設置爲當前線程,此時線程就進入了運行狀態,開始運行run函數當中的代碼。
  • 阻塞狀態。線程正在運行的時候,被暫停,一般是爲了等待某個時間的發生(好比說某項資源就緒)以後再繼續運行。sleep,suspend,wait等方法均可以致使線程阻塞。
  • 死亡狀態。若是一個線程的run方法執行結束或者調用stop方法後,該線程就會死亡。對於已經死亡的線程,沒法再使用start方法令其進入就緒   

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

sleep():方法是線程類(Thread)的靜態方法,讓調用線程進入睡眠狀態,讓出執行機會給其餘線程,等到休眠時間結束後,線程進入就緒狀態和其餘線程一塊兒競爭cpu的執行時間。由於sleep() 是static靜態的方法,他不能改變對象的機鎖,當一個synchronized塊中調用了sleep() 方法,線程雖然進入休眠,可是對象的機鎖沒有被釋放,其餘線程依然沒法訪問這個對象。

wait():wait()是Object類的方法,當一個線程執行到wait方法時,它就進入到一個和該對象相關的等待池,同時釋放對象的機鎖,使得其餘線程可以訪問,能夠經過notify,notifyAll方法來喚醒等待的線程。

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

  • 若是線程調用了對象的 wait()方法,那麼線程便會處於該對象的等待池中,等待池中的線程不會去競爭該對象的鎖。
  • 當有線程調用了對象的 notifyAll()方法(喚醒全部 wait 線程)或 notify()方法(只隨機喚醒一個 wait 線程),被喚醒的的線程便會進入該對象的鎖池中,鎖池中的線程會去競爭該對象鎖。也就是說,調用了notify後只要一個線程會由等待池進入鎖池,而notifyAll會將該對象等待池內的全部線程移動到鎖池中,等待鎖競爭。
  • 優先級高的線程競爭到對象鎖的機率大,倘若某線程沒有競爭到該對象鎖,它還會留在鎖池中,惟有線程再次調用 wait()方法,它纔會從新回到等待池中。而競爭到對象鎖的線程則繼續往下執行,直到執行完了 synchronized 代碼塊,它會釋放掉該對象鎖,這時鎖池中的線程會繼續競爭該對象鎖。

43. 線程的 run()和 start()有什麼區別?

每一個線程都是經過某個特定Thread對象所對應的方法run()來完成其操做的,方法run()稱爲線程體。經過調用Thread類的start()方法來啓動一個線程。

start()方法來啓動一個線程,真正實現了多線程運行。這時無需等待run方法體代碼執行完畢,能夠直接繼續執行下面的代碼; 這時此線程是處於就緒狀態, 並無運行。 而後經過此Thread類調用方法run()來完成其運行狀態, 這裏方法run()稱爲線程體,它包含了要執行的這個線程的內容, Run方法運行結束, 此線程終止。而後CPU再調度其它線程。

run()方法是在本線程裏的,只是線程裏的一個函數,而不是多線程的。 若是直接調用run(),其實就至關因而調用了一個普通函數而已,直接待用run()方法必須等待run()方法執行完畢才能執行下面的代碼,因此執行路徑仍是隻有一條,根本就沒有線程的特徵,因此在多線程執行時要使用start()方法而不是run()方法。

44. 建立線程池有哪幾種方式?

①. newFixedThreadPool(int nThreads)

建立一個固定長度的線程池,每當提交一個任務就建立一個線程,直到達到線程池的最大數量,這時線程規模將再也不變化,當線程發生未預期的錯誤而結束時,線程池會補充一個新的線程。

②. newCachedThreadPool()

建立一個可緩存的線程池,若是線程池的規模超過了處理需求,將自動回收空閒線程,而當需求增長時,則能夠自動添加新線程,線程池的規模不存在任何限制。

③. newSingleThreadExecutor()

這是一個單線程的Executor,它建立單個工做線程來執行任務,若是這個線程異常結束,會建立一個新的來替代它;它的特色是能確保依照任務在隊列中的順序來串行執行。

④. newScheduledThreadPool(int corePoolSize)

建立了一個固定長度的線程池,並且以延遲或定時的方式來執行任務,相似於Timer。

45. 線程池都有哪些狀態?

線程池有5種狀態:Running、ShutDown、Stop、Tidying、Terminated。

線程池各個狀態切換框架圖:

46. 線程池中 submit()和 execute()方法有什麼區別?

  • 接收的參數不同
  • submit有返回值,而execute沒有
  • submit方便Exception處理

47. 在 java 程序中怎麼保證多線程的運行安全?

線程安全在三個方面體現:

  • 原子性:提供互斥訪問,同一時刻只能有一個線程對數據進行操做,(atomic,synchronized);
  • 可見性:一個線程對主內存的修改能夠及時地被其餘線程看到,(synchronized,volatile);
  • 有序性:一個線程觀察其餘線程中的指令執行順序,因爲指令重排序,該觀察結果通常雜亂無序,(happens-before原則)。

48. 多線程鎖的升級原理是什麼?

在Java中,鎖共有4種狀態,級別從低到高依次爲:無狀態鎖,偏向鎖,輕量級鎖和重量級鎖狀態,這幾個狀態會隨着競爭狀況逐漸升級。鎖能夠升級但不能降級。

鎖升級的圖示過程:

49. 什麼是死鎖?

死鎖是指兩個或兩個以上的進程在執行過程當中,因爲競爭資源或者因爲彼此通訊而形成的一種阻塞的現象,若無外力做用,它們都將沒法推動下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱爲死鎖進程。是操做系統層面的一個錯誤,是進程死鎖的簡稱,最先在 1965 年由 Dijkstra 在研究銀行家算法時提出的,它是計算機操做系統乃至整個併發程序設計領域最難處理的問題之一。

50. 怎麼防止死鎖?

死鎖的四個必要條件:

  • 互斥條件:進程對所分配到的資源不容許其餘進程進行訪問,若其餘進程訪問該資源,只能等待,直至佔有該資源的進程使用完成後釋放該資源
  • 請求和保持條件:進程得到必定的資源以後,又對其餘資源發出請求,可是該資源可能被其餘進程佔有,此事請求阻塞,但又對本身得到的資源保持不放
  • 不可剝奪條件:是指進程已得到的資源,在未完成使用以前,不可被剝奪,只能在使用完後本身釋放
  • 環路等待條件:是指進程發生死鎖後,若干進程之間造成一種頭尾相接的循環等待資源關係

這四個條件是死鎖的必要條件,只要系統發生死鎖,這些條件必然成立,而只要上述條件之 一不知足,就不會發生死鎖。

理解了死鎖的緣由,尤爲是產生死鎖的四個必要條件,就能夠最大可能地避免、預防和 解除死鎖。

因此,在系統設計、進程調度等方面注意如何不讓這四個必要條件成立,如何確 定資源的合理分配算法,避免進程永久佔據系統資源。

此外,也要防止進程在處於等待狀態的狀況下佔用資源。所以,對資源的分配要給予合理的規劃。

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

線程局部變量是侷限於線程內部的變量,屬於線程自身全部,不在多個線程間共享。Java提供ThreadLocal類來支持線程局部變量,是一種實現線程安全的方式。可是在管理環境下(如 web 服務器)使用線程局部變量的時候要特別當心,在這種狀況下,工做線程的生命週期比任何應用變量的生命週期都要長。任何線程局部變量一旦在工做完成後沒有釋放,Java 應用就存在內存泄露的風險。

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

synchronized能夠保證方法或者代碼塊在運行時,同一時刻只有一個方法能夠進入到臨界區,同時它還能夠保證共享變量的內存可見性。

Java中每個對象均可以做爲鎖,這是synchronized實現同步的基礎:

  • 普通同步方法,鎖是當前實例對象
  • 靜態同步方法,鎖是當前類的class對象
  • 同步方法塊,鎖是括號裏面的對象

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

  • volatile本質是在告訴jvm當前變量在寄存器(工做內存)中的值是不肯定的,須要從主存中讀取; synchronized則是鎖定當前變量,只有當前線程能夠訪問該變量,其餘線程被阻塞住。
  • volatile僅能使用在變量級別;synchronized則能夠使用在變量、方法、和類級別的。
  • volatile僅能實現變量的修改可見性,不能保證原子性;而synchronized則能夠保證變量的修改可見性和原子性。
  • volatile不會形成線程的阻塞;synchronized可能會形成線程的阻塞。
  • volatile標記的變量不會被編譯器優化;synchronized標記的變量能夠被編譯器優化。

54. synchronized 和 Lock 有什麼區別?

  • 首先synchronized是java內置關鍵字,在jvm層面,Lock是個java類;
  • synchronized沒法判斷是否獲取鎖的狀態,Lock能夠判斷是否獲取到鎖;
  • synchronized會自動釋放鎖(a 線程執行完同步代碼會釋放鎖 ;b 線程執行過程當中發生異常會釋放鎖),Lock需在finally中手工釋放鎖(unlock()方法釋放鎖),不然容易形成線程死鎖;
  • 用synchronized關鍵字的兩個線程1和線程2,若是當前線程1得到鎖,線程2線程等待。若是線程1阻塞,線程2則會一直等待下去,而Lock鎖就不必定會等待下去,若是嘗試獲取不到鎖,線程能夠不用一直等待就結束了;
  • synchronized的鎖可重入、不可中斷、非公平,而Lock鎖可重入、可判斷、可公平(二者皆可);
  • Lock鎖適合大量同步的代碼的同步問題,synchronized鎖適合代碼少許的同步問題。

55. synchronized 和 ReentrantLock 區別是什麼?

synchronized是和if、else、for、while同樣的關鍵字,ReentrantLock是類,這是兩者的本質區別。既然ReentrantLock是類,那麼它就提供了比synchronized更多更靈活的特性,能夠被繼承、能夠有方法、能夠有各類各樣的類變量,ReentrantLock比synchronized的擴展性體如今幾點上:

  • ReentrantLock能夠對獲取鎖的等待時間進行設置,這樣就避免了死鎖
  • ReentrantLock能夠獲取各類鎖的信息
  • ReentrantLock能夠靈活地實現多路通知

另外,兩者的鎖機制其實也是不同的:ReentrantLock底層調用的是Unsafe的park方法加鎖,synchronized操做的應該是對象頭中mark word。

56. 說一下 atomic 的原理?

Atomic包中的類基本的特性就是在多線程環境下,當有多個線程同時對單個(包括基本類型及引用類型)變量進行操做時,具備排他性,即當多個線程同時對該變量的值進行更新時,僅有一個線程能成功,而未成功的線程能夠向自旋鎖同樣,繼續嘗試,一直等到執行成功。

Atomic系列的類中的核心方法都會調用unsafe類中的幾個本地方法。咱們須要先知道一個東西就是Unsafe類,全名爲:sun.misc.Unsafe,這個類包含了大量的對C代碼的操做,包括不少直接內存分配以及原子操做的調用,而它之因此標記爲非安全的,是告訴你這個裏面大量的方法調用都會存在安全隱患,須要當心使用,不然會致使嚴重的後果,例如在經過unsafe分配內存的時候,若是本身指定某些區域可能會致使一些相似C++同樣的指針越界到其餘進程的問題。

4、反射

57. 什麼是反射?

反射主要是指程序能夠訪問、檢測和修改它自己狀態或行爲的一種能力

Java反射:

在Java運行時環境中,對於任意一個類,可否知道這個類有哪些屬性和方法?對於任意一個對象,可否調用它的任意一個方法

Java反射機制主要提供瞭如下功能:

  • 在運行時判斷任意一個對象所屬的類。
  • 在運行時構造任意一個類的對象。
  • 在運行時判斷任意一個類所具備的成員變量和方法。
  • 在運行時調用任意一個對象的方法。

58. 什麼是 java 序列化?什麼狀況下須要序列化?

簡單說就是爲了保存在內存中的各類對象的狀態(也就是實例變量,不是方法),而且能夠把保存的對象狀態再讀出來。雖然你能夠用你本身的各類各樣的方法來保存object states,可是Java給你提供一種應該比你本身好的保存對象狀態的機制,那就是序列化。

什麼狀況下須要序列化:

a)當你想把的內存中的對象狀態保存到一個文件中或者數據庫中時候;

b)當你想用套接字在網絡上傳送對象的時候;

c)當你想經過RMI傳輸對象的時候;

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

動態代理:

當想要給實現了某個接口的類中的方法,加一些額外的處理。好比說加日誌,加事務等。能夠給這個類建立一個代理,故名思議就是建立一個新的類,這個類不只包含原來類方法的功能,並且還在原來的基礎上添加了額外處理的新類。這個代理類並非定義好的,是動態生成的。具備解耦意義,靈活,擴展性強。

動態代理的應用:

  • Spring的AOP
  • 加事務
  • 加權限
  • 加日誌

60. 怎麼實現動態代理?

首先必須定義一個接口,還要有一個InvocationHandler(將實現接口的類的對象傳遞給它)處理類。再有一個工具類Proxy(習慣性將其稱爲代理類,由於調用他的newInstance()能夠產生代理對象,其實他只是一個產生代理對象的工具類)。利用到InvocationHandler,拼接代理類源碼,將其編譯生成代理類的二進制碼,利用加載器加載,並將其實例化產生代理對象,最後返回。

5、對象拷貝

61. 爲何要使用克隆?

想對一個對象進行處理,又想保留原有的數據進行接下來的操做,就須要克隆了,Java語言中克隆針對的是類的實例。

62. 如何實現對象克隆?

有兩種方式:

1). 實現Cloneable接口並重寫Object類中的clone()方法;

2). 實現Serializable接口,經過對象的序列化和反序列化實現克隆,能夠實現真正的深度克隆,代碼以下:

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
 
public class MyUtil {
 
 private MyUtil() {
 throw new AssertionError();
 }
 
 @SuppressWarnings("unchecked")
 public static <T extends Serializable> T clone(T obj) throws Exception {
 ByteArrayOutputStream bout = new ByteArrayOutputStream();
 ObjectOutputStream oos = new ObjectOutputStream(bout);
 oos.writeObject(obj);
 
 ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
 ObjectInputStream ois = new ObjectInputStream(bin);
 return (T) ois.readObject();
 
 // 說明:調用ByteArrayInputStream或ByteArrayOutputStream對象的close方法沒有任何意義
 // 這兩個基於內存的流只要垃圾回收器清理對象就可以釋放資源,這一點不一樣於對外部資源(如文件流)的釋放
 }
}複製代碼

下面是測試代碼:

import java.io.Serializable;
 
/**
 * 人類
 * @author nnngu
 *
 */
class Person implements Serializable {
 private static final long serialVersionUID = -9102017020286042305L;
 
 private String name; // 姓名
 private int age; // 年齡
 private Car car; // 座駕
 
 public Person(String name, int age, Car car) {
 this.name = name;
 this.age = age;
 this.car = car;
 }
 
 public String getName() {
 return name;
 }
 
 public void setName(String name) {
 this.name = name;
 }
 
 public int getAge() {
 return age;
 }
 
 public void setAge(int age) {
 this.age = age;
 }
 
 public Car getCar() {
 return car;
 }
 
 public void setCar(Car car) {
 this.car = car;
 }
 
 @Override
 public String toString() {
 return "Person [name=" + name + ", age=" + age + ", car=" + car + "]";
 }
 
}/**
 * 小汽車類
 * @author nnngu
 *
 */
class Car implements Serializable {
 private static final long serialVersionUID = -5713945027627603702L;
 
 private String brand; // 品牌
 private int maxSpeed; // 最高時速
 
 public Car(String brand, int maxSpeed) {
 this.brand = brand;
 this.maxSpeed = maxSpeed;
 }
 
 public String getBrand() {
 return brand;
 }
 
 public void setBrand(String brand) {
 this.brand = brand;
 }
 
 public int getMaxSpeed() {
 return maxSpeed;
 }
 
 public void setMaxSpeed(int maxSpeed) {
 this.maxSpeed = maxSpeed;
 }
 
 @Override
 public String toString() {
 return "Car [brand=" + brand + ", maxSpeed=" + maxSpeed + "]";
 }
 
}複製代碼


class CloneTest {
 
 public static void main(String[] args) {
 try {
 Person p1 = new Person("郭靖", 33, new Car("Benz", 300));
 Person p2 = MyUtil.clone(p1); // 深度克隆
 p2.getCar().setBrand("BYD");
 // 修改克隆的Person對象p2關聯的汽車對象的品牌屬性
 // 原來的Person對象p1關聯的汽車不會受到任何影響
 // 由於在克隆Person對象時其關聯的汽車對象也被克隆了
 System.out.println(p1);
 } catch (Exception e) {
 e.printStackTrace();
 }
 }
}複製代碼

注意:基於序列化和反序列化實現的克隆不只僅是深度克隆,更重要的是經過泛型限定,能夠檢查出要克隆的對象是否支持序列化,這項檢查是編譯器完成的,不是在運行時拋出異常,這種是方案明顯優於使用Object類的clone方法克隆對象。讓問題在編譯的時候暴露出來老是好過把問題留到運行時。

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

  • 淺拷貝只是複製了對象的引用地址,兩個對象指向同一個內存地址,因此修改其中任意的值,另外一個值都會隨之變化,這就是淺拷貝(例:assign())
  • 深拷貝是將對象及值複製過來,兩個對象修改其中任意的值另外一個值不會改變,這就是深拷貝(例:JSON.parse()和JSON.stringify(),可是此方法沒法複製函數類型)

6、Java Web

64. jsp 和 servlet 有什麼區別?

  1. jsp經編譯後就變成了Servlet.(JSP的本質就是Servlet,JVM只能識別java的類,不能識別JSP的代碼,Web容器將JSP的代碼編譯成JVM可以識別的java類)
  2. jsp更擅長表現於頁面顯示,servlet更擅長於邏輯控制。
  3. Servlet中沒有內置對象,Jsp中的內置對象都是必須經過HttpServletRequest對象,HttpServletResponse對象以及HttpServlet對象獲得。
  4. Jsp是Servlet的一種簡化,使用Jsp只須要完成程序員須要輸出到客戶端的內容,Jsp中的Java腳本如何鑲嵌到一個類中,由Jsp容器完成。而Servlet則是個完整的Java類,這個類的Service方法用於生成對客戶端的響應。

65. jsp 有哪些內置對象?做用分別是什麼?

JSP有9個內置對象:

  • request:封裝客戶端的請求,其中包含來自GET或POST請求的參數;
  • response:封裝服務器對客戶端的響應;
  • pageContext:經過該對象能夠獲取其餘對象;
  • session:封裝用戶會話的對象;
  • application:封裝服務器運行環境的對象;
  • out:輸出服務器響應的輸出流對象;
  • config:Web應用的配置對象;
  • page:JSP頁面自己(至關於Java程序中的this);
  • exception:封裝頁面拋出異常的對象。

66. 說一下 jsp 的 4 種做用域?

JSP中的四種做用域包括page、request、session和application,具體來講:

  • page表明與一個頁面相關的對象和屬性。
  • request表明與Web客戶機發出的一個請求相關的對象和屬性。一個請求可能跨越多個頁面,涉及多個Web組件;須要在頁面顯示的臨時數據能夠置於此做用域。
  • session表明與某個用戶與服務器創建的一次會話相關的對象和屬性。跟某個用戶相關的數據應該放在用戶本身的session中。
  • application表明與整個Web應用程序相關的對象和屬性,它實質上是跨越整個Web應用程序,包括多個頁面、請求和會話的一個全局做用域。

67. session 和 cookie 有什麼區別?

  • 因爲HTTP協議是無狀態的協議,因此服務端須要記錄用戶的狀態時,就須要用某種機制來識具體的用戶,這個機制就是Session.典型的場景好比購物車,當你點擊下單按鈕時,因爲HTTP協議無狀態,因此並不知道是哪一個用戶操做的,因此服務端要爲特定的用戶建立了特定的Session,用用於標識這個用戶,而且跟蹤用戶,這樣才知道購物車裏面有幾本書。這個Session是保存在服務端的,有一個惟一標識。在服務端保存Session的方法不少,內存、數據庫、文件都有。集羣的時候也要考慮Session的轉移,在大型的網站,通常會有專門的Session服務器集羣,用來保存用戶會話,這個時候 Session 信息都是放在內存的,使用一些緩存服務好比Memcached之類的來放 Session。
  • 思考一下服務端如何識別特定的客戶?這個時候Cookie就登場了。每次HTTP請求的時候,客戶端都會發送相應的Cookie信息到服務端。實際上大多數的應用都是用 Cookie 來實現Session跟蹤的,第一次建立Session的時候,服務端會在HTTP協議中告訴客戶端,須要在 Cookie 裏面記錄一個Session ID,之後每次請求把這個會話ID發送到服務器,我就知道你是誰了。有人問,若是客戶端的瀏覽器禁用了 Cookie 怎麼辦?通常這種狀況下,會使用一種叫作URL重寫的技術來進行會話跟蹤,即每次HTTP交互,URL後面都會被附加上一個諸如 sid=xxxxx 這樣的參數,服務端據此來識別用戶。
  • Cookie其實還能夠用在一些方便用戶的場景下,設想你某次登錄過一個網站,下次登陸的時候不想再次輸入帳號了,怎麼辦?這個信息能夠寫到Cookie裏面,訪問網站的時候,網站頁面的腳本能夠讀取這個信息,就自動幫你把用戶名給填了,可以方便一下用戶。這也是Cookie名稱的由來,給用戶的一點甜頭。因此,總結一下:Session是在服務端保存的一個數據結構,用來跟蹤用戶的狀態,這個數據能夠保存在集羣、數據庫、文件中;Cookie是客戶端保存用戶信息的一種機制,用來記錄用戶的一些信息,也是實現Session的一種方式。

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

其實session是一個存在服務器上的相似於一個散列表格的文件。裏面存有咱們須要的信息,在咱們須要用的時候能夠從裏面取出來。相似於一個大號的map吧,裏面的鍵存儲的是用戶的sessionid,用戶向服務器發送請求的時候會帶上這個sessionid。這時就能夠從中取出對應的值了。

69. 若是客戶端禁止 cookie 能實現 session 還能用嗎?

Cookie與 Session,通常認爲是兩個獨立的東西,Session採用的是在服務器端保持狀態的方案,而Cookie採用的是在客戶端保持狀態的方案。但爲何禁用Cookie就不能獲得Session呢?由於Session是用Session ID來肯定當前對話所對應的服務器Session,而Session ID是經過Cookie來傳遞的,禁用Cookie至關於失去了Session ID,也就得不到Session了。

假定用戶關閉Cookie的狀況下使用Session,其實現途徑有如下幾種:

  1. 設置php.ini配置文件中的「session.use_trans_sid = 1」,或者編譯時打開打開了「--enable-trans-sid」選項,讓PHP自動跨頁傳遞Session ID。
  2. 手動經過URL傳值、隱藏表單傳遞Session ID。
  3. 用文件、數據庫等形式保存Session ID,在跨頁過程當中手動調用。

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

  • 攔截機制的不一樣

Struts2是類級別的攔截,每次請求就會建立一個Action,和Spring整合時Struts2的ActionBean注入做用域是原型模式prototype,而後經過setter,getter吧request數據注入到屬性。Struts2中,一個Action對應一個request,response上下文,在接收參數時,能夠經過屬性接收,這說明屬性參數是讓多個方法共享的。Struts2中Action的一個方法能夠對應一個url,而其類屬性卻被全部方法共享,這也就沒法用註解或其餘方式標識其所屬方法了,只能設計爲多例。

SpringMVC是方法級別的攔截,一個方法對應一個Request上下文,因此方法直接基本上是獨立的,獨享request,response數據。而每一個方法同時又何一個url對應,參數的傳遞是直接注入到方法中的,是方法所獨有的。處理結果經過ModeMap返回給框架。在Spring整合時,SpringMVC的Controller Bean默認單例模式Singleton,因此默認對全部的請求,只會建立一個Controller,有應爲沒有共享的屬性,因此是線程安全的,若是要改變默認的做用域,須要添加@Scope註解修改。

Struts2有本身的攔截Interceptor機制,SpringMVC這是用的是獨立的Aop方式,這樣致使Struts2的配置文件量仍是比SpringMVC大。

  • 底層框架的不一樣

Struts2採用Filter(StrutsPrepareAndExecuteFilter)實現,SpringMVC(DispatcherServlet)則採用Servlet實現。Filter在容器啓動以後即初始化;服務中止之後墜毀,晚於Servlet。Servlet在是在調用時初始化,先於Filter調用,服務中止後銷燬。

  • 性能方面

Struts2是類級別的攔截,每次請求對應實例一個新的Action,須要加載全部的屬性值注入,SpringMVC實現了零配置,因爲SpringMVC基於方法的攔截,有加載一次單例模式bean注入。因此,SpringMVC開發效率和性能高於Struts2。

  • 配置方面

spring MVC和Spring是無縫的。從這個項目的管理和安全上也比Struts2高。

71. 如何避免 sql 注入?

  1. PreparedStatement(簡單又有效的方法)
  2. 使用正則表達式過濾傳入的參數
  3. 字符串過濾
  4. JSP中調用該函數檢查是否包函非法字符
  5. JSP頁面判斷代碼

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

XSS攻擊又稱CSS,全稱Cross Site Script (跨站腳本攻擊),其原理是攻擊者向有XSS漏洞的網站中輸入惡意的 HTML 代碼,當用戶瀏覽該網站時,這段 HTML 代碼會自動執行,從而達到攻擊的目的。XSS 攻擊相似於 SQL 注入攻擊,SQL注入攻擊中以SQL語句做爲用戶輸入,從而達到查詢/修改/刪除數據的目的,而在xss攻擊中,經過插入惡意腳本,實現對用戶遊覽器的控制,獲取用戶的一些信息。 XSS是 Web 程序中常見的漏洞,XSS 屬於被動式且用於客戶端的攻擊方式。

XSS防範的整體思路是:對輸入(和URL參數)進行過濾,對輸出進行編碼。

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

CSRF(Cross-site request forgery)也被稱爲 one-click attack或者 session riding,中文全稱是叫跨站請求僞造。通常來講,攻擊者經過僞造用戶的瀏覽器的請求,向訪問一個用戶本身曾經認證訪問過的網站發送出去,使目標網站接收並誤覺得是用戶的真實操做而去執行命令。經常使用於盜取帳號、轉帳、發送虛假消息等。攻擊者利用網站對請求的驗證漏洞而實現這樣的攻擊行爲,網站可以確認請求來源於用戶的瀏覽器,卻不能驗證請求是否源於用戶的真實意願下的操做行爲。

如何避免:

1. 驗證 HTTP Referer 字段

HTTP頭中的Referer字段記錄了該 HTTP 請求的來源地址。在一般狀況下,訪問一個安全受限頁面的請求來自於同一個網站,而若是黑客要對其實施 CSRF攻擊,他通常只能在他本身的網站構造請求。所以,能夠經過驗證Referer值來防護CSRF 攻擊。

2. 使用驗證碼

關鍵操做頁面加上驗證碼,後臺收到請求後經過判斷驗證碼能夠防護CSRF。但這種方法對用戶不太友好。

3. 在請求地址中添加token並驗證

CSRF 攻擊之因此可以成功,是由於黑客能夠徹底僞造用戶的請求,該請求中全部的用戶驗證信息都是存在於cookie中,所以黑客能夠在不知道這些驗證信息的狀況下直接利用用戶本身的cookie 來經過安全驗證。要抵禦 CSRF,關鍵在於在請求中放入黑客所不能僞造的信息,而且該信息不存在於 cookie 之中。能夠在 HTTP 請求中以參數的形式加入一個隨機產生的 token,並在服務器端創建一個攔截器來驗證這個 token,若是請求中沒有token或者 token 內容不正確,則認爲多是 CSRF 攻擊而拒絕該請求。這種方法要比檢查 Referer 要安全一些,token 能夠在用戶登錄後產生並放於session之中,而後在每次請求時把token 從 session 中拿出,與請求中的 token 進行比對,但這種方法的難點在於如何把 token 以參數的形式加入請求。對於 GET 請求,token 將附在請求地址以後,這樣 URL 就變成 http://url?csrftoken=tokenvalue。而對於 POST 請求來講,要在 form 的最後加上 <input type="hidden" name="csrftoken" value="tokenvalue"/>,這樣就把token以參數的形式加入請求了。

4. 在HTTP 頭中自定義屬性並驗證

這種方法也是使用 token 並進行驗證,和上一種方法不一樣的是,這裏並非把 token 以參數的形式置於 HTTP 請求之中,而是把它放到 HTTP 頭中自定義的屬性裏。經過 XMLHttpRequest 這個類,能夠一次性給全部該類請求加上 csrftoken 這個 HTTP 頭屬性,並把 token 值放入其中。這樣解決了上種方法在請求中加入 token 的不便,同時,經過 XMLHttpRequest 請求的地址不會被記錄到瀏覽器的地址欄,也不用擔憂 token 會透過 Referer 泄露到其餘網站中去。

7、異常

74. throw 和 throws 的區別?

throws是用來聲明一個方法可能拋出的全部異常信息,throws是將異常聲明可是不處理,而是將異常往上傳,誰調用我就交給誰處理。而throw則是指拋出的一個具體的異常類型。

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

  • final能夠修飾類、變量、方法,修飾類表示該類不能被繼承、修飾方法表示該方法不能被重寫、修飾變量表示該變量是一個常量不能被從新賦值。
  • finally通常做用在try-catch代碼塊中,在處理異常的時候,一般咱們將必定要執行的代碼方法finally代碼塊中,表示無論是否出現異常,該代碼塊都會執行,通常用來存放一些關閉資源的代碼。
  • finalize是一個方法,屬於Object類的一個方法,而Object類是全部類的父類,該方法通常由垃圾回收器來調用,當咱們調用System的gc()方法的時候,由垃圾回收器調用finalize(),回收垃圾。

76. try-catch-finally 中哪一個部分能夠省略?

答:catch 能夠省略

緣由:

更爲嚴格的說法實際上是:try只適合處理運行時異常,try+catch適合處理運行時異常+普通異常。也就是說,若是你只用try去處理普通異常卻不加以catch處理,編譯是通不過的,由於編譯器硬性規定,普通異常若是選擇捕獲,則必須用catch顯示聲明以便進一步處理。而運行時異常在編譯時沒有如此規定,因此catch能夠省略,你加上catch編譯器也以爲無可厚非。

理論上,編譯器看任何代碼都不順眼,都以爲可能有潛在的問題,因此你即便對全部代碼加上try,代碼在運行期時也只不過是在正常運行的基礎上加一層皮。可是你一旦對一段代碼加上try,就等於顯示地承諾編譯器,對這段代碼可能拋出的異常進行捕獲而非向上拋出處理。若是是普通異常,編譯器要求必須用catch捕獲以便進一步處理;若是運行時異常,捕獲而後丟棄而且+finally掃尾處理,或者加上catch捕獲以便進一步處理。

至於加上finally,則是在無論有沒捕獲異常,都要進行的「掃尾」處理。

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

答:會執行,在 return 前執行。

代碼示例1:

/* * java面試題--若是catch裏面有return語句,finally裏面的代碼還會執行嗎? */public class FinallyDemo2 { public static void main(String[] args) { System.out.println(getInt()); } public static int getInt() { int a = 10; try { System.out.println(a / 0); a = 20; } catch (ArithmeticException e) { a = 30; return a; /* * return a 在程序執行到這一步的時候,這裏不是return a 而是 return 30;這個返回路徑就造成了 * 可是呢,它發現後面還有finally,因此繼續執行finally的內容,a=40 * 再次回到之前的路徑,繼續走return 30,造成返回路徑以後,這裏的a就不是a變量了,而是常量30 */ } finally { a = 40; }// return a; }}複製代碼

執行結果:30

代碼示例2:

package com.java_02;/* * java面試題--若是catch裏面有return語句,finally裏面的代碼還會執行嗎? */public class FinallyDemo2 { public static void main(String[] args) { System.out.println(getInt()); } public static int getInt() { int a = 10; try { System.out.println(a / 0); a = 20; } catch (ArithmeticException e) { a = 30; return a; /* * return a 在程序執行到這一步的時候,這裏不是return a 而是 return 30;這個返回路徑就造成了 * 可是呢,它發現後面還有finally,因此繼續執行finally的內容,a=40 * 再次回到之前的路徑,繼續走return 30,造成返回路徑以後,這裏的a就不是a變量了,而是常量30 */ } finally { a = 40; return a; //若是這樣,就又從新造成了一條返回路徑,因爲只能經過1個return返回,因此這裏直接返回40 }// return a; }}複製代碼

執行結果:40

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

  • NullPointerException:當應用程序試圖訪問空對象時,則拋出該異常。
  • SQLException:提供關於數據庫訪問錯誤或其餘錯誤信息的異常。
  • IndexOutOfBoundsException:指示某排序索引(例如對數組、字符串或向量的排序)超出範圍時拋出。
  • NumberFormatException:當應用程序試圖將字符串轉換成一種數值類型,但該字符串不能轉換爲適當格式時,拋出該異常。
  • FileNotFoundException:當試圖打開指定路徑名錶示的文件失敗時,拋出此異常。
  • IOException:當發生某種I/O異常時,拋出此異常。此類是失敗或中斷的I/O操做生成的異常的通用類。
  • ClassCastException:當試圖將對象強制轉換爲不是實例的子類時,拋出該異常。
  • ArrayStoreException:試圖將錯誤類型的對象存儲到一個對象數組時拋出的異常。
  • IllegalArgumentException:拋出的異常代表向方法傳遞了一個不合法或不正確的參數。
  • ArithmeticException:當出現異常的運算條件時,拋出此異常。例如,一個整數「除以零」時,拋出此類的一個實例。
  • NegativeArraySizeException:若是應用程序試圖建立大小爲負的數組,則拋出該異常。
  • NoSuchMethodException:沒法找到某一特定方法時,拋出該異常。
  • SecurityException:由安全管理器拋出的異常,指示存在安全侵犯。
  • UnsupportedOperationException:當不支持請求的操做時,拋出該異常。
  • RuntimeExceptionRuntimeException:是那些可能在Java虛擬機正常運行期間拋出的異常的超類。

8、網絡

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

答:301,302 都是HTTP狀態的編碼,都表明着某個URL發生了轉移。

區別:

  • 301 redirect: 301 表明永久性轉移(Permanently Moved)。
  • 302 redirect: 302 表明暫時性轉移(Temporarily Moved )。

80. forward 和 redirect 的區別?

Forward和Redirect表明了兩種請求轉發方式:直接轉發和間接轉發。

直接轉發方式(Forward),客戶端和瀏覽器只發出一次請求,Servlet、HTML、JSP或其它信息資源,由第二個信息資源響應該請求,在請求對象request中,保存的對象對於每一個信息資源是共享的。

間接轉發方式(Redirect)實際是兩次HTTP請求,服務器端在響應第一次請求的時候,讓瀏覽器再向另一個URL發出請求,從而達到轉發的目的。

舉個通俗的例子:

直接轉發就至關於:「A找B借錢,B說沒有,B去找C借,借到借不到都會把消息傳遞給A」;

間接轉發就至關於:"A找B借錢,B說沒有,讓A去找C借"。

81. 簡述 tcp 和 udp的區別?

  • TCP面向鏈接(如打電話要先撥號創建鏈接);UDP是無鏈接的,即發送數據以前不須要創建鏈接。
  • TCP提供可靠的服務。也就是說,經過TCP鏈接傳送的數據,無差錯,不丟失,不重複,且按序到達;UDP盡最大努力交付,即不保證可靠交付。
  • Tcp經過校驗和,重傳控制,序號標識,滑動窗口、確認應答實現可靠傳輸。如丟包時的重發控制,還能夠對次序亂掉的分包進行順序控制。
  • UDP具備較好的實時性,工做效率比TCP高,適用於對高速傳輸和實時性有較高的通訊或廣播通訊。
  • 每一條TCP鏈接只能是點到點的;UDP支持一對一,一對多,多對一和多對多的交互通訊。
  • TCP對系統資源要求較多,UDP對系統資源要求較少。

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

爲了實現可靠數據傳輸, TCP 協議的通訊雙方, 都必須維護一個序列號, 以標識發送出去的數據包中, 哪些是已經被對方收到的。 三次握手的過程便是通訊雙方相互告知序列號起始值, 並確認對方已經收到了序列號起始值的必經步驟。

若是隻是兩次握手, 至多隻有鏈接發起方的起始序列號能被確認, 另外一方選擇的序列號則得不到確認。

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

①. 發送方產生粘包

採用TCP協議傳輸數據的客戶端與服務器常常是保持一個長鏈接的狀態(一次鏈接發一次數據不存在粘包),雙方在鏈接不斷開的狀況下,能夠一直傳輸數據;但當發送的數據包過於的小時,那麼TCP協議默認的會啓用Nagle算法,將這些較小的數據包進行合併發送(緩衝區數據發送是一個堆壓的過程);這個合併過程就是在發送緩衝區中進行的,也就是說數據發送出來它已是粘包的狀態了。

②. 接收方產生粘包

接收方採用TCP協議接收數據時的過程是這樣的:數據到底接收方,從網絡模型的下方傳遞至傳輸層,傳輸層的TCP協議處理是將其放置接收緩衝區,而後由應用層來主動獲取(C語言用recv、read等函數);這時會出現一個問題,就是咱們在程序中調用的讀取數據函數不能及時的把緩衝區中的數據拿出來,而下一個數據又到來並有一部分放入的緩衝區末尾,等咱們讀取數據時就是一個粘包。(放數據的速度 > 應用層拿數據速度)

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

  1. 應用層:網絡服務與最終用戶的一個接口。
  2. 表示層:數據的表示、安全、壓縮。
  3. 會話層:創建、管理、終止會話。
  4. 傳輸層:定義傳輸數據的協議端口號,以及流控和差錯校驗。
  5. 網絡層:進行邏輯地址尋址,實現不一樣網絡之間的路徑選擇。
  6. 數據鏈路層:創建邏輯鏈接、進行硬件地址尋址、差錯校驗等功能。
  7. 物理層:創建、維護、斷開物理鏈接。

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

  • GET在瀏覽器回退時是無害的,而POST會再次提交請求。
  • GET產生的URL地址能夠被Bookmark,而POST不能夠。
  • GET請求會被瀏覽器主動cache,而POST不會,除非手動設置。
  • GET請求只能進行url編碼,而POST支持多種編碼方式。
  • GET請求參數會被完整保留在瀏覽器歷史記錄裏,而POST中的參數不會被保留。
  • GET請求在URL中傳送的參數是有長度限制的,而POST麼有。
  • 對參數的數據類型,GET只接受ASCII字符,而POST沒有限制。
  • GET比POST更不安全,由於參數直接暴露在URL上,因此不能用來傳遞敏感信息。
  • GET參數經過URL傳遞,POST放在Request body中。

86. 如何實現跨域?

方式一:圖片ping或script標籤跨域

圖片ping經常使用於跟蹤用戶點擊頁面或動態廣告曝光次數。

script標籤能夠獲得從其餘來源數據,這也是JSONP依賴的根據。

方式二:JSONP跨域

JSONP(JSON with Padding)是數據格式JSON的一種「使用模式」,可讓網頁從別的網域要數據。根據 XmlHttpRequest 對象受到同源策略的影響,而利用 <script>元素的這個開放策略,網頁能夠獲得從其餘來源動態產生的JSON數據,而這種使用模式就是所謂的 JSONP。用JSONP抓到的數據並非JSON,而是任意的JavaScript,用 JavaScript解釋器運行而不是用JSON解析器解析。全部,經過Chrome查看全部JSONP發送的Get請求都是js類型,而非XHR。

缺點:

  • 只能使用Get請求
  • 不能註冊success、error等事件監聽函數,不能很容易的肯定JSONP請求是否失敗
  • JSONP是從其餘域中加載代碼執行,容易受到跨站請求僞造的攻擊,其安全性沒法確保

方式三:CORS

Cross-Origin Resource Sharing(CORS)跨域資源共享是一份瀏覽器技術的規範,提供了 Web 服務從不一樣域傳來沙盒腳本的方法,以避開瀏覽器的同源策略,確保安全的跨域數據傳輸。現代瀏覽器使用CORS在API容器如XMLHttpRequest來減小HTTP請求的風險來源。與 JSONP 不一樣,CORS 除了 GET 要求方法之外也支持其餘的 HTTP 要求。服務器通常須要增長以下響應頭的一種或幾種:

Access-Control-Allow-Origin: *Access-Control-Allow-Methods: POST, GET, OPTIONSAccess-Control-Allow-Headers: X-PINGOTHER, Content-TypeAccess-Control-Max-Age: 86400複製代碼

跨域請求默認不會攜帶Cookie信息,若是須要攜帶,請配置下述參數:

"Access-Control-Allow-Credentials": true// Ajax設置"withCredentials": true複製代碼

方式四:window.name+iframe

window.name經過在iframe(通常動態建立i)中加載跨域HTML文件來起做用。而後,HTML文件將傳遞給請求者的字符串內容賦值給window.name。而後,請求者能夠檢索window.name值做爲響應。

  • iframe標籤的跨域能力;
  • window.name屬性值在文檔刷新後依舊存在的能力(且最大容許2M左右)。

每一個iframe都有包裹它的window,而這個window是top window的子窗口。contentWindow屬性返回<iframe>元素的Window對象。你能夠使用這個Window對象來訪問iframe的文檔及其內部DOM。

<!-- 下述用端口 10000表示:domainA 10001表示:domainB--><!-- localhost:10000 --><script> var iframe = document.createElement('iframe'); iframe.style.display = 'none'; // 隱藏 var state = 0; // 防止頁面無限刷新 iframe.onload = function() { if(state === 1) { console.log(JSON.parse(iframe.contentWindow.name)); // 清除建立的iframe iframe.contentWindow.document.write(''); iframe.contentWindow.close(); document.body.removeChild(iframe); } else if(state === 0) { state = 1; // 加載完成,指向當前域,防止錯誤(proxy.html爲空白頁面) // Blocked a frame with origin "http://localhost:10000" from accessing a cross-origin frame. iframe.contentWindow.location = 'http://localhost:10000/proxy.html'; } }; iframe.src = 'http://localhost:10001'; document.body.appendChild(iframe);</script><!-- localhost:10001 --><!DOCTYPE html>...<script>window.name = JSON.stringify({a: 1, b: 2});</script></html>複製代碼

方式五:window.postMessage()

HTML5新特性,能夠用來向其餘全部的 window 對象發送消息。須要注意的是咱們必需要保證全部的腳本執行完才發送 MessageEvent,若是在函數執行的過程當中調用了它,就會讓後面的函數超時沒法執行。

下述代碼實現了跨域存儲localStorage

<!-- 下述用端口 10000表示:domainA 10001表示:domainB--><!-- localhost:10000 --><iframe src="http://localhost:10001/msg.html" name="myPostMessage" style="display:none;"></iframe><script> function main() { LSsetItem('test', 'Test: ' + new Date()); LSgetItem('test', function(value) { console.log('value: ' + value); }); LSremoveItem('test'); } var callbacks = {}; window.addEventListener('message', function(event) { if (event.source === frames['myPostMessage']) { console.log(event) var data = /^#localStorage#(\d+)(null)?#([\S\s]*)/.exec(event.data); if (data) { if (callbacks[data[1]]) { callbacks[data[1]](data[2] === 'null' ? null : data[3]); } delete callbacks[data[1]]; } } }, false); var domain = '*'; // 增長 function LSsetItem(key, value) { var obj = { setItem: key, value: value }; frames['myPostMessage'].postMessage(JSON.stringify(obj), domain); } // 獲取 function LSgetItem(key, callback) { var identifier = new Date().getTime(); var obj = { identifier: identifier, getItem: key }; callbacks[identifier] = callback; frames['myPostMessage'].postMessage(JSON.stringify(obj), domain); } // 刪除 function LSremoveItem(key) { var obj = { removeItem: key }; frames['myPostMessage'].postMessage(JSON.stringify(obj), domain); }</script><!-- localhost:10001 --><script> window.addEventListener('message', function(event) { console.log('Receiver debugging', event); if (event.origin == 'http://localhost:10000') { var data = JSON.parse(event.data); if ('setItem' in data) { localStorage.setItem(data.setItem, data.value); } else if ('getItem' in data) { var gotItem = localStorage.getItem(data.getItem); event.source.postMessage( '#localStorage#' + data.identifier + (gotItem === null ? 'null#' : '#' + gotItem), event.origin ); } else if ('removeItem' in data) { localStorage.removeItem(data.removeItem); } } }, false);</script>複製代碼

注意Safari一下,會報錯:

Blocked a frame with origin 「http://localhost:10001」 from accessing a frame with origin 「http://localhost:10000「. Protocols, domains, and ports must match.

避免該錯誤,能夠在Safari瀏覽器中勾選開發菜單==>停用跨域限制。或者只能使用服務器端轉存的方式實現,由於Safari瀏覽器默認只支持CORS跨域請求。

方式六:修改document.domain跨子域

前提條件:這兩個域名必須屬於同一個基礎域名!並且所用的協議,端口都要一致,不然沒法利用document.domain進行跨域,因此只能跨子域

在根域範圍內,容許把domain屬性的值設置爲它的上一級域。例如,在」aaa.xxx.com」域內,能夠把domain設置爲 「xxx.com」 但不能設置爲 「XXX.com Porn - Free PORNO videos」 或者」com」。

如今存在兩個域名aaa.xxx.com和bbb.xxx.com。在aaa下嵌入bbb的頁面,因爲其document.name不一致,沒法在aaa下操做bbb的js。能夠在aaa和bbb下經過js將document.name = 'xxx.com';設置一致,來達到互相訪問的做用。

方式七:WebSocket

WebSocket protocol 是HTML5一種新的協議。它實現了瀏覽器與服務器全雙工通訊,同時容許跨域通信,是server push技術的一種很棒的實現。相關文章,請查看:WebSocket、WebSocket-SockJS

須要注意:WebSocket對象不支持DOM 2級事件偵聽器,必須使用DOM 0級語法分別定義各個事件。

方式八:代理

同源策略是針對瀏覽器端進行的限制,能夠經過服務器端來解決該問題

DomainA客戶端(瀏覽器) ==> DomainA服務器 ==> DomainB服務器 ==> DomainA客戶端(瀏覽器)

來源:blog.csdn.net/ligang2585116/article/details/73072868

87.說一下 JSONP 實現原理?

jsonp 即 json+padding,動態建立script標籤,利用script標籤的src屬性能夠獲取任何域下的js腳本,經過這個特性(也能夠說漏洞),服務器端不在返貨json格式,而是返回一段調用某個函數的js代碼,在src中進行了調用,這樣實現了跨域。

9、設計模式

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

參考:經常使用的設計模式彙總,超詳細!

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

簡單工廠模式:

這個模式自己很簡單並且使用在業務較簡單的狀況下。通常用於小項目或者具體產品不多擴展的狀況(這樣工廠類纔不用常常更改)。

它由三種角色組成:

  • 工廠類角色:這是本模式的核心,含有必定的商業邏輯和判斷邏輯,根據邏輯不一樣,產生具體的工廠產品。如例子中的Driver類。
  • 抽象產品角色:它通常是具體產品繼承的父類或者實現的接口。由接口或者抽象類來實現。如例中的Car接口。
  • 具體產品角色:工廠類所建立的對象就是此角色的實例。在java中由一個具體類實現,如例子中的Benz、Bmw類。

來用類圖來清晰的表示下的它們之間的關係:

抽象工廠模式:

先來認識下什麼是產品族: 位於不一樣產品等級結構中,功能相關聯的產品組成的家族。

圖中的BmwCar和BenzCar就是兩個產品樹(產品層次結構);而如圖所示的BenzSportsCar和BmwSportsCar就是一個產品族。他們均可以放到跑車家族中,所以功能有所關聯。同理BmwBussinessCar和BenzBusinessCar也是一個產品族。

能夠這麼說,它和工廠方法模式的區別就在於須要建立對象的複雜程度上。並且抽象工廠模式是三個裏面最爲抽象、最具通常性的。抽象工廠模式的用意爲:給客戶端提供一個接口,能夠建立多個產品族中的產品對象。

並且使用抽象工廠模式還要知足一下條件:

  1. 系統中有多個產品族,而系統一次只可能消費其中一族產品
  2. 同屬於同一個產品族的產品以其使用。

來看看抽象工廠模式的各個角色(和工廠方法的一模一樣):

  • 抽象工廠角色: 這是工廠方法模式的核心,它與應用程序無關。是具體工廠角色必須實現的接口或者必須繼承的父類。在java中它由抽象類或者接口來實現。
  • 具體工廠角色:它含有和具體業務邏輯有關的代碼。由應用程序調用以建立對應的具體產品的對象。在java中它由具體的類來實現。
  • 抽象產品角色:它是具體產品繼承的父類或者是實現的接口。在java中通常有抽象類或者接口來實現。
  • 具體產品角色:具體工廠角色所建立的對象就是此角色的實例。在java中由具體的類來實現。

10、Spring / Spring MVC

90. 爲何要使用 spring?

1.簡介

  • 目的:解決企業應用開發的複雜性
  • 功能:使用基本的JavaBean代替EJB,並提供了更多的企業應用功能
  • 範圍:任何Java應用

簡單來講,Spring是一個輕量級的控制反轉(IoC)和麪向切面(AOP)的容器框架。

2.輕量 

從大小與開銷兩方面而言Spring都是輕量的。完整的Spring框架能夠在一個大小隻有1MB多的JAR文件裏發佈。而且Spring所需的處理開銷也是微不足道的。此外,Spring是非侵入式的:典型地,Spring應用中的對象不依賴於Spring的特定類。

3.控制反轉

Spring經過一種稱做控制反轉(IoC)的技術促進了鬆耦合。當應用了IoC,一個對象依賴的其它對象會經過被動的方式傳遞進來,而不是這個對象本身建立或者查找依賴對象。你能夠認爲IoC與JNDI相反——不是對象從容器中查找依賴,而是容器在對象初始化時不等對象請求就主動將依賴傳遞給它。

4.面向切面

Spring提供了面向切面編程的豐富支持,容許經過分離應用的業務邏輯與系統級服務(例如審計(auditing)和事務(transaction)管理)進行內聚性的開發。應用對象只實現它們應該作的——完成業務邏輯——僅此而已。它們並不負責(甚至是意識)其它的系統級關注點,例如日誌或事務支持。

5.容器

Spring包含並管理應用對象的配置和生命週期,在這個意義上它是一種容器,你能夠配置你的每一個bean如何被建立——基於一個可配置原型(prototype),你的bean能夠建立一個單獨的實例或者每次須要時都生成一個新的實例——以及它們是如何相互關聯的。然而,Spring不該該被混同於傳統的重量級的EJB容器,它們常常是龐大與笨重的,難以使用。

6.框架

Spring能夠將簡單的組件配置、組合成爲複雜的應用。在Spring中,應用對象被聲明式地組合,典型地是在一個XML文件裏。Spring也提供了不少基礎功能(事務管理、持久化框架集成等等),將應用邏輯的開發留給了你。

全部Spring的這些特徵使你可以編寫更乾淨、更可管理、而且更易於測試的代碼。它們也爲Spring中的各類模塊提供了基礎支持。

91. 解釋一下什麼是 aop?

AOP(Aspect-Oriented Programming,面向方面編程),能夠說是OOP(Object-Oriented Programing,面向對象編程)的補充和完善。OOP引入封裝、繼承和多態性等概念來創建一種對象層次結構,用以模擬公共行爲的一個集合。當咱們須要爲分散的對象引入公共行爲的時候,OOP則顯得無能爲力。也就是說,OOP容許你定義從上到下的關係,但並不適合定義從左到右的關係。例如日誌功能。日誌代碼每每水平地散佈在全部對象層次中,而與它所散佈到的對象的核心功能毫無關係。對於其餘類型的代碼,如安全性、異常處理和透明的持續性也是如此。這種散佈在各處的無關的代碼被稱爲橫切(cross-cutting)代碼,在OOP設計中,它致使了大量代碼的重複,而不利於各個模塊的重用。

而AOP技術則偏偏相反,它利用一種稱爲「橫切」的技術,剖解開封裝的對象內部,並將那些影響了多個類的公共行爲封裝到一個可重用模塊,並將其名爲「Aspect」,即方面。所謂「方面」,簡單地說,就是將那些與業務無關,卻爲業務模塊所共同調用的邏輯或責任封裝起來,便於減小系統的重複代碼,下降模塊間的耦合度,並有利於將來的可操做性和可維護性。AOP表明的是一個橫向的關係,若是說「對象」是一個空心的圓柱體,其中封裝的是對象的屬性和行爲;那麼面向方面編程的方法,就彷彿一把利刃,將這些空心圓柱體剖開,以得到其內部的消息。而剖開的切面,也就是所謂的「方面」了。而後它又以巧奪天功的妙手將這些剖開的切面復原,不留痕跡。

使用「橫切」技術,AOP把軟件系統分爲兩個部分:核心關注點和橫切關注點。業務處理的主要流程是核心關注點,與之關係不大的部分是橫切關注點。橫切關注點的一個特色是,他們常常發生在覈心關注點的多處,而各處都基本類似。好比權限認證、日誌、事務處理。Aop 的做用在於分離系統中的各類關注點,將核心關注點和橫切關注點分離開來。正如Avanade公司的高級方案構架師Adam Magee所說,AOP的核心思想就是「將應用程序中的商業邏輯同對其提供支持的通用服務進行分離。」

92. 解釋一下什麼是 ioc?

IOC是Inversion of Control的縮寫,多數書籍翻譯成「控制反轉」。

1996年,Michael Mattson在一篇有關探討面向對象框架的文章中,首先提出了IOC 這個概念。對於面向對象設計及編程的基本思想,前面咱們已經講了不少了,再也不贅述,簡單來講就是把複雜系統分解成相互合做的對象,這些對象類經過封裝之後,內部實現對外部是透明的,從而下降了解決問題的複雜度,並且能夠靈活地被重用和擴展。

IOC理論提出的觀點大致是這樣的:藉助於「第三方」實現具備依賴關係的對象之間的解耦。以下圖:

你們看到了吧,因爲引進了中間位置的「第三方」,也就是IOC容器,使得A、B、C、D這4個對象沒有了耦合關係,齒輪之間的傳動所有依靠「第三方」了,所有對象的控制權所有上繳給「第三方」IOC容器,因此,IOC容器成了整個系統的關鍵核心,它起到了一種相似「粘合劑」的做用,把系統中的全部對象粘合在一塊兒發揮做用,若是沒有這個「粘合劑」,對象與對象之間會彼此失去聯繫,這就是有人把IOC容器比喻成「粘合劑」的由來。

咱們再來作個試驗:把上圖中間的IOC容器拿掉,而後再來看看這套系統:

咱們如今看到的畫面,就是咱們要實現整個系統所須要完成的所有內容。這時候,A、B、C、D這4個對象之間已經沒有了耦合關係,彼此毫無聯繫,這樣的話,當你在實現A的時候,根本無須再去考慮B、C和D了,對象之間的依賴關係已經下降到了最低程度。因此,若是真能實現IOC容器,對於系統開發而言,這將是一件多麼美好的事情,參與開發的每一成員只要實現本身的類就能夠了,跟別人沒有任何關係!

咱們再來看看,控制反轉(IOC)到底爲何要起這麼個名字?咱們來對比一下:

軟件系統在沒有引入IOC容器以前,如圖1所示,對象A依賴於對象B,那麼對象A在初始化或者運行到某一點的時候,本身必須主動去建立對象B或者使用已經建立的對象B。不管是建立仍是使用對象B,控制權都在本身手上。

軟件系統在引入IOC容器以後,這種情形就徹底改變了,如圖3所示,因爲IOC容器的加入,對象A與對象B之間失去了直接聯繫,因此,當對象A運行到須要對象B的時候,IOC容器會主動建立一個對象B注入到對象A須要的地方。

經過先後的對比,咱們不難看出來:對象A得到依賴對象B的過程,由主動行爲變爲了被動行爲,控制權顛倒過來了,這就是「控制反轉」這個名稱的由來。

93. spring 有哪些主要模塊?

Spring框架至今已集成了20多個模塊。這些模塊主要被分以下圖所示的核心容器、數據訪問/集成,、Web、AOP(面向切面編程)、工具、消息和測試模塊。

更多信息:Spring Tutorials - HowToDoInJava

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

Spring經過DI(依賴注入)實現IOC(控制反轉),經常使用的注入方式主要有三種:

  1. 構造方法注入
  2. setter注入
  3. 基於註解的注入

95. spring 中的 bean 是線程安全的嗎?

Spring容器中的Bean是否線程安全,容器自己並無提供Bean的線程安全策略,所以能夠說spring容器中的Bean自己不具有線程安全的特性,可是具體仍是要結合具體scope的Bean去研究。

96. spring 支持幾種 bean 的做用域?

當經過spring容器建立一個Bean實例時,不只能夠完成Bean實例的實例化,還能夠爲Bean指定特定的做用域。Spring支持以下5種做用域:

  • singleton:單例模式,在整個Spring IoC容器中,使用singleton定義的Bean將只有一個實例
  • prototype:原型模式,每次經過容器的getBean方法獲取prototype定義的Bean時,都將產生一個新的Bean實例
  • request:對於每次HTTP請求,使用request定義的Bean都將產生一個新實例,即每次HTTP請求將會產生不一樣的Bean實例。只有在Web應用中使用Spring時,該做用域纔有效
  • session:對於每次HTTP Session,使用session定義的Bean豆漿產生一個新實例。一樣只有在Web應用中使用Spring時,該做用域纔有效
  • globalsession:每一個全局的HTTP Session,使用session定義的Bean都將產生一個新實例。典型狀況下,僅在使用portlet context的時候有效。一樣只有在Web應用中使用Spring時,該做用域纔有效

其中比較經常使用的是singleton和prototype兩種做用域。對於singleton做用域的Bean,每次請求該Bean都將得到相同的實例。容器負責跟蹤Bean實例的狀態,負責維護Bean實例的生命週期行爲;若是一個Bean被設置成prototype做用域,程序每次請求該id的Bean,Spring都會新建一個Bean實例,而後返回給程序。在這種狀況下,Spring容器僅僅使用new 關鍵字建立Bean實例,一旦建立成功,容器不在跟蹤實例,也不會維護Bean實例的狀態。

若是不指定Bean的做用域,Spring默認使用singleton做用域。Java在建立Java實例時,須要進行內存申請;銷燬實例時,須要完成垃圾回收,這些工做都會致使系統開銷的增長。所以,prototype做用域Bean的建立、銷燬代價比較大。而singleton做用域的Bean實例一旦建立成功,能夠重複使用。所以,除非必要,不然儘可能避免將Bean被設置成prototype做用域。

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

Spring容器負責建立應用程序中的bean同時經過ID來協調這些對象之間的關係。做爲開發人員,咱們須要告訴Spring要建立哪些bean而且如何將其裝配到一塊兒。

spring中bean裝配有兩種方式:

  • 隱式的bean發現機制和自動裝配
  • 在java代碼或者XML中進行顯示配置

固然這些方式也能夠配合使用。

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

  1. 編程式事務管理對基於 POJO 的應用來講是惟一選擇。咱們須要在代碼中調用beginTransaction()、commit()、rollback()等事務管理相關的方法,這就是編程式事務管理。
  2. 基於 TransactionProxyFactoryBean 的聲明式事務管理
  3. 基於 @Transactional 的聲明式事務管理
  4. 基於 Aspectj AOP 配置事務

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

事務隔離級別指的是一個事務對數據的修改與另外一個並行的事務的隔離程度,當多個事務同時訪問相同數據時,若是沒有采起必要的隔離機制,就可能發生如下問題:

  • 髒讀:一個事務讀到另外一個事務未提交的更新數據。
  • 幻讀:例如第一個事務對一個表中的數據進行了修改,好比這種修改涉及到表中的「所有數據行」。同時,第二個事務也修改這個表中的數據,這種修改是向表中插入「一行新數據」。那麼,之後就會發生操做第一個事務的用戶發現表中還存在沒有修改的數據行,就好象發生了幻覺同樣。
  • 不可重複讀:比方說在同一個事務中前後執行兩條如出一轍的select語句,期間在這次事務中沒有執行過任何DDL語句,但前後獲得的結果不一致,這就是不可重複讀。

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

Spring MVC運行流程圖:

Spring運行流程描述:

1. 用戶向服務器發送請求,請求被Spring 前端控制Servelt DispatcherServlet捕獲;

2. DispatcherServlet對請求URL進行解析,獲得請求資源標識符(URI)。而後根據該URI,調用HandlerMapping得到該Handler配置的全部相關的對象(包括Handler對象以及Handler對象對應的攔截器),最後以HandlerExecutionChain對象的形式返回;

3. DispatcherServlet 根據得到的Handler,選擇一個合適的HandlerAdapter;(附註:若是成功得到HandlerAdapter後,此時將開始執行攔截器的preHandler(...)方法)

4. 提取Request中的模型數據,填充Handler入參,開始執行Handler(Controller)。 在填充Handler的入參過程當中,根據你的配置,Spring將幫你作一些額外的工做:

  • HttpMessageConveter: 將請求消息(如Json、xml等數據)轉換成一個對象,將對象轉換爲指定的響應信息
  • 數據轉換:對請求消息進行數據轉換。如String轉換成Integer、Double等
  • 數據根式化:對請求消息進行數據格式化。 如將字符串轉換成格式化數字或格式化日期等
  • 數據驗證: 驗證數據的有效性(長度、格式等),驗證結果存儲到BindingResult或Error中

5. Handler執行完成後,向DispatcherServlet 返回一個ModelAndView對象;

6. 根據返回的ModelAndView,選擇一個適合的ViewResolver(必須是已經註冊到Spring容器中的ViewResolver)返回給DispatcherServlet ;

7. ViewResolver 結合Model和View,來渲染視圖;

8. 將渲染結果返回給客戶端。

101. spring mvc 有哪些組件?

Spring MVC的核心組件:

  1. DispatcherServlet:中央控制器,把請求給轉發到具體的控制類
  2. Controller:具體處理請求的控制器
  3. HandlerMapping:映射處理器,負責映射中央處理器轉發給controller時的映射策略
  4. ModelAndView:服務層返回的數據和視圖層的封裝類
  5. ViewResolver:視圖解析器,解析具體的視圖
  6. Interceptors :攔截器,負責攔截咱們定義的請求而後作處理工做

102. @RequestMapping 的做用是什麼?

RequestMapping是一個用來處理請求地址映射的註解,可用於類或方法上。用於類上,表示類中的全部響應請求的方法都是以該地址做爲父路徑。

RequestMapping註解有六個屬性,下面咱們把她分紅三類進行說明。

value, method:

  • value:指定請求的實際地址,指定的地址能夠是URI Template 模式(後面將會說明);
  • method:指定請求的method類型, GET、POST、PUT、DELETE等;

consumes,produces

  • consumes:指定處理請求的提交內容類型(Content-Type),例如application/json, text/html;
  • produces:指定返回的內容類型,僅當request請求頭中的(Accept)類型中包含該指定類型才返回;

params,headers

  • params: 指定request中必須包含某些參數值是,才讓該方法處理。
  • headers:指定request中必須包含某些指定的header值,才能讓該方法處理請求。

103. @Autowired 的做用是什麼?

《@Autowired用法詳解》

總結:

對於2019年100道經典面試真題的總結和解析,給你們在未來面試路上一點幫助,更多的精彩文章和麪試真題請關注:微信公衆號:java程序員彙集地


350道一線互聯網面試真題:

1080道2019年常見面試真題:

相關文章
相關標籤/搜索