Java 最常見的面試題之二(最全200多題與詳細答案)

Java 最常見的面試題之二(最全200多題與詳細答案)

博智互聯為大家整理了一份Java面試題,這個面試題是目前已知全網最全的一套應對Java面試的題,出題解題,希望對大家有所幫助。

這份Java面試題,包含內容有十九個模組:

Java 基礎、容器、多執行緒、反射、物件複製、Java Web 模組、異常、網路、設計模式、Spring/Spring MVC、Spring Boot/Spring Cloud、Hibernate、Mybatis、RabbitMQ、Kafka、Zookeeper、MySql、Redis、JVM

閱讀人群

需要面試的初/中/高階 java 程式設計師

想要查漏補缺的人

想要不斷完善和擴充自己 java 技術棧的人

java 面試官

上一篇文章是java基礎、容器、多執行緒、反射模組的提問與答案。本篇繼續物件複製、javaWeb模組、異常、網路來出題與解答。

物件複製

61. 為什麼要使用克隆?

想對一個物件進行處理,又想保留原有的資料進行接下來的操作,就需要克隆了,Java語言中克隆針對的是類的例項。

62. 如何實現物件克隆?

有兩種方式:

實現Cloneable介面並重寫Object類中的clone()方法;

實現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 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(),但是此方法無法複製函式型別)

JavaWeb

64. jsp 和 servlet 有什麼區別?

jsp經編譯後就變成了Servlet。(JSP的本質就是Servlet,JVM只能識別java的類,不能識別JSP的程式碼,Web容器將JSP的程式碼編譯成JVM能夠識別的java類)

jsp更擅長表現於頁面顯示,servlet更擅長於邏輯控制。

Servlet中沒有內建物件,Jsp中的內建物件都是必須透過HttpServletRequest物件,HttpServletResponse物件以及HttpServlet物件得到。

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,其實現途徑有以下幾種:

設定php。ini配置檔案中的“session。use_trans_sid = 1”,或者編譯時開啟打開了“——enable-trans-sid”選項,讓PHP自動跨頁傳遞Session ID。

手動透過URL傳值、隱藏表單傳遞Session ID。

用檔案、資料庫等形式儲存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 注入?

PreparedStatement(簡單又有效的方法)

使用正則表示式過濾傳入的引數

字串過濾

JSP中呼叫該函式檢查是否包含非法字元

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:

/* * 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 cn。richwit;/* * 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虛擬機器正常執行期間丟擲的異常的超類。

網路

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是無連線的,即傳送資料之前不需要建立連線。

CP提供可靠的服務。也就是說,透過TCP連線傳送的資料,無差錯,不丟失,不重複,且按序到達;UDP盡最大努力交付,即不保證可靠交付。

cp透過校驗和,重傳控制,序號標識,滑動視窗、確認應答實現可靠傳輸。如丟包時的重複控制,還可以對次序亂掉的分包進行順序控制。

UDP具有較好的實時性,工作效率比TCP高,適用於對高速傳輸和實時性有較高的通訊或廣播通訊。

每一條TCP連線只能是點到點的;UDP支援一對一,一對多,多對一和多對多的互動通訊。

TCP對系統資源要求較多,UDP對系統資源要求較少。

82. tcp 為什麼要三次握手,兩次不行嗎?為什麼?

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

必經步驟。

如果只是兩次握手, 至多隻有連線發起方的起始序列號能被確認, 另一方選擇的序列號則得不到確認。

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

①。 傳送方產生粘包

採用TCP協議傳輸資料的客戶端與伺服器經常是保持一個長連線的狀態(一次連線發一次資料不存在粘包),雙方在連線不斷開的情況下,可以一直傳輸資料;但當傳送的資料包過於

低小

時,那麼TCP協議預設的會啟用Nagle演算法,將這些較小的資料包進行合併傳送(緩衝區資料傳送是一個堆壓的過程);這個合併過程就是在傳送緩衝區中進行的,也就是說資料傳送出來它已經是粘包的狀態了。

②。 接收方產生粘包

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

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

應用層:網路服務與終端使用者的一個介面。

表示層:資料的表示、安全、壓縮。

會話層:建立、管理、終止會話。

傳輸層:定義傳輸資料的協議埠號,以及流控和差錯校驗。

網路層:進行邏輯地址定址,實現不同網路之間的路徑選擇。

資料鏈路層:建立邏輯連線、進行硬體地址定址、差錯校驗等功能。

物理層:建立、維護、斷開物理連線。

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

LET在瀏覽器回退時是無害的,而POST會再次提交請求。

GET產生的URL地址可以被Bookmark,而POST不可以。

GET請求會被瀏覽器主動cache,而POST不會,除非手動設定。

GET請求只能進行url編碼,而POST支援多種編碼方式。

GET請求引數會被完整保留在瀏覽器歷史記錄裡,而POST中的引數不會被保留。

GET請求在URL中傳送的引數是有長度限制的,而POST沒有。

引數的資料型別,GET只接受ASCII字元,而POST沒有限制。

ET比POST更不安全,因為引數直接暴露在URL上,所以不能用來傳遞敏感資訊。

GET引數透過URL傳遞,POST放在Request body中。

86. 如何實現跨域?

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

圖片ping

常用於跟蹤使用者點選頁面或動態廣告曝光次數。

script標籤

可以得到從其他來源資料,這也是JSONP依賴的根據。

方式二:JSONP跨域

JSONP(JSON with Padding)是資料格式JSON的一種“使用模式”,可以讓網頁從別的網域要資料。根據 XmlHttpRequest 物件受到同源策略的影響,而利用

只能使用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標籤的跨域能力;

indow。name屬性值在文件重新整理後依舊存在的能力(且最大允許2M左右)。

每個iframe都有包裹它的window,而這個window是top window的子視窗。contentWindow屬性返回元素的Window物件。你可以使用這個Window物件來訪問iframe的文件及其內部DOM。

<!—— 下述用埠 10000表示:domainA 10001表示:domainB——><!—— localhost:10000 ——><!—— localhost:10001 ——><!DOCTYPE html>。。。123456789101112131415161718192021222324252627282930313233343536373839

方式五:window.postMessage()

HTML5新特性,可以用來向其他所有的 window 物件傳送訊息。需要注意的是我們必須要保證所有的指令碼執行完才傳送 MessageEvent,如果在函式執行的過程中呼叫了它,就會讓後面的函式超時無法執行。

下述程式碼實現了跨域儲存localStorage

<!—— 下述用埠 10000表示:domainA 10001表示:domainB——><!—— localhost:10000 ——><!—— localhost:10001 ——>1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283

注意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”。

現在存在兩個域名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客戶端(瀏覽器)

87.說一下 JSONP 實現原理?

jsonp 即 json+padding,動態建立script標籤,利用script標籤的src屬性可以獲取任何域下的js指令碼,透過這個特性(也可以說漏洞),伺服器端不再返回json格式,而是返回一段呼叫某個函式的js程式碼,在src中進行了呼叫,這樣實現了跨域。