面試題全集

Java基礎部分 3

1、面向對象的特徵有哪些方面? 3

2、short s1 = 1; s1 = s1 + 1;有錯嗎?short s1 = 1; s1 += 1;有錯嗎? 4

3、Java有沒有goto? 4

4、int和Integer有什麼區別? 4

5、&和&&的區別? 4

6、解釋內存中的棧(stack)、堆(heap)和靜態區(static area)的用法。 5

7、用最有效率的方法計算2^3=8? 5

8、在Java中,如何跳出當前的多重嵌套循環? 5

9、構造器(constructor)是否可被重寫(override)? 5

10、兩個對象值相同(x.equals(y) == true),但卻可有不同的hash code,這句話對不對? 6

11、是否可以繼承String類? 6

12、String和StringBuilder、StringBuffer的區別? 6

13、重載(Overload)和重寫(Override)的區別。 7

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

15、char 型變量中能不能存貯一箇中文漢字,爲什麼? 8

16、抽象類(abstract class)和接口(interface)有什麼異同? 8

17、靜態嵌套類(Static Nested Class)和內部類(Inner Class)的不同? 8

18、Java 中會存在內存泄漏嗎,請簡單描述。 8

19、闡述靜態變量和實例變量的區別。 9

20、是否可以從一個靜態(static)方法內部發出對非靜態(non-static)方法的調用? 9

21、如何實現對象克隆? 9

22、GC是什麼?爲什麼要有GC? 9

23、String s = new String(“xyz”);創建了幾個字符串對象? 10

24、Java 中的final關鍵字有哪些用法? 10

25、數據類型之間的轉換: 10

26、比較一下Java和JavaSciprt。 11

27、Error和Exception有什麼區別? 11

28、try{}裏有一個return語句,那麼緊跟在這個try後的finally{}裏的代碼會不會被執行,什麼時候被執行,在return前還是後? 12

29、Java語言如何進行異常處理,關鍵字:throws、throw、try、catch、finally分別如何使用? 12

30、運行時異常與受檢異常有何異同? 12

31、列出一些你常見的運行時異常? 13

32、闡述final、finally、finalize的區別。 13

33 、List、Set、Map是否繼承自Collection接口? 13

34、闡述ArrayList、Vector、LinkedList的存儲性能和特性。 14

35、Collection和Collections的區別? 14

36、List、Map、Set三個接口存取元素時,各有什麼特點? 14

37、TreeMap和TreeSet在排序時如何比較元素?Collections工具類中的sort()方法如何比較元素? 15

38、Thread類的sleep()方法和對象的wait()方法都可以讓線程暫停執行,它們有什麼區別? 15

39、線程的sleep()方法和yield()方法有什麼區別? 15

40、編寫多線程程序有幾種實現方式? 16

41、synchronized關鍵字的用法? 16

42、舉例說明同步和異步。 16

43、啓動一個線程是調用run()還是start()方法? 16

44、什麼是線程池(thread pool)? 17

45、Java中如何實現序列化,有什麼意義? 17

46、Java中有幾種類型的流? 18

47、HashMap的原理: 18

48、HashMap和Hashtable 的區別 18

49、HashSet和HashMap 的區別 19

50ArrayList和LinkedList 的區別 19

Web部分 19

51、XML文檔定義有幾種形式?它們之間有何本質區別?解析XML文檔有哪幾種方式? 19

52、你在項目中哪些地方用到了XML? 20

53、闡述JDBC操作數據庫的步驟。 20

54、Statement和PreparedStatement有什麼區別?哪個性能更好? 20

55、使用JDBC操作數據庫時,如何提升讀取數據的性能?如何提升更新數據的性能? 21

56、在進行數據庫編程時,連接池有什麼作用? 21

57、什麼是DAO模式? 21

58、事務的ACID是指什麼? 22

59、獲得一個類的類對象有哪些方式? 22

60、如何通過反射創建對象? 22

61、如何通過反射獲取和設置對象私有字段的值? 22

62、簡述一下你瞭解的設計模式。 23

63、Servlet接口中有哪些方法? 24

64、轉發(forward)和重定向(redirect)的區別? 24

65、JSP有哪些內置對象?作用分別是什麼? 24

66、get和post請求的區別? 25

67、常用的Web服務器有哪些? 25

68、JSP和Servlet是什麼關係? 25

69、講解JSP中的四種作用域。 26

70、如何實現JSP或Servlet的單線程模式? 26

71、過濾器有哪些作用和用法? 26

72、監聽器有哪些作用和用法? 27

73、JSP中的靜態包含和動態包含有什麼區別? 27

74、Servlet中如何獲取用戶提交的查詢參數或表單數據? 27

75、Servlet中如何獲取用戶配置的初始化參數以及服務器上下文參數? 28

76、如何設置請求的編碼以及響應內容的類型? 28

77、什麼是Web Service(Web服務)? 28

框架部分 28

78、什麼是ORM? 28

79、持久層設計要考慮的問題有哪些?你用過的持久層框架有哪些? 28

80、Hibernate中SessionFactory是線程安全的嗎?Session是線程安全的嗎(兩個線程能夠共享同一個Session嗎)? 29

81、Hibernate中Session的load和get方法的區別是什麼? 29

82、闡述Session加載實體對象的過程。 29

83、Query接口的list方法和iterate方法有什麼區別? 30

84、Hibernate如何實現分頁查詢? 30

85、如何理解Hibernate的延遲加載機制?在實際應用中,延遲加載與Session關閉的矛盾是如何處理的? 30

86、簡述Hibernate常見優化策略。 31

87、談一談Hibernate的一級緩存、二級緩存和查詢緩存。 31

88、@OneToMany註解的mappedBy屬性有什麼作用? 32

89、MyBatis中使用 # 和 $ 書寫佔位符有什麼區別? 32

90、解釋一下MyBatis中命名空間(namespace)的作用。 32

91、MyBatis中的動態SQL是什麼意思? 32

92、什麼是IoC和DI?DI是如何實現的? 33

93、解釋一下什麼叫AOP(面向切面編程)? 33

94、Spring中自動裝配的方式有哪些? 33

95、Spring中如何使用註解來配置Bean?有哪些相關的註解? 33

96、Spring支持的事務管理類型有哪些?你在項目中使用哪種方式? 34

97、Spring MVC的工作原理是怎樣的? 34

98、選擇使用Spring框架的原因(Spring框架爲企業級開發帶來的好處有哪些)? 34

99、闡述Spring框架中Bean的生命週期? 35

Java基礎部分
1、面向對象的特徵有哪些方面?
答:面向對象的特徵主要有以下幾個方面:

  • 抽象:抽象是將一類對象的共同特徵總結出來構造類的過程,包括數據抽象和行爲抽象兩方面。抽象只關注對象有哪些屬性和行爲,並不關注這些行爲的細節是什麼。

  • 繼承:繼承是從已有類得到繼承信息創建新類的過程。提供繼承信息的類被稱爲父類(超類、基類);得到繼承信息的類被稱爲子類(派生類)。繼承讓變化中的軟件系統有了一定的延續性,同時繼承也是封裝程序中可變因素的重要手段。

  • 封裝:通常認爲封裝是把數據和操作數據的方法綁定起來,對數據的訪問只能通過已定義的接口。面向對象的本質就是將現實世界描繪成一系列完全自治、封閉的對象。我們在類中編寫的方法就是對實現細節的一種封裝;我們編寫一個類就是對數據和數據操作的封裝。可以說,封裝就是隱藏一切可隱藏的東西,只向外界提供最簡單的編程接口。

  • 多態性:多態性是指允許不同子類型的對象對同一消息作出不同的響應。簡單的說就是用同樣的對象引用調用同樣的方法但是做了不同的事情。多態性分爲編譯時的多態性和運行時的多態性。

2、short s1 = 1; s1 = s1 + 1;有錯嗎?short s1 = 1; s1 += 1;有錯嗎?
答:對於short s1 = 1; s1 = s1 + 1;由於1是int類型,因此s1+1運算結果也是int 型,需要強制轉換類型才能賦值給short型。而short s1 = 1; s1 += 1;可以正確編譯,因爲s1+= 1;相當於s1 = (short)(s1 + 1);其中有隱含的強制類型

轉換。

3、Java有沒有goto?
答:goto 是Java中的保留字,在目前版本的Java中沒有使用。(根據James Gosling(Java之父)編寫的《TheJava Programming Language》一書的附錄中給出了一個Java關鍵字列表,其中有goto和const,但是這兩個是目前無法使用的關鍵字,因此有些地方將其稱之爲保留字,其實保留字這個詞應該有更廣泛的意義,因爲熟悉C語言的程序員都知道,在系統類庫中使用過的有特殊意義的單詞或單詞的組合都被視爲保留字)。

4、int和Integer有什麼區別?
答:Java是一個近乎純潔的面向對象編程語言,但是爲了編程的方便還是引入了基本數據類型,但是爲了能夠將這些基本數據類型當成對象操作,Java爲每一個基本數據類型都引入了對應的包裝類型(wrapper class),int的包裝類就是Integer,從Java 5開始引入了自動裝箱/拆箱機制,使得二者可以相互轉換。

5、&和&&的區別?
答:&運算符有兩種用法:(1)按位與;(2)邏輯與。&&運算符是短路與運算。邏輯與跟短路與的差別是非常巨大的,雖然二者都要求運算符左右兩端的布爾值都是true整個表達式的值纔是true。&&之所以稱爲短路運算是因爲,如果&&左邊的表達式的值是false,右邊的表達式會被直接短路掉,不會進行運算。很多時候我們可能都需要用&&而不是&,例如在驗證用戶登錄時判定用戶名不是null而且不是空字符串,應當寫爲:username != null&&!username.equals(""),二者的順序不能交換,更不能用&運算符,因爲第一個條件如果不成立,根本不能進行字符串的equals比較,否則會產生NullPointerException異常。注意:邏輯或運算符(|)和短路或運算符(||)的差別也是如此。

6、解釋內存中的棧(stack)、堆(heap)和靜態區(static area)的用法。
答:通常我們定義一個基本數據類型的變量,一個對象的引用,還有就是函數調用的現場保存都使用內存中的棧空間;而通過new關鍵字和構造器創建的對象放在堆空間;程序中的字面量(literal)如直接書寫的100、"hello"和常量都是放在靜態區中。棧空間操作起來最快但是棧很小,通常大量的對象都是放在堆空間,理論上整個內存沒有被其他進程使用的空間甚至硬盤上的虛擬內存都可以被當成堆空間來使用。

上面的語句中變量str放在棧上,用new創建出來的字符串對象放在堆上,而"hello"這個字面量放在靜態區。

補充:較新版本的Java(從Java 6的某個更新開始)中使用了一項叫"逃逸分析"的技術,可以將一些局部對象放在棧上以提升對象的操作性能。

7、用最有效率的方法計算2^3=8?
答: 2 << 3(左移3位相當於乘以2的3次方,右移3位相當於除以2的3次方)。

補充:我們爲編寫的類重寫hashCode方法時,可能會看到如下所示的代碼,其實我們不太理解爲什麼要使用這樣的乘法運算來產生哈希碼(散列碼),而且爲什麼這個數是個素數,爲什麼通常選擇31這個數?前兩個問題的答案你可以自己百度一下,選擇31是因爲可以用移位和減法運算來代替乘法,從而得到更好的性能。說到這裏你可能已經想到了:31 * num 等價於(num << 5) - num,左移5位相當於乘以2的5次方再減去自身就相當於乘以31,現在的VM都能自動完成這個優化。

8、在Java中,如何跳出當前的多重嵌套循環?
答:在最外層循環前加一個標記如A,然後用break A;可以跳出多重循環。(Java中支持帶標籤的break和continue語句,作用有點類似於C和C++中的goto語句,但是就像要避免使用goto一樣,應該避免使用帶標籤的break和continue,因爲它不會讓你的程序變得更優雅,很多時候甚至有相反的作用,所以這種語法其實不知道更好)

9、構造器(constructor)是否可被重寫(override)?
答:構造器不能被繼承,因此不能被重寫,但可以被重載。

10、兩個對象值相同(x.equals(y) == true),但卻可有不同的hash code,這句話對不對?
答:不對,如果兩個對象x和y滿足x.equals(y) == true,它們的哈希碼(hash code)應當相同。Java對於eqauls方法和hashCode方法是這樣規定的:(1)如果兩個對象相同(equals方法返回true),那麼它們的hashCode值一定要相同;(2)如果兩個對象的hashCode相同,它們並不一定相同。當然,你未必要按照要求去做,但是如果你違背了上述原則就會發現在使用容器時,相同的對象可以出現在Set集合中,同時增加新元素的效率會大大下降(對於使用哈希存儲的系統,如果哈希碼頻繁的衝突將會造成存取性能急劇下降)。

補充:關於equals和hashCode方法,很多Java程序都知道,但很多人也就是僅僅知道而已,在JoshuaBloch的大作《Effective Java》(很多軟件公司,《Effective Java》、《Java編程思想》以及《重構:改善既有代碼質量》是Java程序員必看書籍,如果你還沒看過,那就趕緊去亞馬遜買一本吧)中是這樣介紹equals方法的:首先equals方法必須滿足自反性(x.equals(x)必須返回true)、對稱性(x.equals(y)返回true時,y.equals(x)也必須返回true)、傳遞性(x.equals(y)和y.equals(z)都返回true時,x.equals(z)也必須返回true)和一致性(當x和y引用的對象信息沒有被修改時,多次調用x.equals(y)應該得到同樣的返回值),而且對於任何非null值的引用x,x.equals(null)必須返回false。實現高質量的equals方法的訣竅包括:1. 使用==操作符檢查"參數是否爲這個對象的引用";2. 使用instanceof操作符檢查"參數是否爲正確的類型";3. 對於類中的關鍵屬性,檢查參數傳入對象的屬性是否與之相匹配;4. 編寫完equals方法後,問自己它是否滿足對稱性、傳遞性、一致性;5. 重寫equals時總是要重寫hashCode;6. 不要將equals方法參數中的Object對象替換爲其他的類型,在重寫時不要忘掉@Override註解。

11、是否可以繼承String類?
答:String 類是final類,不可以被繼承。

補充:繼承String本身就是一個錯誤的行爲,對String類型最好的重用方式是關聯關係(Has-A)和依賴關係(Use-A)而不是繼承關係(Is-A)。

12、String和StringBuilder、StringBuffer的區別?
答:Java平臺提供了兩種類型的字符串:String和StringBuffer/StringBuilder,它們可以儲存和操作字符串。其中String是隻讀字符串,也就意味着String引用的字符串內容是不能被改變的。而StringBuffer/StringBuilder類表示的字符串對象可以直接進行修改。StringBuilder是Java 5中引入的,它和StringBuffer的方法完全相同,區別在於它是在單線程環境下使用的,因爲它的所有方面都沒有被synchronized修飾,因此它的效率也比StringBuffer要高。

13、重載(Overload)和重寫(Override)的區別。
重載的方法能否根據返回類型進行區分?

答:方法的重載和重寫都是實現多態的方式,區別在於前者實現的是編譯時的多態性,而後者實現的是運行時的多態性。重載發生在一個類中,同名的方法如果有不同的參數列表(參數類型不同、參數個數不同或者二者都不同)則視爲重載;重寫發生在子類與父類之間,重寫要求子類被重寫方法與父類被重寫方法有相同的返回類型,比父類被重寫方法更好訪問,不能比父類被重寫方法聲明更多的異常(里氏代換原則)。重載對返回類型沒有特殊的要求。

14、描述一下JVM加載class文件的原理機制?
答:JVM中類的裝載是由類加載器(ClassLoader)和它的子類來實現的,Java中的類加載器是一個重要的Java運行時系統組件,它負責在運行時查找和裝入類文件中的類。

由於Java的跨平臺性,經過編譯的Java源程序並不是一個可執行程序,而是一個或多個類文件。當Java程序需要使用某個類時,JVM會確保這個類已經被加載、連接(驗證、準備和解析)和初始化。類的加載是指把類的.class文件中的數據讀入到內存中,通常是創建一個字節數組讀入.class文件,然後產生與所加載類對應的Class對象。加載完成後,Class對象還不完整,所以此時的類還不可用。當類被加載後就進入連接階段,這一階段包括驗證、準備(爲靜態變量分配內存並設置默認的初始值)和解析(將符號引用替換爲直接引用)三個步驟。最後JVM對類進行初始化,包括:1)如果類存在直接的父類並且這個類還沒有被初始化,那麼就先初始化父類;2)如果類中存在初始化語句,就依次執行這些初始化語句。

類的加載是由類加載器完成的,類加載器包括:根加載器(BootStrap)、擴展加載器(Extension)、系統加載器(System)和用戶自定義類加載器(java.lang.ClassLoader的子類)。從Java 2(JDK 1.2)開始,類加載過程採取了父親委託機制(PDM)。PDM更好的保證了Java平臺的安全性,在該機制中,JVM自帶的Bootstrap是根加載器,其他的加載器都有且僅有一個父類加載器。類的加載首先請求父類加載器加載,父類加載器無能爲力時才由其子類加載器自行加載。JVM不會向Java程序提供對Bootstrap的引用。下面是關於幾個類加載器的說明:

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

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

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

15、char 型變量中能不能存貯一箇中文漢字,爲什麼?
答:char類型可以存儲一箇中文漢字,因爲Java中使用的編碼是Unicode(不選擇任何特定的編碼,直接使用字符在字符集中的編號,這是統一的唯一方法),一個char類型佔2個字節(16比特),所以放一箇中文是沒問題的。

補充:使用Unicode意味着字符在JVM內部和外部有不同的表現形式,在JVM內部都是Unicode,當這個字符被從JVM內部轉移到外部時(例如存入文件系統中),需要進行編碼轉換。所以Java中有字節流和字符流,以及在字符流和字節流之間進行轉換的轉換流,如InputStreamReader和OutputStreamReader,這兩個類是字節流和字符流之間的適配器類,承擔了編碼轉換的任務;對於C程序員來說,要完成這樣的編碼轉換恐怕要依賴於union(聯合體/共用體)共享內存的特徵來實現了。

16、抽象類(abstract class)和接口(interface)有什麼異同?
答:抽象類和接口都不能夠實例化,但可以定義抽象類和接口類型的引用。一個類如果繼承了某個抽象類或者實現了某個接口都需要對其中的抽象方法全部進行實現,否則該類仍然需要被聲明爲抽象類。接口比抽象類更加抽象,因爲抽象類中可以定義構造器,可以有抽象方法和具體方法,而接口中不能定義構造器而且其中的方法全部都是抽象方法。抽象類中的成員可以是private、默認、protected、public的,而接口中的成員全都是public的。抽象類中可以定義成員變量,而接口中定義的成員變量實際上都是常量。有抽象方法的類必須被聲明爲抽象類,而抽象類未必要有抽象方法。

17、靜態嵌套類(Static Nested Class)和內部類(Inner Class)的不同?
答:Static Nested Class是被聲明爲靜態(static)的內部類,它可以不依賴於外部類實例被實例化。而通常的內部類需要在外部類實例化後才能實例化,

18、Java 中會存在內存泄漏嗎,請簡單描述。
答:理論上Java因爲有垃圾回收機制(GC)不會存在內存泄露問題(這也是Java被廣泛使用於服務器端編程的一個重要原因);然而在實際開發中,可能會存在無用但可達的對象,這些對象不能被GC回收,因此也會導致內存泄露的發生。例如hibernate的Session(一級緩存)中的對象屬於持久態,垃圾回收器是不會回收這些對象的,然而這些對象中可能存在無用的垃圾對象,如果不及時關閉(close)或清空(flush)一級緩存就可能導致內存泄露。

答:都不能。抽象方法需要子類重寫,而靜態的方法是無法被重寫的,因此二者是矛盾的。本地方法是由本地代碼(如C代碼)實現的方法,而抽象方法是沒有實現的,也是矛盾的。synchronized和方法的實現細節有關,抽象方法不涉及實現細節,因此也是相互矛盾的。

19、闡述靜態變量和實例變量的區別。
答:靜態變量是被static修飾符修飾的變量,也稱爲類變量,它屬於類,不屬於類的任何一個對象,一個類不管創建多少個對象,靜態變量在內存中有且僅有一個拷貝;實例變量必須依存於某一實例,需要先創建對象然後通過對象才能訪問到它。靜態變量可以實現讓多個對象共享內存。

補充:在Java開發中,上下文類和工具類中通常會有大量的靜態成員。

20、是否可以從一個靜態(static)方法內部發出對非靜態(non-static)方法的調用?
答:不可以,靜態方法只能訪問靜態成員,因爲非靜態方法的調用要先創建對象,在調用靜態方法時可能對象並沒有被初始化。

21、如何實現對象克隆?
答:有兩種方式:

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

2). 實現Serializable接口,通過對象的序列化和反序列化實現克隆,可以實現真正的深度克隆。

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

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

垃圾回收調用。

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

補充:垃圾回收機制有很多種,包括:分代複製垃圾回收、標記垃圾回收、增量垃圾回收等方式。標準的Java進程既有棧又有堆。棧保存了原始型局部變量,堆保存了要創建的對象。Java平臺對堆內存回收和再利用的基本算法被稱爲標記和清除,但是Java對其進行了改進,採用“分代式垃圾收集”。這種方法會跟Java對象的生命週期將堆內存劃分爲不同的區域,在垃圾收集過程中,可能會將對象移動到不同區域:

  • 伊甸園(Eden):這是對象最初誕生的區域,並且對大多數對象來說,這裏是它們唯一存在過的區域。

  • 倖存者樂園(Survivor):從伊甸園倖存下來的對象會被挪到這裏。

  • 終身頤養園(Tenured):這是足夠老的倖存對象的歸宿。年輕代收集(Minor-GC)過程是不會觸及這個地方的。當年輕代收集不能把對象放進終身頤養園時,就會觸發一次完全收集(Major-GC),這裏可能還會牽扯到壓縮,以便爲大對象騰出足夠的空間。

23、String s = new String(“xyz”);創建了幾個字符串對象?
答:兩個對象,一個是靜態區的"xyz",一個是用new創建在堆上的對象。

24、Java 中的final關鍵字有哪些用法?
答:(1)修飾類:表示該類不能被繼承;(2)修飾方法:表示方法不能被重寫;(3)修飾變量:表示變量只能一次賦值以後值不能被修改(常量)。

25、數據類型之間的轉換:

  • 如何將字符串轉換爲基本數據類型?

  • 如何將基本數據類型轉換爲字符串?

答:

  • 調用基本數據類型對應的包裝類中的方法parseXXX(String)或valueOf(String)即可返回相應基本類型;

  • 一種方法是將基本數據類型與空字符串("")連接(+)即可獲得其所對應的字符串;另一種方法是調用String 類

中的valueOf()方法返回相應字符串

26、比較一下Java和JavaSciprt。
答:JavaScript 與Java是兩個公司開發的不同的兩個產品。Java 是原Sun Microsystems公司推出的面向對象的程序設計語言,特別適合於互聯網應用程序開發;而JavaScript是Netscape公司的產品,爲了擴展Netscape瀏覽器的功能而開發的一種可以嵌入Web頁面中運行的基於對象和事件驅動的解釋性語言。JavaScript的前身是LiveScript;而Java的前身是Oak語言。

下面對兩種語言間的異同作如下比較:

  • 基於對象和麪向對象:Java是一種真正的面向對象的語言,即使是開發簡單的程序,必須設計對象;JavaScript是種腳本語言,它可以用來製作與網絡無關的,與用戶交互作用的複雜軟件。它是一種基於對象(Object-Based)和事件驅動(Event-Driven)的編程語言,因而它本身提供了非常豐富的內部對象供設計人員使用。

  • 解釋和編譯:Java的源代碼在執行之前,必須經過編譯。JavaScript是一種解釋性編程語言,其源代碼不需經過編譯,由瀏覽器解釋執行。(目前的瀏覽器幾乎都使用了JIT(即時編譯)技術來提升JavaScript的運行效率)

  • 強類型變量和類型弱變量:Java採用強類型變量檢查,即所有變量在編譯之前必須作聲明;JavaScript中變量是弱類型的,甚至在使用變量前可以不作聲明,JavaScript的解釋器在運行時檢查推斷其數據類型。

  • 代碼格式不一樣。

補充:上面列出的四點是網上流傳的所謂的標準答案。其實Java和JavaScript最重要的區別是一個是靜態語言,一個是動態語言。目前的編程語言的發展趨勢是函數式語言和動態語言。在Java中類(class)是一等公民,而JavaScript中函數(function)是一等公民,因此JavaScript支持函數式編程,可以使用Lambda函數和閉包(closure),當然Java 8也開始支持函數式編程,提供了對Lambda表達式以及函數式接口的支持。對於這類問題,在面試的時候最好還是用自己的語言回答會更加靠譜,不要背網上所謂的標準答案。

27、Error和Exception有什麼區別?
答:Error表示系統級的錯誤和程序不必處理的異常,是恢復不是不可能但很困難的情況下的一種嚴重問題;比如內存溢出,不可能指望程序能處理這樣的情況;Exception表示需要捕捉或者需要程序進行處理的異常,是一種設計或實現問題;也就是說,它表示如果程序運行正常,從不會發生的情況。

28、try{}裏有一個return語句,那麼緊跟在這個try後的finally{}裏的代碼會不會被執行,什麼時候被執行,在return前還是後?
答:會執行,在方法返回調用者前執行。

注意:在finally中改變返回值的做法是不好的,因爲如果存在finally代碼塊,try中的return語句不會立馬返回調用者,而是記錄下返回值待finally代碼塊執行完畢之後再向調用者返回其值,然後如果在finally中修改了返回值,就會返回修改後的值。顯然,在finally中返回或者修改返回值會對程序造成很大的困擾,C#中直接用編譯錯誤的方式來阻止程序員幹這種齷齪的事情,Java中也可以通過提升編譯器的語法檢查級別來產生警告或錯誤,Eclipse中可以在如圖所示的地方進行設置,強烈建議將此項設置爲編譯錯誤。

29、Java語言如何進行異常處理,關鍵字:throws、throw、try、catch、finally分別如何使用?
答:Java通過面向對象的方法進行異常處理,把各種不同的異常進行分類,並提供了良好的接口。在Java中,每個異常都是一個對象,它是Throwable類或其子類的實例。當一個方法出現異常後便拋出一個異常對象,該對象中包含有異常信息,調用這個對象的方法可以捕獲到這個異常並可以對其進行處理。Java的異常處理是通過5個關鍵詞來實現的:try、catch、throw、throws和finally。一般情況下是用try來執行一段程序,如果系統會拋出(throw)一個異常對象,可以通過它的類型來捕獲(catch)它,或通過總是執行代碼塊(finally)來處理;try用來指定一塊預防所有異常的程序;catch子句緊跟在try塊後面,用來指定你想要捕獲的異常的類型;throw語句用來明確地拋出一個異常;throws用來聲明一個方法可能拋出的各種異常(當然聲明異常時允許無病呻吟);finally爲確保一段代碼不管發生什麼異常狀況都要被執行;try語句可以嵌套,每當遇到一個try語句,異常的結構就會被放入異常棧中,直到所有的try語句都完成。如果下一級的try語句沒有對某種異常進行處理,異常棧就會執行出棧操作,直到遇到有處理這種異常的try語句或者最終將異常拋給JVM。

30、運行時異常與受檢異常有何異同?
答:異常表示程序運行過程中可能出現的非正常狀態,運行時異常表示虛擬機的通常操作中可能遇到的異常,是一種常見運行錯誤,只要程序設計得沒有問題通常就不會發生。受檢異常跟程序運行的上下文環境有關,即使程序設計無誤,仍然可能因使用的問題而引發。Java編譯器要求方法必須聲明拋出可能發生的受檢異常,但是並不要求必須聲明拋出未被捕獲的運行時異常。異常和繼承一樣,是面向對象程序設計中經常被濫用的東西,在Effective Java

中對異常的使用給出了以下指導原則:

  • 不要將異常處理用於正常的控制流(設計良好的API不應該強迫它的調用者爲了正常的控制流而使用異常)

  • 對可以恢復的情況使用受檢異常,對編程錯誤使用運行時異常

  • 避免不必要的使用受檢異常(可以通過一些狀態檢測手段來避免異常的發生)

  • 優先使用標準的異常

  • 每個方法拋出的異常都要有文檔

  • 保持異常的原子性

  • 不要在catch中忽略掉捕獲到的異常

31、列出一些你常見的運行時異常?
答:

  • ArithmeticException(算術異常)

  • ClassCastException (類轉換異常)

  • IllegalArgumentException (非法參數異常)

  • IndexOutOfBoundsException (下標越界異常)

  • NullPointerException (空指針異常)

  • SecurityException (安全異常)

32、闡述final、finally、finalize的區別。
答:

  • final:修飾符(關鍵字)有三種用法:如果一個類被聲明爲final,意味着它不能再派生出新的子類,即不能被繼承,因此它和abstract是反義詞。將變量聲明爲final,可以保證它們在使用中不被改變,被聲明爲final的變量必須在聲明時給定初值,而在以後的引用中只能讀取不可修改。被聲明爲final的方法也同樣只能使用,不能在子類中被重寫。

  • finally:通常放在try…catch…的後面構造總是執行代碼塊,這就意味着程序無論正常執行還是發生異常,這裏的代碼只要JVM不關閉都能執行,可以將釋放外部資源的代碼寫在finally塊中。

  • finalize:Object類中定義的方法,Java中允許使用finalize()方法在垃圾收集器將對象從內存中清除出去之前做必要的清理工作。這個方法是由垃圾收集器在銷燬對象時調用的,通過重寫finalize()方法可以整理系統資源或者執行其他清理工作。

33 、List、Set、Map是否繼承自Collection接口?
答:List、Set 是,Map 不是。Map是鍵值對映射容器,與List和Set有明顯的區別,而Set存儲的零散的元素且不允許有重複元素(數學中的集合也是如此),List是線性結構的容器,適用於按數值索引訪問元素的情形。

34、闡述ArrayList、Vector、LinkedList的存儲性能和特性。
答:ArrayList 和Vector都是使用數組方式存儲數據,此數組元素數大於實際存儲的數據以便增加和插入元素,它們都允許直接按序號索引元素,但是插入元素要涉及數組元素移動等內存操作,所以索引數據快而插入數據慢,Vector中的方法由於添加了synchronized修飾,因此Vector是線程安全的容器,但性能上較ArrayList差,因此已經是Java中的遺留容器。LinkedList使用雙向鏈表實現存儲(將內存中零散的內存單元通過附加的引用關聯起來,形成一個可以按序號索引的線性結構,這種鏈式存儲方式與數組的連續存儲方式相比,內存的利用率更高),按序號索引數據需要進行前向或後向遍歷,但是插入數據時只需要記錄本項的前後項即可,所以插入速度較快。Vector屬於遺留容器(Java早期的版本中提供的容器,除此之外,Hashtable、Dictionary、BitSet、Stack、Properties都是遺留容器),已經不推薦使用,但是由於ArrayList和LinkedListed都是非線程安全的,如果遇到多個線程操作同一個容器的場景,則可以通過工具類Collections中的synchronizedList方法將其轉換成線程安全的容器後再使用(這是對裝潢模式的應用,將已有對象傳入另一個類的構造器中創建新的對象來增強實現)。

補充:遺留容器中的Properties類和Stack類在設計上有嚴重的問題,Properties是一個鍵和值都是字符串的特殊的鍵值對映射,在設計上應該是關聯一個Hashtable並將其兩個泛型參數設置爲String類型,但是Java API中的Properties直接繼承了Hashtable,這很明顯是對繼承的濫用。這裏複用代碼的方式應該是Has-A關係而不是Is-A關係,另一方面容器都屬於工具類,繼承工具類本身就是一個錯誤的做法,使用工具類最好的方式是Has-A關係(關聯)或Use-A關係(依賴)。同理,Stack類繼承Vector也是不正確的。Sun公司的工程師們也會犯這種低級錯誤,讓人唏噓不已。

35、Collection和Collections的區別?
答:Collection是一個接口,它是Set、List等容器的父接口;Collections是個一個工具類,提供了一系列的靜態方法來輔助容器操作,這些方法包括對容器的搜索、排序、線程安全化等等。

36、List、Map、Set三個接口存取元素時,各有什麼特點?
答:List以特定索引來存取元素,可以有重複元素。Set不能存放重複元素(用對象的equals()方法來區分元素是否重複)。Map保存鍵值對(key-value pair)映射,映射關係可以是一對一或多對一。Set和Map容器都有基於哈希存儲和排序樹的兩種實現版本,基於哈希存儲的版本理論存取時間複雜度爲O(1),而基於排序樹版本的實現在插入或刪除元素時會按照元素或元素的鍵(key)構成排序樹從而達到排序和去重的效果。

37、TreeMap和TreeSet在排序時如何比較元素?Collections工具類中的sort()方法如何比較元素?
答:TreeSet要求存放的對象所屬的類必須實現Comparable接口,該接口提供了比較元素的compareTo()方法,當插入元素時會回調該方法比較元素的大小。TreeMap要求存放的鍵值對映射的鍵必須實現Comparable接口從而根據鍵對元素進行排序。Collections工具類的sort方法有兩種重載的形式,第一種要求傳入的待排序容器中存放的對象比較實現Comparable接口以實現元素的比較;第二種不強制性的要求容器中的元素必須可比較,但是要求傳入第二個參數,參數是Comparator接口的子類型(需要重寫compare方法實現元素的比較),相當於一個臨時定義的排序規則,其實就是通過接口注入比較元素大小的算法,也是對回調模式的應用(Java中對函數式編程的支持)。

38、Thread類的sleep()方法和對象的wait()方法都可以讓線程暫停執行,它們有什麼區別?
答:sleep()方法(休眠)是線程類(Thread)的靜態方法,調用此方法會讓當前線程暫停執行指定的時間,將執行機會(CPU)讓給其他線程,但是對象的鎖依然保持,因此休眠時間結束後會自動恢復。wait()是Object類的方法,調用對象的wait()方法導致當前線程放棄對象的鎖(線程暫停執行),進入對象的等待池(wait pool),只有調用對象的notify()方法(或notifyAll()方法)時才能喚醒等待池中的線程進入等鎖池(lock pool),如果線程重新獲得對象的鎖就可以進入就緒狀態。

補充:可能不少人對什麼是進程,什麼是線程還比較模糊,對於爲什麼需要多線程編程也不是特別理解。簡單的說:進程是具有一定獨立功能的程序關於某個數據集合上的一次運行活動,是操作系統進行資源分配和調度的一個獨立單位;線程是進程的一個實體,是CPU調度和分派的基本單位,是比進程更小的能獨立運行的基本單位。線程的劃分尺度小於進程,這使得多線程程序的併發性高;進程在執行時通常擁有獨立的內存單元,而線程之間可以共享內存。使用多線程的編程通常能夠帶來更好的性能和用戶體驗,但是多線程的程序對於其他程序是不友好的,因爲它可能佔用了更多的CPU資源。當然,也不是線程越多,程序的性能就越好,因爲線程之間的調度和切換也會浪費CPU時間。時下很時髦的Node.js就採用了單線程異步I/O的工作模式。

39、線程的sleep()方法和yield()方法有什麼區別?
答:

① sleep()方法給其他線程運行機會時不考慮線程的優先級,因此會給低優先級的線程以運行的機會;yield()方法只會給相同優先級或更高優先級的線程以運行的機會;

② 線程執行sleep()方法後轉入阻塞(blocked)狀態,而執行yield()方法後轉入就緒(ready)狀態;

③ sleep()方法聲明拋出InterruptedException,而yield()方法沒有聲明任何異常;

④ sleep()方法比yield()方法(跟操作系統CPU調度相關)具有更好的可移植性。

40、編寫多線程程序有幾種實現方式?
答:Java 5以前實現多線程有兩種實現方法:一種是繼承Thread類;另一種是實現Runnable接口。兩種方式都要通過重寫run()方法來定義線程的行爲,推薦使用後者,因爲Java中的繼承是單繼承,一個類有一個父類,如果繼承了Thread類就無法再繼承其他類了,顯然使用Runnable接口更爲靈活。

補充:Java 5以後創建線程還有第三種方式:實現Callable接口,該接口中的call方法可以在線程執行結束時產生一個返回值.

41、synchronized關鍵字的用法?
答:synchronized關鍵字可以將對象或者方法標記爲同步,以實現對對象和方法的互斥訪問,可以用synchronized(對象) { … }定義同步代碼塊,或者在聲明方法時將synchronized作爲方法的修飾符。

42、舉例說明同步和異步。
答:如果系統中存在臨界資源(資源數量少於競爭資源的線程數量的資源),例如正在寫的數據以後可能被另一個線程讀到,或者正在讀的數據可能已經被另一個線程寫過了,那麼這些數據就必須進行同步存取(數據庫操作中的排他鎖就是最好的例子)。當應用程序在對象上調用了一個需要花費很長時間來執行的方法,並且不希望讓程序等待方法的返回時,就應該使用異步編程,在很多情況下采用異步途徑往往更有效率。事實上,所謂的同步就是指阻

塞式操作,而異步就是非阻塞式操作。

43、啓動一個線程是調用run()還是start()方法?
答:啓動一個線程是調用start()方法,使線程所代表的虛擬處理機處於可運行狀態,這意味着它可以由JVM 調度並執行,這並不意味着線程就會立即運行。run()方法是線程啓動後要進行回調(callback)的方法。

44、什麼是線程池(thread pool)?
答:在面向對象編程中,創建和銷燬對象是很費時間的,因爲創建一個對象要獲取內存資源或者其它更多資源。在Java中更是如此,虛擬機將試圖跟蹤每一個對象,以便能夠在對象銷燬後進行垃圾回收。所以提高服務程序效率的一個手段就是儘可能減少創建和銷燬對象的次數,特別是一些很耗資源的對象創建和銷燬,這就是”池化資源”技術產生的原因。線程池顧名思義就是事先創建若干個可執行的線程放入一個池(容器)中,需要的時候從池中獲取線程不用自行創建,使用完畢不需要銷燬線程而是放回池中,從而減少創建和銷燬線程對象的開銷。

Java 5+中的Executor接口定義一個執行線程的工具。它的子類型即線程池接口是ExecutorService。要配置一個線程池是比較複雜的,尤其是對於線程池的原理不是很清楚的情況下,因此在工具類Executors面提供了一些靜態工廠方法,生成一些常用的線程池,如下所示:

  • newSingleThreadExecutor:創建一個單線程的線程池。這個線程池只有一個線程在工作,也就是相當於單線程串行執行所有任務。如果這個唯一的線程因爲異常結束,那麼會有一個新的線程來替代它。此線程池保證所有任務的執行順序按照任務的提交順序執行。

  • newFixedThreadPool:創建固定大小的線程池。每次提交一個任務就創建一個線程,直到線程達到線程池的最大大小。線程池的大小一旦達到最大值就會保持不變,如果某個線程因爲執行異常而結束,那麼線程池會補充一個新線程。

  • newCachedThreadPool:創建一個可緩存的線程池。如果線程池的大小超過了處理任務所需要的線程,那麼就會回收部分空閒(60秒不執行任務)的線程,當任務數增加時,此線程池又可以智能的添加新線程來處理任務。此線程池不會對線程池大小做限制,線程池大小完全依賴於操作系統(或者說JVM)能夠創建的最大線程大小。

  • newScheduledThreadPool:創建一個大小無限的線程池。此線程池支持定時以及週期性執行任務的需求。

  • newSingleThreadExecutor:創建一個單線程的線程池。此線程池支持定時以及週期性執行任務的需求。

45、Java中如何實現序列化,有什麼意義?
答:序列化就是一種用來處理對象流的機制,所謂對象流也就是將對象的內容進行流化。可以對流化後的對象進行讀寫操作,也可將流化後的對象傳輸於網絡之間。序列化是爲了解決對象流讀寫操作時可能引發的問題(如果不進行序列化可能會存在數據亂序的問題)。

要實現序列化,需要讓一個類實現Serializable接口,該接口是一個標識性接口,標註該類對象是可被序列化的,然後使用一個輸出流來構造一個對象輸出流並通過writeObject(Object)方法就可以將實現對象寫出(即保存其狀態);如果需要反序列化則可以用一個輸入流建立對象輸入流,然後通過readObject方法從流中讀取對象。序列化除了能夠實現對象的持久化之外,還能夠用於對象的深度克隆。

46、Java中有幾種類型的流?
答:字節流和字符流。字節流繼承於InputStream、OutputStream,字符流繼承於Reader、Writer。在java.io 包中還有許多其他的流,主要是爲了提高性能和使用方便。關於Java的I/O需要注意的有兩點:一是兩種對稱性(輸入和輸出的對稱性,字節和字符的對稱性);二是兩種設計模式(適配器模式和裝潢模式)。另外Java中的流不同於C#的是它只有一個維度一個方向。

47、HashMap的原理:
HashMap 在底層將 key-value 當成一個整體進行處理,這個整體就是一個 Entry 對象。

HashMap 底層採用一個Entry[] 數組來保存所有的key-value 對,當需要存儲一個Entry 對象時,會根據 hash 算法來決定其在數組中的存儲位置,在據equals 方法決定其在該數組位置上的鏈表中的存儲位置;當需要取出一個Entry時,也會根據hash算法找到其在數組中的存儲位置,再根據equals方法從該位置上的鏈表中取出該Entry。

48、HashMap和Hashtable 的區別
HashMap 和 Hashtable 都實現了 Map 接口,但決定用哪一個之前先要弄清楚它們之間的分別。主要的區別有:線程安全性,同步(synchronization),以及速度。

HashMap 幾乎可以等價於 Hashtable,除了 HashMap 是非 synchronized 的,並可以接受null(HashMap 可以接受爲 null 的鍵值(key)和值(value),而 Hashtable 則不行)。

HashMap 是非 synchronized,而 Hashtable 是 synchronized,這意味着 Hashtable 是線程安全的,多個線程可以共享一個 Hashtable;而如果沒有正確的同步的話,多個線程是不能共享 HashMap 的。

Java 5 提供了 ConcurrentHashMap,它是 HashTable 的替代,比 HashTable 的擴展性更好。

另一個區別是 HashMap 的迭代器(Iterator)是 fail-fast 迭代器,而 Hashtable 的 enumerator迭代器不是 fail-fast 的。

所以當有其它線程改變了 HashMap 的結構(增加或者移除元素),將會拋出

ConcurrentModificationException,但迭代器本身的 remove()方法移除元素則不會拋出 ConcurrentModificationException 異常。但這並不是一個一定發生的行爲,

要看 JVM。這條同樣也是 Enumeration 和 Iterator 的區別。

由於 Hashtable 是線程安全的也是 synchronized,所以在單線程環境下它比 HashMap 要慢。如果你不需要同步,只需要單一線程,那麼使用 HashMap 性能要好過 Hashtable。

HashMap 不能保證隨着時間的推移 Map 中的元素次序是不變的。

49、HashSet和HashMap 的區別
HashMap實現了Map 接口HashSet 實現了Set 接口

HashMap 儲存鍵值對 HashSet 僅僅存儲對象

使用 put()方法將元素放入 map 中 使用 add()方法將元素放入 set 中

HashMap 中使用鍵對象來計算 hashcode 值 HashSet 使用成員對象來計算 hashcode 值,對於兩個對象來說 hashcode 可能相同,所以 equals()方法用來判斷對象的相等性,如果兩個對象不同的話,那麼返回 false

HashMap 比較快,因爲是使用唯一的鍵來獲取對象 HashSet 較 HashMap 來說比較慢

50ArrayList和LinkedList 的區別
1.ArrayList 是實現了基於動態數組的數據結構,LinkedList 基於鏈表的數據結構。

2.對於隨機訪問 get 和 set,ArrayList 優於 LinkedList,因爲 ArrayList 可以隨機定位,而LinkedList 要移動指針一步一步的移動到節點處。(參考數組與鏈表來思考)

3.對於新增和刪除操作 add 和 remove,LinedList 比較佔優勢,只需要對指針進行修改即可,而 ArrayList 要移動數據來填補被刪除的對象的空間。

Web部分
51、XML文檔定義有幾種形式?它們之間有何本質區別?解析XML文檔有哪幾種方式?
答:XML文檔定義分爲DTD和Schema兩種形式,二者都是對XML語法的約束,其本質區別在於Schema本身也是一個XML文件,可以被XML解析器解析,而且可以爲XML承載的數據定義類型,約束能力較之DTD更強大。對XML的解析主要有DOM(文檔對象模型,Document Object Model)、SAX(Simple API for XML)和StAX(Java 6中引入的新的解析XML的方式,Streaming API for XML),其中DOM處理大型文件時其性能下降的非常厲害,這個問題是由DOM樹結構佔用的內存較多造成的,而且DOM解析方式必須在解析文件之前把整個文檔裝入內存,適合對XML的隨機訪問(典型的用空間換取時間的策略);SAX是事件驅動型的XML解析方式,它順序讀取XML文件,不需要一次全部裝載整個文件。當遇到像文件開頭,文檔結束,或者標籤開頭與標籤結束時,它會觸發一個事件,用戶通過事件回調代碼來處理XML文件,適合對XML的順序訪問;顧名思義,StAX把重點放在流上,實際上StAX與其他解析方式的本質區別就在於應用程序能夠把XML作爲一個事件流來處理。將XML作爲一組事件來處理的想法並不新穎(SAX就是這樣做的),但不同之處在於StAX允許應用程序代碼把這些事件逐個拉出來,而不用提供在解析器方便時從解析器中接收事件的處理程序。

52、你在項目中哪些地方用到了XML?
答:XML的主要作用有兩個方面:數據交換和信息配置。在做數據交換時,XML將數據用標籤組裝成起來,然後壓縮打包加密後通過網絡傳送給接收者,接收解密與解壓縮後再從XML文件中還原相關信息進行處理,XML曾經是異構系統間交換數據的事實標準,但此項功能幾乎已經被JSON(JavaScript Object Notation)取而代之。當然,目前很多軟件仍然使用XML來存儲配置信息,我們在很多項目中通常也會將作爲配置信息的硬代碼寫在XML文件中,Java的很多框架也是這麼做的,而且這些框架都選擇了dom4j作爲處理XML的工具,因爲Sun公司的官方API實在不怎麼好用。

補充:現在有很多時髦的軟件(如Sublime)已經開始將配置文件書寫成JSON格式,我們已經強烈的感受到XML的另一項功能也將逐漸被業界拋棄。

53、闡述JDBC操作數據庫的步驟。
答:下面的代碼以連接本機的Oracle數據庫爲例,演示JDBC操作數據庫的步驟。

加載驅動。

創建連接。

創建語句。

執行語句。

處理結果。

關閉資源。

54、Statement和PreparedStatement有什麼區別?哪個性能更好?
答:與Statement相比,①PreparedStatement接口代表預編譯的語句,它主要的優勢在於可以減少SQL的編譯錯誤並增加SQL的安全性(減少SQL注射攻擊的可能性);②PreparedStatement中的SQL語句是可以帶參數的,避免了用字符串連接拼接SQL語句的麻煩和不安全;③當批量處理SQL或頻繁執行相同的查詢時,PreparedStatement有明顯的性能上的優勢,由於數據庫可以將編譯優化後的SQL語句緩存起來,下次執行相同結構的語句時就會很快(不用再次編譯和生成執行計劃)。

補充:爲了提供對存儲過程的調用,JDBC API中還提供了CallableStatement接口。存儲過程(StoredProcedure)是數據庫中一組爲了完成特定功能的SQL語句的集合,經編譯後存儲在數據庫中,用戶通過指定存儲過程的名字並給出參數(如果該存儲過程帶有參數)來執行它。雖然調用存儲過程會在網絡開銷、安全性、性能上獲得很多好處,但是存在如果底層數據庫發生遷移時就會有很多麻煩,因爲每種數據庫的存儲過程在書寫上存在不少的差別。

55、使用JDBC操作數據庫時,如何提升讀取數據的性能?如何提升更新數據的性能?
答:要提升讀取數據的性能,可以指定通過結果集(ResultSet)對象的setFetchSize()方法指定每次抓取的記錄數(典型的空間換時間策略);要提升更新數據的性能可以使用PreparedStatement語句構建批處理,將若干SQL語句置於一個批處理中執行。

56、在進行數據庫編程時,連接池有什麼作用?
答:由於創建連接和釋放連接都有很大的開銷(尤其是數據庫服務器不在本地時,每次建立連接都需要進行TCP的三次握手,釋放連接需要進行TCP四次握手,造成的開銷是不可忽視的),爲了提升系統訪問數據庫的性能,可以事先創建若干連接置於連接池中,需要時直接從連接池獲取,使用結束時歸還連接池而不必關閉連接,從而避免頻繁創建和釋放連接所造成的開銷,這是典型的用空間換取時間的策略(浪費了空間存儲連接,但節省了創建和釋放連接的時間)。池化技術在Java開發中是很常見的,在使用線程時創建線程池的道理與此相同。基於Java的開源數據庫連接池主要有:C3P0、Proxool、DBCP、BoneCP、Druid等。

補充:在計算機系統中時間和空間是不可調和的矛盾,理解這一點對設計滿足性能要求的算法是至關重要的。大型網站性能優化的一個關鍵就是使用緩存,而緩存跟上面講的連接池道理非常類似,也是使用空間換時間的策略。可以將熱點數據置於緩存中,當用戶查詢這些數據時可以直接從緩存中得到,這無論如何也快過去數據庫中查詢。當然,緩存的置換策略等也會對系統性能產生重要影響,對於這個問題的討論已經超出了這裏要闡述的範圍。

57、什麼是DAO模式?
答:DAO(Data Access Object)顧名思義是一個爲數據庫或其他持久化機制提供了抽象接口的對象,在不暴露底層持久化方案實現細節的前提下提供了各種數據訪問操作。在實際的開發中,應該將所有對數據源的訪問操作進行抽象化後封裝在一個公共API中。用程序設計語言來說,就是建立一個接口,接口中定義了此應用程序中將會用到的所有事務方法。在這個應用程序中,當需要和數據源進行交互的時候則使用這個接口,並且編寫一個單獨的類來實現這個接口,在邏輯上該類對應一個特定的數據存儲。DAO模式實際上包含了兩個模式,一是Data Accessor(數據訪問器),二是Data Object(數據對象),前者要解決如何訪問數據的問題,而後者要解決的是如何用對象封裝數據。

58、事務的ACID是指什麼?
答:

  • 原子性(Atomic):事務中各項操作,要麼全做要麼全不做,任何一項操作的失敗都會導致整個事務的失敗;

  • 一致性(Consistent):事務結束後系統狀態是一致的;

  • 隔離性(Isolated):併發執行的事務彼此無法看到對方的中間狀態;

  • 持久性(Durable):事務完成後所做的改動都會被持久化,即使發生災難性的失敗。通過日誌和同步備份可以在故障發生後重建數據。

59、獲得一個類的類對象有哪些方式?
答:

  • 方法1:類型.class,例如:String.class

  • 方法2:對象.getClass(),例如:“hello”.getClass()

  • 方法3:Class.forName(),例如:Class.forName(“java.lang.String”)

60、如何通過反射創建對象?
答:

  • 方法1:通過類對象調用newInstance()方法,例如:String.class.newInstance()

  • 方法2:通過類對象的getConstructor()或getDeclaredConstructor()方法獲得構造器(Constructor)對象並調用其newInstance()方法創建對象,例如:String.class.getConstructor(String.class).newInstance(“Hello”);

61、如何通過反射獲取和設置對象私有字段的值?
答:可以通過類對象的getDeclaredField()方法字段(Field)對象,然後再通過字段對象的setAccessible(true)將其設置爲可以訪問,接下來就可以通過get/set方法來獲取/設置字段的值了。下面的代碼實現了一個反射的工具類,其中的兩個靜態方法分別用於獲取和設置私有字段的值,字段可以是基本類型也可以是對象類型且支持多級對象操作。

62、簡述一下你瞭解的設計模式。
答:所謂設計模式,就是一套被反覆使用的代碼設計經驗的總結(情境中一個問題經過證實的一個解決方案)。使用設計模式是爲了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性。設計模式使人們可以更加簡單方便的複用成功的設計和體系結構。將已證實的技術表述成設計模式也會使新系統開發者更加容易理解其設計思路。

在GoF的《Design Patterns: Elements of Reusable Object-Oriented Software》中給出了三類(創建型[對類的實例化過程的抽象化]、結構型[描述如何將類或對象結合在一起形成更大的結構]、行爲型[對在不同的對象之間劃分責任和算法的抽象化])共23種設計模式,包括:Abstract Factory(抽象工廠模式),Builder(建造者模式),Factory Method(工廠方法模式),Prototype(原始模型模式),Singleton(單例模式);Facade(門面模式),Adapter(適配器模式),Bridge(橋樑模式),Composite(合成模式),Decorator(裝飾模式),Flyweight(享元模式),Proxy(代理模式);Command(命令模式),Interpreter(解釋器模式),Visitor(訪問者模式),Iterator(迭代子模式),Mediator(調停者模式),Memento(備忘錄模式),Observer(觀察者模式),State(狀態模式),Strategy(策略模式),Template Method(模板方法模式),Chain Of Responsibility(責任鏈模式)。

面試被問到關於設計模式的知識時,可以揀最常用的作答,例如:

  • 工廠模式:工廠類可以根據條件生成不同的子類實例,這些子類有一個公共的抽象父類並且實現了相同的方法,

但是這些方法針對不同的數據進行了不同的操作(多態方法)。當得到子類的實例後,開發人員可以調用基類中的方法而不必考慮到底返回的是哪一個子類的實例。

  • 代理模式:給一個對象提供一個代理對象,並由代理對象控制原對象的引用。實際開發中,按照使用目的的不同,代理可以分爲:遠程代理、虛擬代理、保護代理、Cache代理、防火牆代理、同步化代理、智能引用代理。

  • 適配器模式:把一個類的接口變換成客戶端所期待的另一種接口,從而使原本因接口不匹配而無法在一起使用的類能夠一起工作。

  • 模板方法模式:提供一個抽象類,將部分邏輯以具體方法或構造器的形式實現,然後聲明一些抽象方法來迫使子類實現剩餘的邏輯。不同的子類可以以不同的方式實現這些抽象方法(多態實現),從而實現不同的業務邏輯。除此之外,還可以講講上面提到的門面模式、橋樑模式、單例模式、裝潢模式(Collections工具類和I/O系統中都使用裝潢模式)等。

63、Servlet接口中有哪些方法?
答:Servlet接口定義了5個方法,其中前三個方法與Servlet生命週期相關:

  • void init(ServletConfig config) throws ServletException

  • void service(ServletRequest req, ServletResponse resp) throws ServletException, java.io.IOException

  • void destory()

  • java.lang.String getServletInfo()

  • ServletConfig getServletConfig()

Web容器加載Servlet並將其實例化後,Servlet生命週期開始,容器運行其init()方法進行Servlet的初始化;請求到

達時調用Servlet的service()方法,service()方法會根據需要調用與請求對應的doGet或doPost等方法;當服務器關

閉或項目被卸載時服務器會將Servlet實例銷燬,此時會調用Servlet的destroy()方法。

64、轉發(forward)和重定向(redirect)的區別?
答:forward是容器中控制權的轉向,是服務器請求資源,服務器直接訪問目標地址的URL,把那個URL 的響應內

容讀取過來,然後把這些內容再發給瀏覽器,瀏覽器根本不知道服務器發送的內容是從哪兒來的,所以它的地址欄

中還是原來的地址。redirect就是服務器端根據邏輯,發送一個狀態碼,告訴瀏覽器重新去請求那個地址,因此從

瀏覽器的地址欄中可以看到跳轉後的鏈接地址,很明顯redirect無法訪問到服務器保護起來資源,但是可以從一個

網站redirect到其他網站。forward更加高效,所以在滿足需要時儘量使用forward(通過調用RequestDispatcher

對象的forward()方法,該對象可以通過ServletRequest對象的getRequestDispatcher()方法獲得),並且這樣也

有助於隱藏實際的鏈接;在有些情況下,比如需要訪問一個其它服務器上的資源,則必須使用重定向(通過

HttpServletResponse對象調用其sendRedirect()方法實現)。

65、JSP有哪些內置對象?作用分別是什麼?

  • request:封裝客戶端的請求,其中包含來自GET或POST請求的參數;

  • response:封裝服務器對客戶端的響應;

  • pageContext:通過該對象可以獲取其他對象;

  • session:封裝用戶會話的對象;

  • application:封裝服務器運行環境的對象;

  • out:輸出服務器響應的輸出流對象;

  • config:Web應用的配置對象;

  • page:JSP頁面本身(相當於Java程序中的this);

  • exception:封裝頁面拋出異常的對象。

66、get和post請求的區別?
答:

①get請求用來從服務器上獲得資源,而post是用來向服務器提交數據;

②get將表單中數據按照name=value的形式,添加到action 所指向的URL 後面,並且兩者使用"?“連接,而各個變量之間使用”&"連接;post是將表單中的數據放在HTTP協議的請求頭或消息體中,傳遞到action所指向URL;

③get傳輸的數據要受到URL長度限制(1024字節);而post可以傳輸大量的數據,上傳文件通常要使用post方式;

④使用get時參數會顯示在地址欄上,如果這些數據不是敏感數據,那麼可以使用get;對於敏感數據還是應用使用post;

⑤get使用MIME類型application/x-www-form-urlencoded的URL編碼(也叫百分號編碼)文本的格式傳遞參數,保證被傳送的參數由遵循規範的文本組成,例如一個空格的編碼是"%20"。

67、常用的Web服務器有哪些?
答:Unix和Linux平臺下使用最廣泛的免費HTTP服務器是Apache服務器,而Windows平臺的服務器通常使用IIS作爲Web服務器。選擇Web服務器應考慮的因素有:性能、安全性、日誌和統計、虛擬主機、代理服務器、緩衝服務和集成應用程序等。

68、JSP和Servlet是什麼關係?
答:其實這個問題在上面已經闡述過了,Servlet是一個特殊的Java程序,它運行於服務器的JVM中,能夠依靠服務器的支持向瀏覽器提供顯示內容。JSP本質上是Servlet的一種簡易形式,JSP會被服務器處理成一個類似於Servlet的Java程序,可以簡化頁面內容的生成。Servlet和JSP最主要的不同點在於,Servlet的應用邏輯是在Java文件中,並且完全從表示層中的HTML分離開來。而JSP的情況是Java和HTML可以組合成一個擴展名爲.jsp的文件。有人說,Servlet就是在Java中寫HTML,而JSP就是在HTML中寫Java代碼,當然這個說法是很片面且不夠準確的。JSP側重於視圖,Servlet更側重於控制邏輯,在MVC架構模式中,JSP適合充當視圖(view)而Servlet適合充當控制器(controller)。

69、講解JSP中的四種作用域。
答:JSP中的四種作用域包括page、request、session和application,具體來說:

  • page代表與一個頁面相關的對象和屬性。

  • request代表與Web客戶機發出的一個請求相關的對象和屬性。一個請求可能跨越多個頁面,涉及多個Web組件;需要在頁面顯示的臨時數據可以置於此作用域。

  • session代表與某個用戶與服務器建立的一次會話相關的對象和屬性。跟某個用戶相關的數據應該放在用戶自己的session中。

  • application代表與整個Web應用程序相關的對象和屬性,它實質上是跨越整個Web應用程序,包括多個頁面、請求和會話的一個全局作用域。

70、如何實現JSP或Servlet的單線程模式?
答:

對於JSP頁面,可以通過page指令進行設置。

<%@page isThreadSafe=”false”%>

對於Servlet,可以讓自定義的Servlet實現SingleThreadModel標識接口。

說明:如果將JSP或Servlet設置成單線程工作模式,會導致每個請求創建一個Servlet實例,這種實踐將導致嚴重的性能問題(服務器的內存壓力很大,還會導致頻繁的垃圾回收),所以通常情況下並不會這麼做。

71、過濾器有哪些作用和用法?
答: Java Web開發中的過濾器(filter)是從Servlet 2.3規範開始增加的功能,並在Servlet 2.4規範中得到增強。對Web應用來說,過濾器是一個駐留在服務器端的Web組件,它可以截取客戶端和服務器之間的請求與響應信息,並對這些信息進行過濾。當Web容器接受到一個對資源的請求時,它將判斷是否有過濾器與這個資源相關聯。如果有,那麼容器將把請求交給過濾器進行處理。在過濾器中,你可以改變請求的內容,或者重新設置請求的報頭信息,然後再將請求發送給目標資源。當目標資源對請求作出響應時候,容器同樣會將響應先轉發給過濾器,在過濾器中你可以對響應的內容進行轉換,然後再將響應發送到客戶端。

常見的過濾器用途主要包括:對用戶請求進行統一認證、對用戶的訪問請求進行記錄和審覈、對用戶發送的數據進行過濾或替換、轉換圖象格式、對響應內容進行壓縮以減少傳輸量、對請求或響應進行加解密處理、觸發資源訪問事件、對XML的輸出應用XSLT等。

和過濾器相關的接口主要有:Filter、FilterConfig和FilterChain。

72、監聽器有哪些作用和用法?
答:Java Web開發中的監聽器(listener)就是application、session、request三個對象創建、銷燬或者往其中添加修改刪除屬性時自動執行代碼的功能組件,如下所示:

①ServletContextListener:對Servlet上下文的創建和銷燬進行監聽。

②ServletContextAttributeListener:監聽Servlet上下文屬性的添加、刪除和替換。

③HttpSessionListener:對Session的創建和銷燬進行監聽。

補充:session的銷燬有兩種情況:1). session超時(可以在web.xml中通過/標籤配置超時時間);2). 通過調用session對象的invalidate()方法使session失效。

④HttpSessionAttributeListener:對Session對象中屬性的添加、刪除和替換進行監聽。

⑤ServletRequestListener:對請求對象的初始化和銷燬進行監聽。

⑥ServletRequestAttributeListener:對請求對象屬性的添加、刪除和替換進行監聽。

73、JSP中的靜態包含和動態包含有什麼區別?
答:靜態包含是通過JSP的include指令包含頁面,動態包含是通過JSP標準動作jsp:forward包含頁面。靜態包含是編譯時包含,如果包含的頁面不存在則會產生編譯錯誤,而且兩個頁面的"contentType"屬性應保持一致,因爲兩個頁面會合二爲一,只產生一個class文件,因此被包含頁面發生的變動再包含它的頁面更新前不會得到更新。動態包含是運行時包含,可以向被包含的頁面傳遞參數,包含頁面和被包含頁面是獨立的,會編譯出兩個class文件,如果被包含的頁面不存在,不會產生編譯錯誤,也不影響頁面其他部分的執行。

74、Servlet中如何獲取用戶提交的查詢參數或表單數據?
答:可以通過請求對象(HttpServletRequest)的getParameter()方法通過參數名獲得參數值。如果有包含多個值的參數(例如複選框),可以通過請求對象的getParameterValues()方法獲得。當然也可以通過請求對象的getParameterMap()獲得一個參數名和參數值的映射(Map)。

75、Servlet中如何獲取用戶配置的初始化參數以及服務器上下文參數?
答:可以通過重寫Servlet接口的init(ServletConfig)方法並通過ServletConfig對象的getInitParameter()方法來獲取Servlet的初始化參數。可以通過ServletConfig對象的getServletContext()方法獲取ServletContext對象,並通過該對象的getInitParameter()方法來獲取服務器上下文參數。當然,ServletContext對象也在處理用戶請求的方法(如doGet()方法)中通過請求對象的getServletContext()方法來獲得。

76、如何設置請求的編碼以及響應內容的類型?
答:通過請求對象(ServletRequest)的setCharacterEncoding(String)方法可以設置請求的編碼,其實要徹底解決亂碼問題就應該讓頁面、服務器、請求和響應、Java程序都使用統一的編碼,最好的選擇當然是UTF-8;通過響應對象(ServletResponse)的setContentType(String)方法可以設置響應內容的類型,當然也可以通過HttpServletResponsed對象的setHeader(String, String)方法來設置。

77、什麼是Web Service(Web服務)?
答:從表面上看,Web Service就是一個應用程序,它向外界暴露出一個能夠通過Web進行調用的API。這就是說,你能夠用編程的方法透明的調用這個應用程序,不需要了解它的任何細節,跟你使用的編程語言也沒有關係。

例如可以創建一個提供天氣預報的Web Service,那麼無論你用哪種編程語言開發的應用都可以通過調用它的API並傳入城市信息來獲得該城市的天氣預報。之所以稱之爲Web Service,是因爲它基於HTTP協議傳輸數據,這使得運行在不同機器上的不同應用無須藉助附加的、專門的第三方軟件或硬件,就可相互交換數據或集成。

框架部分
78、什麼是ORM?
答:對象關係映射(Object-Relational Mapping,簡稱ORM)是一種爲了解決程序的面向對象模型與數據庫的關係模型互不匹配問題的技術;簡單的說,ORM是通過使用描述對象和數據庫之間映射的元數據(在Java中可以用XML或者是註解),將程序中的對象自動持久化到關係數據庫中或者將關係數據庫表中的行轉換成Java對象,其本質上就是將數據從一種形式轉換到另外一種形式。

79、持久層設計要考慮的問題有哪些?你用過的持久層框架有哪些?
答:所謂"持久"就是將數據保存到可掉電式存儲設備中以便今後使用,簡單的說,就是將內存中的數據保存到關係型數據庫、文件系統、消息隊列等提供持久化支持的設備中。持久層就是系統中專注於實現數據持久化的相對獨立的層面。

持久層設計的目標包括:

  • 數據存儲邏輯的分離,提供抽象化的數據訪問接口。

  • 數據訪問底層實現的分離,可以在不修改代碼的情況下切換底層實現。

  • 資源管理和調度的分離,在數據訪問層實現統一的資源調度(如緩存機制)。

  • 數據抽象,提供更面向對象的數據操作。

80、Hibernate中SessionFactory是線程安全的嗎?Session是線程安全的嗎(兩個線程能夠共享同一個Session嗎)?
答:SessionFactory對應Hibernate的一個數據存儲的概念,它是線程安全的,可以被多個線程併發訪問。SessionFactory一般只會在啓動的時候構建。對於應用程序,最好將SessionFactory通過單例模式進行封裝以便於訪問。Session是一個輕量級非線程安全的對象(線程間不能共享session),它表示與數據庫進行交互的一個工作單元。Session是由SessionFactory創建的,在任務完成之後它會被關閉。Session是持久層服務對外提供的主要接口。Session會延遲獲取數據庫連接(也就是在需要的時候纔會獲取)。爲了避免創建太多的session,可以使用ThreadLocal將session和當前線程綁定在一起,這樣可以讓同一個線程獲得的總是同一個session。Hibernate 3中SessionFactory的getCurrentSession()方法就可以做到。

81、Hibernate中Session的load和get方法的區別是什麼?
答:主要有以下三項區別:

① 如果沒有找到符合條件的記錄,get方法返回null,load方法拋出異常。

② get方法直接返回實體類對象,load方法返回實體類對象的代理。

③ 在Hibernate 3之前,get方法只在一級緩存中進行數據查找,如果沒有找到對應的數據則越過二級緩存,直接發出SQL語句完成數據讀取;load方法則可以從二級緩存中獲取數據;從Hibernate 3開始,get方法不再是對二級緩存只寫不讀,它也是可以訪問二級緩存的。

說明:對於load()方法Hibernate認爲該數據在數據庫中一定存在可以放心的使用代理來實現延遲加載,如果沒有數據就拋出異常,而通過get()方法獲取的數據可以不存在。

82、闡述Session加載實體對象的過程。
答:Session加載實體對象的步驟是:

① Session在調用數據庫查詢功能之前,首先會在一級緩存中通過實體類型和主鍵進行查找,如果一級緩存查找命中且數據狀態合法,則直接返回;

② 如果一級緩存沒有命中,接下來Session會在當前NonExists記錄(相當於一個查詢黑名單,如果出現重複的無效查詢可以迅速做出判斷,從而提升性能)中進行查找,如果NonExists中存在同樣的查詢條件,則返回null;

③ 如果一級緩存查詢失敗則查詢二級緩存,如果二級緩存命中則直接返回;

④ 如果之前的查詢都未命中,則發出SQL語句,如果查詢未發現對應記錄則將此次查詢添加到Session的NonExists中加以記錄,並返回null;

⑤ 根據映射配置和SQL語句得到ResultSet,並創建對應的實體對象;

⑥ 將對象納入Session(一級緩存)的管理;

⑦ 如果有對應的攔截器,則執行攔截器的onLoad方法;

⑧ 如果開啓並設置了要使用二級緩存,則將數據對象納入二級緩存;

⑨ 返回數據對象。

83、Query接口的list方法和iterate方法有什麼區別?
答:

① list()方法無法利用一級緩存和二級緩存(對緩存只寫不讀),它只能在開啓查詢緩存的前提下使用查詢緩存;iterate()方法可以充分利用緩存,如果目標數據只讀或者讀取頻繁,使用iterate()方法可以減少性能開銷。

② list()方法不會引起N+1查詢問題,而iterate()方法可能引起N+1查詢問題

84、Hibernate如何實現分頁查詢?
答:通過Hibernate實現分頁查詢,開發人員只需要提供HQL語句(調用Session的createQuery()方法)或查詢條件(調用Session的createCriteria()方法)、設置查詢起始行數(調用Query或Criteria接口的setFirstResult()方法)和最大查詢行數(調用Query或Criteria接口的setMaxResults()方法),並調用Query或Criteria接口的list()方法,Hibernate會自動生成分頁查詢的SQL語句。

85、如何理解Hibernate的延遲加載機制?在實際應用中,延遲加載與Session關閉的矛盾是如何處理的?
答:延遲加載就是並不是在讀取的時候就把數據加載進來,而是等到使用時再加載。Hibernate使用了虛擬代理機制實現延遲加載,我們使用Session的load()方法加載數據或者一對多關聯映射在使用延遲加載的情況下從一的一方加載多的一方,得到的都是虛擬代理,簡單的說返回給用戶的並不是實體本身,而是實體對象的代理。代理對象在用戶調用getter方法時纔會去數據庫加載數據。但加載數據就需要數據庫連接。而當我們把會話關閉時,數據庫連接就同時關閉了。

延遲加載與session關閉的矛盾一般可以這樣處理:

① 關閉延遲加載特性。這種方式操作起來比較簡單,因爲Hibernate的延遲加載特性是可以通過映射文件或者註解進行配置的,但這種解決方案存在明顯的缺陷。首先,出現"no session or session was closed"通常說明系統中已經存在主外鍵關聯,如果去掉延遲加載的話,每次查詢的開銷都會變得很大。

② 在session關閉之前先獲取需要查詢的數據,可以使用工具方法Hibernate.isInitialized()判斷對象是否被加載,如果沒有被加載則可以使用Hibernate.initialize()方法加載對象。

③ 使用攔截器或過濾器延長Session的生命週期直到視圖獲得數據。Spring整合Hibernate提供的OpenSessionInViewFilter和OpenSessionInViewInterceptor就是這種做法。

86、簡述Hibernate常見優化策略。
答:這個問題應當挑自己使用過的優化策略回答,常用的有:

① 制定合理的緩存策略(二級緩存、查詢緩存)。

② 採用合理的Session管理機制。

③ 儘量使用延遲加載特性。

④ 設定合理的批處理參數。

⑤ 如果可以,選用UUID作爲主鍵生成器。

⑥ 如果可以,選用基於版本號的樂觀鎖替代悲觀鎖。

⑦ 在開發過程中, 開啓hibernate.show_sql選項查看生成的SQL,從而瞭解底層的狀況;開發完成後關閉此選項。

⑧ 考慮數據庫本身的優化,合理的索引、恰當的數據分區策略等都會對持久層的性能帶來可觀的提升,但這些需要專業的DBA(數據庫管理員)提供支持。

87、談一談Hibernate的一級緩存、二級緩存和查詢緩存。
答:Hibernate的Session提供了一級緩存的功能,默認總是有效的,當應用程序保存持久化實體、修改持久化實體時,Session並不會立即把這種改變提交到數據庫,而是緩存在當前的Session中,除非顯示調用了Session的flush()方法或通過close()方法關閉Session。通過一級緩存,可以減少程序與數據庫的交互,從而提高數據庫訪問性能。

SessionFactory級別的二級緩存是全局性的,所有的Session可以共享這個二級緩存。不過二級緩存默認是關閉的,需要顯示開啓並指定需要使用哪種二級緩存實現類(可以使用第三方提供的實現)。一旦開啓了二級緩存並設置了需要使用二級緩存的實體類,SessionFactory就會緩存訪問過的該實體類的每個對象,除非緩存的數據超出了指定的緩存空間。

一級緩存和二級緩存都是對整個實體進行緩存,不會緩存普通屬性,如果希望對普通屬性進行緩存,可以使用查詢緩存。查詢緩存是將HQL或SQL語句以及它們的查詢結果作爲鍵值對進行緩存,對於同樣的查詢可以直接從緩存中獲取數據。查詢緩存默認也是關閉的,需要顯示開啓。

88、@OneToMany註解的mappedBy屬性有什麼作用?
答:@OneToMany用來配置一對多關聯映射,但通常情況下,一對多關聯映射都由多的一方來維護關聯關係,例如學生和班級,應該在學生類中添加班級屬性來維持學生和班級的關聯關係(在數據庫中是由學生表中的外鍵班級編號來維護學生表和班級表的多對一關係),如果要使用雙向關聯,在班級類中添加一個容器屬性來存放學生,並使用@OneToMany註解進行映射,此時mappedBy屬性就非常重要。如果使用XML進行配置,可以用標籤的inverse="true"設置來達到同樣的效果。

89、MyBatis中使用 # 和 $ 書寫佔位符有什麼區別?
答: # 將傳入的數據都當成一個字符串,會對傳入的數據自動加上引號; $ 將傳入的數據直接顯示生成在SQL中。

注意:使用 $ 佔位符可能會導致SQL注射攻擊,能用 # 的地方就不要使用 $ ,寫order by子句的時候應該用 $ 而不是 # 。

90、解釋一下MyBatis中命名空間(namespace)的作用。
答:在大型項目中,可能存在大量的SQL語句,這時候爲每個SQL語句起一個唯一的標識(ID)就變得並不容易了。爲了解決這個問題,在MyBatis中,可以爲每個映射文件起一個唯一的命名空間,這樣定義在這個映射文件中的每個SQL語句就成了定義在這個命名空間中的一個ID。只要我們能夠保證每個命名空間中這個ID是唯一的,即使在不同映射文件中的語句ID相同,也不會再產生衝突了。

91、MyBatis中的動態SQL是什麼意思?
答:對於一些複雜的查詢,我們可能會指定多個查詢條件,但是這些條件可能存在也可能不存在,例如在58同城上面找房子,我們可能會指定面積、樓層和所在位置來查找房源,也可能會指定面積、價格、戶型和所在位置來查找房源,此時就需要根據用戶指定的條件動態生成SQL語句。如果不使用持久層框架我們可能需要自己拼裝SQL語句,還好MyBatis提供了動態SQL的功能來解決這個問題。

92、什麼是IoC和DI?DI是如何實現的?
答:IoC叫控制反轉,是Inversion of Control的縮寫,DI(Dependency Injection)叫依賴注入,是對IoC更簡單的詮釋。控制反轉是把傳統上由程序代碼直接操控的對象的調用權交給容器,通過容器來實現對象組件的裝配和管理。所謂的"控制反轉"就是對組件對象控制權的轉移,從程序代碼本身轉移到了外部容器,由容器來創建對象並管理對象之間的依賴關係。IoC體現了好萊塢原則 - “Don’t call me, we will call you”。依賴注入的基本原則是應用組件不應該負責查找資源或者其他依賴的協作對象。配置對象的工作應該由容器負責,查找資源的邏輯應該從應用組件的代碼中抽取出來,交給容器來完成。DI是對IoC更準確的描述,即組件之間的依賴關係由容器在運行期決定,形象的來說,即由容器動態的將某種依賴關係注入到組件之中。

93、解釋一下什麼叫AOP(面向切面編程)?
答:AOP(Aspect-Oriented Programming)指一種程序設計範型,該範型以一種稱爲切面(aspect)的語言構造爲基礎,切面是一種新的模塊化機制,用來描述分散在對象、類或方法中的橫切關注點(crosscuttingconcern)。

94、Spring中自動裝配的方式有哪些?
答:

  • no:不進行自動裝配,手動設置Bean的依賴關係。

  • byName:根據Bean的名字進行自動裝配。

  • byType:根據Bean的類型進行自動裝配。

  • constructor:類似於byType,不過是應用於構造器的參數,如果正好有一個Bean與構造器的參數類型相同則可以自動裝配,否則會導致錯誤。

  • autodetect:如果有默認的構造器,則通過constructor的方式進行自動裝配,否則使用byType的方式進行自動裝配。

說明:自動裝配沒有自定義裝配方式那麼精確,而且不能自動裝配簡單屬性(基本類型、字符串等),在使用時應注意。

95、Spring中如何使用註解來配置Bean?有哪些相關的註解?
答:首先需要在Spring配置文件中增加如下配置:

<context:component-scan base-package=“org.example”/>

然後可以用@Component、@Controller、@Service、@Repository註解來標註需要由Spring IoC容器進行對象託管的類。這幾個註解沒有本質區別,只不過@Controller通常用於控制器,@Service通常用於業務邏輯類,

@Repository通常用於倉儲類(例如我們的DAO實現類),普通的類用@Component來標註。

96、Spring支持的事務管理類型有哪些?你在項目中使用哪種方式?
答:Spring支持編程式事務管理和聲明式事務管理。許多Spring框架的用戶選擇聲明式事務管理,因爲這種方式和應用程序的關聯較少,因此更加符合輕量級容器的概念。聲明式事務管理要優於編程式事務管理,儘管在靈活性方面它弱於編程式事務管理,因爲編程式事務允許你通過代碼控制業務。

事務分爲全局事務和局部事務。全局事務由應用服務器管理,需要底層服務器JTA支持(如WebLogic、WildFly等)。局部事務和底層採用的持久化方案有關,例如使用JDBC進行持久化時,需要使用Connetion對象來操作事務;而採用Hibernate進行持久化時,需要使用Session對象來操作事務。

97、Spring MVC的工作原理是怎樣的?
答:Spring MVC的工作原理如下圖所示:

① 客戶端的所有請求都交給前端控制器DispatcherServlet來處理,它會負責調用系統的其他模塊來真正處理用戶的請求。

② DispatcherServlet收到請求後,將根據請求的信息(包括URL、HTTP協議方法、請求頭、請求參數、Cookie等)以及HandlerMapping的配置找到處理該請求的Handler(任何一個對象都可以作爲請求的Handler)。

③在這個地方Spring會通過HandlerAdapter對該處理器進行封裝。

④ HandlerAdapter是一個適配器,它用統一的接口對各種Handler中的方法進行調用。

⑤ Handler完成對用戶請求的處理後,會返回一個ModelAndView對象給DispatcherServlet,ModelAndView顧名思義,包含了數據模型以及相應的視圖的信息。

⑥ ModelAndView的視圖是邏輯視圖,DispatcherServlet還要藉助ViewResolver完成從邏輯視圖到真實視圖對象的解析工作。

⑦ 當得到真正的視圖對象後,DispatcherServlet會利用視圖對象對模型數據進行渲染。

⑧ 客戶端得到響應,可能是一個普通的HTML頁面,也可以是XML或JSON字符串,還可以是一張圖片或者一個PDF文件。

98、選擇使用Spring框架的原因(Spring框架爲企業級開發帶來的好處有哪些)?
答:可以從以下幾個方面作答:

  • 非侵入式:支持基於POJO的編程模式,不強制性的要求實現Spring框架中的接口或繼承Spring框架中的類。

  • IoC容器:IoC容器幫助應用程序管理對象以及對象之間的依賴關係,對象之間的依賴關係如果發生了改變只需要修改配置文件而不是修改代碼,因爲代碼的修改可能意味着項目的重新構建和完整的迴歸測試。有了IoC容器,程序員再也不需要自己編寫工廠、單例,這一點特別符合Spring的精神"不要重複的發明輪子"。

  • AOP(面向切面編程):將所有的橫切關注功能封裝到切面(aspect)中,通過配置的方式將橫切關注功能動態添加到目標代碼上,進一步實現了業務邏輯和系統服務之間的分離。另一方面,有了AOP程序員可以省去很多自己寫代理類的工作。

  • MVC:Spring的MVC框架是非常優秀的,從各個方面都可以甩Struts 2幾條街,爲Web表示層提供了更好的解決

方案。

  • 事務管理:Spring以寬廣的胸懷接納多種持久層技術,並且爲其提供了聲明式的事務管理,在不需要任何一行代碼的情況下就能夠完成事務管理。

  • 其他:選擇Spring框架的原因還遠不止於此,Spring爲Java企業級開發提供了一站式選擇,你可以在需要的時候使用它的部分和全部,更重要的是,你甚至可以在感覺不到Spring存在的情況下,在你的項目中使用Spring提供的各種優秀的功能。

99、闡述Spring框架中Bean的生命週期?
答:

① Spring IoC容器找到關於Bean的定義並實例化該Bean。

② Spring IoC容器對Bean進行依賴注入。

③ 如果Bean實現了BeanNameAware接口,則將該Bean的id傳給setBeanName方法。

④ 如果Bean實現了BeanFactoryAware接口,則將BeanFactory對象傳給setBeanFactory方法。

⑤ 如果Bean實現了BeanPostProcessor接口,則調用其postProcessBeforeInitialization方法。

⑥ 如果Bean實現了InitializingBean接口,則調用其afterPropertySet方法。

⑦ 如果有和Bean關聯的BeanPostProcessors對象,則這些對象的postProcessAfterInitialization方法被調用。

⑧ 當銷燬Bean實例時,如果Bean實現了DisposableBean接口,則調用其destroy方法。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章