2020Java面試題總結(下篇)


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

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

六、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應用程序,包括多個頁面、請求和會話的一個全局作用域。
  • 由於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。這時就可以從中取出對應的值了。

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 的最后加上 ,這樣就把token以參數的形式加入請求了。

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

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

七、異常

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:

1 /*
 2  * java面試題--如果catch里面有return語句,finally里面的代碼還會執行嗎?
 3  */
 4 public class FinallyDemo2 {
 5     public static void main(String[] args) {
 6         System.out.println(getInt());
 7     }
 8 
 9     public static int getInt() {
10         int a = 10;
11         try {
12             System.out.println(a / 0);
13             a = 20;
14         } catch (ArithmeticException e) {
15             a = 30;
16             return a;
17             /*
18              * return a 在程序執行到這一步的時候,這里不是return a 而是 return 30;這個返回路徑就形成了
19              * 但是呢,它發現后面還有finally,所以繼續執行finally的內容,a=40
20              * 再次回到以前的路徑,繼續走return 30,形成返回路徑之后,這里的a就不是a變量了,而是常量30
21              */
22         } finally {
23             a = 40;
24         }
25 
26 //      return a;
27     }
28 }

執行結果:30

代碼示例2:

1 package com.java_02;
 2 
 3 /*
 4  * java面試題--如果catch里面有return語句,finally里面的代碼還會執行嗎?
 5  */
 6 public class FinallyDemo2 {
 7     public static void main(String[] args) {
 8         System.out.println(getInt());
 9     }
10 
11     public static int getInt() {
12         int a = 10;
13         try {
14             System.out.println(a / 0);
15             a = 20;
16         } catch (ArithmeticException e) {
17             a = 30;
18             return a;
19             /*
20              * return a 在程序執行到這一步的時候,這里不是return a 而是 return 30;這個返回路徑就形成了
21              * 但是呢,它發現后面還有finally,所以繼續執行finally的內容,a=40
22              * 再次回到以前的路徑,繼續走return 30,形成返回路徑之后,這里的a就不是a變量了,而是常量30
23              */
24         } finally {
25             a = 40;
26             return a; //如果這樣,就又重新形成了一條返回路徑,由於只能通過1個return返回,所以這里直接返回40
27         }
28 
29 //      return a;
30     }
31 }

執行結果:40

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

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

八、網絡

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 對象受到同源策略的影響,而利用 元素的這個開放策略,網頁可以得到從其他來源動態產生的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 要求。服務器一般需要增加如下響應頭的一種或幾種:

1 Access-Control-Allow-Origin: *
2 Access-Control-Allow-Methods: POST, GET, OPTIONS
3 Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
4 Access-Control-Max-Age: 86400

跨域請求默認不會攜帶Cookie信息,如果需要攜帶,請配置下述參數:

1 "Access-Control-Allow-Credentials": true
2 // Ajax設置
3 "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屬性返回元素的Window對象。你可以使用這個Window對象來訪問iframe的文檔及其內部DOM。

1 <!-- 
 2  下述用端口 
 3  10000表示:domainA
 4  10001表示:domainB
 5 -->
 6 
 7 <!-- localhost:10000 -->
 8 <script>
 9   var iframe = document.createElement('iframe');
10   iframe.style.display = 'none'; // 隱藏
11 
12   var state = 0; // 防止頁面無限刷新
13   iframe.onload = function() {
14       if(state === 1) {
15           console.log(JSON.parse(iframe.contentWindow.name));
16           // 清除創建的iframe
17           iframe.contentWindow.document.write('');
18           iframe.contentWindow.close();
19           document.body.removeChild(iframe);
20       } else if(state === 0) {
21           state = 1;
22           // 加載完成,指向當前域,防止錯誤(proxy.html為空白頁面)
23           // Blocked a frame with origin "http://localhost:10000" from accessing a cross-origin frame.
24           iframe.contentWindow.location = 'http://localhost:10000/proxy.html';
25       }
26   };
27 
28   iframe.src = 'http://localhost:10001';
29   document.body.appendChild(iframe);
30 </script>
31 
32 <!-- localhost:10001 -->
33 <!DOCTYPE html>
34 ...
35 <script>
36   window.name = JSON.stringify({a: 1, b: 2});
37 </script>
38 </html>

方式五:window.postMessage()

HTML5新特性,可以用來向其他所有的 window 對象發送消息。需要注意的是我們必須要保證所有的腳本執行完才發送 MessageEvent,如果在函數執行的過程中調用了它,就會讓后面的函數超時無法執行。

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

 1 <!-- 
 2  下述用端口 
 3  10000表示:domainA
 4  10001表示:domainB
 5 -->
 6 
 7 <!-- localhost:10000 -->
 8 <iframe src="http://localhost:10001/msg.html" name="myPostMessage" style="display:none;">
 9 </iframe>
10 
11 <script>
12   function main() {
13       LSsetItem('test', 'Test: ' + new Date());
14       LSgetItem('test', function(value) {
15           console.log('value: ' + value);
16       });
17       LSremoveItem('test');
18   }
19 
20   var callbacks = {};
21   window.addEventListener('message', function(event) {
22       if (event.source === frames['myPostMessage']) {
23           console.log(event)
24           var data = /^#localStorage#(\d+)(null)?#([\S\s]*)/.exec(event.data);
25           if (data) {
26               if (callbacks[data[1]]) {
27                   callbacks[data[1]](data[2] === 'null' ? null : data[3]);
28               }
29               delete callbacks[data[1]];
30           }
31       }
32   }, false);
33 
34   var domain = '*';
35   // 增加
36   function LSsetItem(key, value) {
37       var obj = {
38           setItem: key,
39           value: value
40       };
41       frames['myPostMessage'].postMessage(JSON.stringify(obj), domain);
42   }
43   // 獲取
44   function LSgetItem(key, callback) {
45       var identifier = new Date().getTime();
46       var obj = {
47           identifier: identifier,
48           getItem: key
49       };
50       callbacks[identifier] = callback;
51       frames['myPostMessage'].postMessage(JSON.stringify(obj), domain);
52   }
53   // 刪除
54   function LSremoveItem(key) {
55       var obj = {
56           removeItem: key
57       };
58       frames['myPostMessage'].postMessage(JSON.stringify(obj), domain);
59   }
60 </script>
61 
62 <!-- localhost:10001 -->
63 <script>
64   window.addEventListener('message', function(event) {
65     console.log('Receiver debugging', event);
66     if (event.origin == 'http://localhost:10000') {
67       var data = JSON.parse(event.data);
68       if ('setItem' in data) {
69         localStorage.setItem(data.setItem, data.value);
70       } else if ('getItem' in data) {
71         var gotItem = localStorage.getItem(data.getItem);
72         event.source.postMessage(
73           '#localStorage#' + data.identifier +
74           (gotItem === null ? 'null#' : '#' + gotItem),
75           event.origin
76         );
77       } else if ('removeItem' in data) {
78         localStorage.removeItem(data.removeItem);
79       }
80     }
81   }, false);
82 </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.org” 或者”com”。

現在存在兩個域名http://aaa.xxx.comhttp://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客戶端(瀏覽器)

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

87.說一下 JSONP 實現原理?

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

九、設計模式

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

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

簡單工廠模式

這個模式本身很簡單而且使用在業務較簡單的情況下。一般用於小項目或者具體產品很少擴展的情況(這樣工廠類才不用經常更改)。

它由三種角色組成:

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

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

抽象工廠模式:

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

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

可以這么說,它和工廠方法模式的區別就在於需要創建對象的復雜程度上。而且抽象工廠模式是三個里面最為抽象、最具一般性的。抽象工廠模式的用意為:給客戶端提供一個接口,可以創建多個產品族中的產品對象。

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

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

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

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

十、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的核心思想就是“將應用程序中的商業邏輯同對其提供支持的通用服務進行分離。2. 解釋一下什么是 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(面向切面編程)、工具、消息和測試模塊。

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 的作用是什么?

首先要知道另一個東西,default-autowire,它是在xml文件中進行配置的,可以設置為byName、byType、constructor和autodetect;比如byName,不用顯式的在bean中寫出依賴的對象,它會自動的匹配其它bean中id名與本bean的set**相同的,並自動裝載。

@Autowired是用在JavaBean中的注解,通過byType形式,用來給指定的字段或方法注入所需的外部資源。

兩者的功能是一樣的,就是能減少或者消除屬性或構造器參數的設置,只是配置地方不一樣而已。

autowire四種模式的區別:

完結撒花.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM