1. JDK 和 JRE 有什麼區別?javascript
具體來講 JDK 其實包含了 JRE,同時還包含了編譯 java 源碼的編譯器 javac,還包含了不少 java 程序調試和分析的工具。簡單來講:若是你須要運行 java 程序,只需安裝 JRE 就能夠了,若是你須要編寫 java 程序,須要安裝 JDK。php
2. == 和 equals 的區別是什麼?html
== 解讀前端
對於基本類型和引用類型 == 的做用效果是不一樣的,以下所示:java
代碼示例:node
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。mysql
equals 解讀nginx
equals 本質上就是 ==,只不過 String 和 Integer 等重寫了 equals 方法,把它變成了值比較。看下面的代碼就明白了。程序員
首先來看默認狀況下 equals 比較一個有相同值的對象,代碼如web
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 源碼就知道了,源碼以下:
public boolean equals(Object obj) { return (this == obj); }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 中有什麼做用?
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 // StringBuffer reverseStringBuffer stringBuffer = new StringBuffer();stringBuffer.append("abcdefg");System.out.println(stringBuffer.reverse()); // gfedcba// StringBuilder reverseStringBuilder stringBuilder = new StringBuilder();stringBuilder.append("abcdefg");System.out.println(stringBuilder.reverse()); // gfedcba
10. String 類的經常使用方法都有那些?
11. 抽象類必需要有抽象方法嗎?
不須要,抽象類不必定非要有抽象方法。
示例代碼:
abstract class Cat { public static void sayHi() { System.out.println("hi~"); } }
上面代碼,抽象類並無抽象方法但徹底能夠正常運行。
12. 普通類和抽象類有哪些區別?
13. 抽象類能使用 final 修飾嗎?
不能,定義抽象類就是讓其餘類繼承的,若是定義爲 final 該類就不能被繼承,這樣彼此就會產生矛盾,因此 final 不能修飾抽象類,編輯器也會提示錯誤信息。
14. 接口和抽象類有什麼區別?
15. java 中 IO 流分爲幾種?
按功能來分:輸入流(input)、輸出流(output)。
按類型來分:字節流和字符流。
字節流和字符流的區別是:字節流按 8 位傳輸以字節爲單位輸入輸出數據,字符流按 16 位傳輸以字符爲單位輸入輸出數據。
16. BIO、NIO、AIO 有什麼區別?
17. Files的經常使用方法都有哪些?
18. java 容器都有哪些?
經常使用容器的圖錄:
19. Collection 和 Collections 有什麼區別?
20. List、Set、Map 之間的區別是什麼?
21. 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 的實現原理?
25. ArrayList 和 LinkedList 的區別是什麼?
最明顯的區別是 ArrrayList底層的數據結構是數組,支持隨機訪問,而 LinkedList 的底層數據結構是雙向循環鏈表,不支持隨機訪問。使用下標訪問一個元素,ArrayList 的時間複雜度是 O(1),而 LinkedList 是 O(n)。
26. 如何實現數組和 List 之間的轉換?
27. ArrayList 和 Vector 的區別是什麼?
28. Array 和 ArrayList 有何區別?
29. 在 Queue 中 poll()和 remove()有什麼區別?
poll() 和 remove() 都是從隊列中取出一個元素,可是 poll() 在獲取元素失敗的時候會返回空,可是 remove() 失敗的時候會拋出異常。
30. 哪些集合類是線程安全的?
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 有什麼區別?
35. 並行和併發有什麼區別?
因此併發編程的目標是充分的利用處理器的每個核,以達到最高的處理性能。
36. 線程和進程的區別?
簡而言之,進程是程序運行和資源分配的基本單位,一個程序至少有一個進程,一個進程至少有一個線程。進程在執行過程當中擁有獨立的內存單元,而多個線程共享內存資源,減小切換次數,從而效率更高。線程是進程的一個實體,是cpu調度和分派的基本單位,是比程序更小的能獨立運行的基本單位。同一進程中的多個線程之間能夠併發執行。
37. 守護線程是什麼?
守護線程(即daemon thread),是個服務線程,準確地來講就是服務其餘的線程。
38. 建立線程有哪幾種方式?
①. 繼承Thread類建立線程類
②. 經過Runnable接口建立線程類
③. 經過Callable和Future建立線程
39. 說一下 runnable 和 callable 有什麼區別?
有點深的問題了,也看出一個Java程序員學習知識的廣度。
40. 線程有哪些狀態?
線程一般都有五種狀態,建立、就緒、運行、阻塞和死亡。
41. sleep() 和 wait() 有什麼區別?
sleep():方法是線程類(Thread)的靜態方法,讓調用線程進入睡眠狀態,讓出執行機會給其餘線程,等到休眠時間結束後,線程進入就緒狀態和其餘線程一塊兒競爭cpu的執行時間。由於sleep() 是static靜態的方法,他不能改變對象的機鎖,當一個synchronized塊中調用了sleep() 方法,線程雖然進入休眠,可是對象的機鎖沒有被釋放,其餘線程依然沒法訪問這個對象。
wait():wait()是Object類的方法,當一個線程執行到wait方法時,它就進入到一個和該對象相關的等待池,同時釋放對象的機鎖,使得其餘線程可以訪問,能夠經過notify,notifyAll方法來喚醒等待的線程
42. notify()和 notifyAll()有什麼區別?
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()方法有什麼區別?
47. 在 java 程序中怎麼保證多線程的運行安全?
線程安全在三個方面體現:
48. 多線程鎖的升級原理是什麼?
在Java中,鎖共有4種狀態,級別從低到高依次爲:無狀態鎖,偏向鎖,輕量級鎖和重量級鎖狀態,這幾個狀態會隨着競爭狀況逐漸升級。鎖能夠升級但不能降級。
鎖升級的圖示過程:
49. 什麼是死鎖?
死鎖是指兩個或兩個以上的進程在執行過程當中,因爲競爭資源或者因爲彼此通訊而形成的一種阻塞的現象,若無外力做用,它們都將沒法推動下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱爲死鎖進程。是操做系統層面的一個錯誤,是進程死鎖的簡稱,最先在 1965 年由 Dijkstra 在研究銀行家算法時提出的,它是計算機操做系統乃至整個併發程序設計領域最難處理的問題之一。
50. 怎麼防止死鎖?
死鎖的四個必要條件:
這四個條件是死鎖的必要條件,只要系統發生死鎖,這些條件必然成立,而只要上述條件之 一不知足,就不會發生死鎖。
理解了死鎖的緣由,尤爲是產生死鎖的四個必要條件,就能夠最大可能地避免、預防和 解除死鎖。
因此,在系統設計、進程調度等方面注意如何不讓這四個必要條件成立,如何確 定資源的合理分配算法,避免進程永久佔據系統資源。
此外,也要防止進程在處於等待狀態的狀況下佔用資源。所以,對資源的分配要給予合理的規劃。
51. ThreadLocal 是什麼?有哪些使用場景?
線程局部變量是侷限於線程內部的變量,屬於線程自身全部,不在多個線程間共享。Java提供ThreadLocal類來支持線程局部變量,是一種實現線程安全的方式。可是在管理環境下(如 web 服務器)使用線程局部變量的時候要特別當心,在這種狀況下,工做線程的生命週期比任何應用變量的生命週期都要長。任何線程局部變量一旦在工做完成後沒有釋放,Java 應用就存在內存泄露的風險。
52.說一下 synchronized 底層實現原理?
synchronized能夠保證方法或者代碼塊在運行時,同一時刻只有一個方法能夠進入到臨界區,同時它還能夠保證共享變量的內存可見性。
Java中每個對象均可以做爲鎖,這是synchronized實現同步的基礎:
53. synchronized 和 volatile 的區別是什麼?
54. synchronized 和 Lock 有什麼區別?
55. synchronized 和 ReentrantLock 區別是什麼?
synchronized是和if、else、for、while同樣的關鍵字,ReentrantLock是類,這是兩者的本質區別。既然ReentrantLock是類,那麼它就提供了比synchronized更多更靈活的特性,能夠被繼承、能夠有方法、能夠有各類各樣的類變量,ReentrantLock比synchronized的擴展性體如今幾點上:
另外,兩者的鎖機制其實也是不同的:ReentrantLock底層調用的是Unsafe的park方法加鎖,synchronized操做的應該是對象頭中mark word。
56. 說一下 atomic 的原理?
Atomic包中的類基本的特性就是在多線程環境下,當有多個線程同時對單個(包括基本類型及引用類型)變量進行操做時,具備排他性,即當多個線程同時對該變量的值進行更新時,僅有一個線程能成功,而未成功的線程能夠向自旋鎖同樣,繼續嘗試,一直等到執行成功。
Atomic系列的類中的核心方法都會調用unsafe類中的幾個本地方法。咱們須要先知道一個東西就是Unsafe類,全名爲:sun.misc.Unsafe,這個類包含了大量的對C代碼的操做,包括不少直接內存分配以及原子操做的調用,而它之因此標記爲非安全的,是告訴你這個裏面大量的方法調用都會存在安全隱患,須要當心使用,不然會致使嚴重的後果,例如在經過unsafe分配內存的時候,若是本身指定某些區域可能會致使一些相似C++同樣的指針越界到其餘進程的問題。
57. 什麼是反射?
反射主要是指程序能夠訪問、檢測和修改它自己狀態或行爲的一種能力
Java反射:
在Java運行時環境中,對於任意一個類,可否知道這個類有哪些屬性和方法?對於任意一個對象,可否調用它的任意一個方法
Java反射機制主要提供瞭如下功能:
58. 什麼是 java 序列化?什麼狀況下須要序列化?
簡單說就是爲了保存在內存中的各類對象的狀態(也就是實例變量,不是方法),而且能夠把保存的對象狀態再讀出來。雖然你能夠用你本身的各類各樣的方法來保存object states,可是Java給你提供一種應該比你本身好的保存對象狀態的機制,那就是序列化。
什麼狀況下須要序列化:
a)當你想把的內存中的對象狀態保存到一個文件中或者數據庫中時候;
b)當你想用套接字在網絡上傳送對象的時候;
c)當你想經過RMI傳輸對象的時候;
59. 動態代理是什麼?有哪些應用?
動態代理:
當想要給實現了某個接口的類中的方法,加一些額外的處理。好比說加日誌,加事務等。能夠給這個類建立一個代理,故名思議就是建立一個新的類,這個類不只包含原來類方法的功能,並且還在原來的基礎上添加了額外處理的新類。這個代理類並非定義好的,是動態生成的。具備解耦意義,靈活,擴展性強。
動態代理的應用:
60. 怎麼實現動態代理?
首先必須定義一個接口,還要有一個InvocationHandler(將實現接口的類的對象傳遞給它)處理類。再有一個工具類Proxy(習慣性將其稱爲代理類,由於調用他的newInstance()能夠產生代理對象,其實他只是一個產生代理對象的工具類)。利用到InvocationHandler,拼接代理類源碼,將其編譯生成代理類的二進制碼,利用加載器加載,並將其實例化產生代理對象,最後返回。
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. 深拷貝和淺拷貝區別是什麼?
64. jsp 和 servlet 有什麼區別?
65. jsp 有哪些內置對象?做用分別是什麼?
JSP有9個內置對象:
66. 說一下 jsp 的 4 種做用域?
JSP中的四種做用域包括page、request、session和application,具體來講:
67. session 和 cookie 有什麼區別?
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,其實現途徑有如下幾種:
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 注入?
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,中文全稱是叫跨站請求僞造。通常來講,攻擊者經過僞造用戶的瀏覽器的請求,向訪問一個用戶本身曾經認證訪問過的網站發送出去,使目標網站接收並誤覺得是用戶的真實操做而去執行命令。經常使用於盜取帳號、轉帳、發送虛假消息等。攻擊者利用網站對請求的驗證漏洞而實現這樣的攻擊行爲,網站可以確認請求來源於用戶的瀏覽器,卻不能驗證請求是否源於用戶的真實意願下的操做行爲。
如何避免:
驗證 HTTP Referer字段
HTTP頭中的Referer字段記錄了該 HTTP 請求的來源地址。在一般狀況下,訪問一個安全受限頁面的請求來自於同一個網站,而若是黑客要對其實施 CSRF
攻擊,他通常只能在他本身的網站構造請求。所以,能夠經過驗證Referer值來防護CSRF 攻擊。
使用驗證碼
關鍵操做頁面加上驗證碼,後臺收到請求後經過判斷驗證碼能夠防護CSRF。但這種方法對用戶不太友好。
在請求地址中添加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 的最後加上 ,這樣就把token以參數的形式加入請求了。
在HTTP 頭中自定義屬性並驗證
這種方法也是使用 token 並進行驗證,和上一種方法不一樣的是,這裏並非把 token 以參數的形式置於 HTTP 請求之中,而是把它放到 HTTP 頭中自定義的屬性裏。經過 XMLHttpRequest 這個類,能夠一次性給全部該類請求加上 csrftoken 這個 HTTP 頭屬性,並把 token 值放入其中。這樣解決了上種方法在請求中加入 token 的不便,同時,經過 XMLHttpRequest 請求的地址不會被記錄到瀏覽器的地址欄,也不用擔憂 token 會透過 Referer 泄露到其餘網站中去。
74. throw 和 throws 的區別?
throws是用來聲明一個方法可能拋出的全部異常信息,throws是將異常聲明可是不處理,而是將異常往上傳,誰調用我就交給誰處理。而throw則是指拋出的一個具體的異常類型。
75. final、finally、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. 常見的異常類有哪些?
79. http 響應碼 301 和 302 表明的是什麼?有什麼區別?
答:301,302 都是HTTP狀態的編碼,都表明着某個URL發生了轉移。
區別:
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的區別?
82. tcp 爲何要三次握手,兩次不行嗎?爲何?
爲了實現可靠數據傳輸, TCP 協議的通訊雙方, 都必須維護一個序列號, 以標識發送出去的數據包中, 哪些是已經被對方收到的。 三次握手的過程便是通訊雙方相互告知序列號起始值, 並確認對方已經收到了序列號起始值的必經步驟。
若是隻是兩次握手, 至多隻有鏈接發起方的起始序列號能被確認, 另外一方選擇的序列號則得不到確認。
83. 說一下 tcp 粘包是怎麼產生的?
①. 發送方產生粘包
採用TCP協議傳輸數據的客戶端與服務器常常是保持一個長鏈接的狀態(一次鏈接發一次數據不存在粘包),雙方在鏈接不斷開的狀況下,能夠一直傳輸數據;但當發送的數據包過於的小時,那麼TCP協議默認的會啓用Nagle算法,將這些較小的數據包進行合併發送(緩衝區數據發送是一個堆壓的過程);這個合併過程就是在發送緩衝區中進行的,也就是說數據發送出來它已是粘包的狀態了。
②. 接收方產生粘包
接收方採用TCP協議接收數據時的過程是這樣的:數據到底接收方,從網絡模型的下方傳遞至傳輸層,傳輸層的TCP協議處理是將其放置接收緩衝區,而後由應用層來主動獲取(C語言用recv、read等函數);這時會出現一個問題,就是咱們在程序中調用的讀取數據函數不能及時的把緩衝區中的數據拿出來,而下一個數據又到來並有一部分放入的緩衝區末尾,等咱們讀取數據時就是一個粘包。(放數據的速度 > 應用層拿數據速度)
84. OSI 的七層模型都有哪些?
85. get 和 post 請求有哪些區別?
86. 如何實現跨域?
方式一:圖片ping或script標籤跨域
圖片ping經常使用於跟蹤用戶點擊頁面或動態廣告曝光次數。
script標籤能夠獲得從其餘來源數據,這也是JSONP依賴的根據。
方式二:JSONP跨域
JSONP(JSON with Padding)是數據格式JSON的一種「使用模式」,可讓網頁從別的網域要數據。根據 XmlHttpRequest 對象受到同源策略的影響,而利用