java面試題整理(1)
JAVA常考點總結1
6、 String、StringBuilder、StringBuffer三者的區別 6
1、輸入網址,瀏覽器的響應過程
1、瀏覽器根據域名通過DNS服務器查詢域名對應的服務器的IP地址
2、瀏覽器主機根據IP地址與服務器建立TCP連接。
3、瀏覽器將訪問請求封裝爲一個HTTP請求報文,通過TCP協議發送給服務器。
4、服務器收到請求並響應,生成一個HTTP響應報文,通過TCP協議發送給瀏覽器主機。
5、瀏覽器得到響應報文之後,對響應報文進行解析以及渲染輸出。
5、瀏覽器異步請求其他資源
2、進程和線程的區別
一、各自包含什麼?
進程是線程的容器,因此簡單地來講,一個進程內部包含一個或多個線程。
線程是進程的一個實體,包含程序計數器(指向當前指令)、寄存器(存儲線程內的局部變量)和堆棧。
二、可獨立運行嗎?
進程是正在運行程序的一個實例,因此進程可以獨立運行。
線程依託於進程,線程不可獨立於進程而運行。
三、擁有系統資源嗎?
進程是系統進行資源分配基本單位,所以進程擁有系統資源。
線程不擁有系統資源,或者說很少。
四、獨立還是共享
各個進程有獨立的地址空間,即進程間互獨立。
處在同一個進程內的所有線程共享此進程內的所有資源,即同一進程內的線程資源共享。
五、創建與切換的開銷比較
從狹義上來講,進程是正在運行的程序的一個實例,啓動一個程序即創建一個進程,開銷是很大的。
線程是一種輕量級的進程,線程比進程小,基本上不擁有系統資源,因此創建線程的開銷比創建進程小得多。
3、如何老道地介紹自己的項目
(1)先從業務需求入手,我爲什麼做這個項目。
(2)項目裏有什麼功能,我負責了哪些模塊。
(3)項目具體用到了什麼技術。
(4)項目最後達到了一個怎樣的效果,下載量、活躍度、盈利等。
4、反轉單鏈表
鏈表結點定義爲:
public class ListNode {
int val;
ListNode next;
ListNode(int val) {
this.val = val;
}
}
這裏我們採用兩種方法,分別是迭代與遞歸。
(1)迭代
從鏈表頭部開始處理,我們需要定義三個連續的結點pPre,當前需要反轉的結點pCur,下一個需要反轉的結點pFuture和一個永遠指向頭結點的pFirst。每次我們只需要將pPre指向pFuture,pCur指向pFirst,調整頭結點pFirst,調整當前需要反轉的結點爲下一個結點即pFuture,最後調整pFuture爲pCur的next結點。
public ListNode reverseList(ListNode head) {
if (head == null) {
return null;
}
//始終指向鏈表的頭結點
ListNode pFirst = head;
//三個結點中的第一個結點
ListNode pPre = pFirst;
//當前需要反轉的結點
ListNode pCur = head.next;
//下一次即將需要反轉的結點
ListNode pFuture = null;
while (pCur != null) {
pFuture = pCur.next;
pPre.next = pFuture;
pCur.next = pFirst;
pFirst = pCur;
pCur = pFuture;
}
return pFirst;
}
(2)遞歸
遞歸與迭代不同,遞歸是從鏈表的尾結點開始,反轉尾結點與前一個結點的指向。
代碼演示:
public ListNode reverseList2(ListNode head) {
if (head == null || head.next == null) {
return head;
}
ListNode pNext = head.next;
head.next = null;
ListNode reverseListNode = reverseList2(pNext);
pNext.next = head;
return reverseListNode;
}
5、OSI七層網絡模型,每層的功能以及有哪些協議
在互聯網中實際使用的是TCP/IP參考模型。實際存在的協議主要包括在:物理層、數據鏈路層、網絡層、傳輸層和應用層。各協議也分別對應這5個層次而已。
要找出7個層次所對應的各協議,恐怕會話層和表示層的協議難找到啊。。
【1】物理層:主要定義物理設備標準,如網線的接口類型、光纖的接口類型、各種傳輸介質的傳輸速率等。它的主要作用是傳輸比特流(就是由1、0轉化爲電流強弱來進行傳輸,到達目的地後在轉化爲1、0,也就是我們常說的數模轉換與模數轉換),這一層的數據叫做比特。
【2】數據鏈路層:定義瞭如何讓格式化數據以進行傳輸,以及如何讓控制對物理介質的訪問,這一層通常還提供錯誤檢測和糾正,以確保數據的可靠傳輸。
【3】網絡層:在位於不同地理位置的網絡中的兩個主機系統之間提供連接和路徑選擇,Internet的發展使得從世界各站點訪問信息的用戶數大大增加,而網絡層正是管理這種連接的層。
【4】傳輸層:定義了一些傳輸數據的協議和端口號(WWW端口80等),如:TCP(傳輸控制協議,傳輸效率低,可靠性強,用於傳輸可靠性要求高,數據量大的數據),UDP(用戶數據報協議,與TCP特性恰恰相反,用於傳輸可靠性要求不高,數據量小的數據,如QQ聊天數據就是通過這種方式傳輸的), 主要是將從下層接收的數據進行分段和傳輸,到達目的地址後再進行重組,常常把這一層數據叫做段。
【5】會話層:通過傳輸層(端口號:傳輸端口與接收端口)建立數據傳輸的通路,主要在你的系統之間發起會話或者接受會話請求(設備之間需要互相認識可以是IP也可以是MAC或者是主機名)。
【6】表示層:可確保一個系統的應用層所發送的信息可以被另一個系統的應用層讀取。例如,PC程序與另一臺計算機進行通信,其中一臺計算機使用擴展二一十進制交換碼(EBCDIC),而另一臺則使用美國信息交換標準碼(ASCII)來表示相同的字符。如有必要,表示層會通過使用一種通格式來實現多種數據格式之間的轉換。
【7】應用層: 是最靠近用戶的OSI層,這一層爲用戶的應用程序(例如電子郵件、文件傳輸和終端仿真)提供網絡服務。
以下列表是一些協議的歸類,如果有錯了或不對的地方,希望各位大神多多提出!其實在應用、表示和會話這三層之間的協議可共用(由於實際的網絡協議將它們歸了一類所致)
應用層
DHCP · DNS · FTP · Gopher · HTTP · IMAP4 · IRC · NNTP · XMPP · POP3 · SIP · SMTP ·
SNMP · SSH · TELNET · RPC · RTCP · RTP ·RTSP · SDP · SOAP · GTP · STUN · NTP · SSDP
表示層
HTTP/HTML · FTP · Telnet · ASN.1(具有表示層功能)
會話層
ADSP·ASP·H.245·ISO-SP·iSNS·NetBIOS·PAP·RPC·
RTCP·SMPP·SCP·SSH·ZIP·SDP(具有會話層功能)
傳輸層
TCP · UDP · TLS · DCCP · SCTP ·RSVP · PPTP
網絡層
IP (IPv4 · IPv6) · ICMP · ICMPv6 · IGMP ·IS-IS · IPsec · BGP · RIP · OSPF ·ARP · RARP
數據鏈路層
Wi-Fi(IEEE 802.11) · WiMAX(IEEE 802.16) ·ATM · DTM · 令牌環 · 以太網路 ·
FDDI · 幀中繼 · GPRS · EVDO · HSPA · HDLC · PPP · L2TP · ISDN ·STP
物理層
以太網路卡 · 調制解調器 · 電力線通信(PLC) · SONET/SDH(光同步數字傳輸網) ·
G.709(光傳輸網絡) · 光導纖維 · 同軸電纜 · 雙絞線
6、String、StringBuilder、StringBuffer三者的區別
三者都是處理字符串常用的類,不同在於:
速度上:String<StringBuffer<StringBuilder;
安全上:StringBuffer線程安全,StringBuilder線程不安全;
原因在於,String的每次改變都會涉及到字符數組的複製,而StringBuffer和StringBuilder直接在字符數組上改變;同時,StringBuffer是線程安全的,而StringBuilder線程不安全,沒有StringBuffer的同步,所以StringBuilder快於StringBuffer。
總結:
如果對字符串的改變少,使用String;
如果對字符串修改的較多,需要線程安全就用StringBuffer,不需要就使用StringBuilder。
7、冒泡排序
冒泡排序的優化版本
public static void bubbleSort(int[] a) {
boolean flag = true;//flag表示這一趟是否存在元素調換
while (flag) {
flag = false;
int temp = 0;
for (int i = 0; i < a.length; i++) {
for (int j = 0; j < a.length - i - 1; j++) {
if (a[j] > a[j + 1]) {
temp = a[j];
a[j] = a[j + 1];
a[j + 1] = temp;
flag = true;
}
}
//flag爲false表示本趟不存在元素調換
if (!flag) {
break;
}
}
}
}
8、二分查找
//非遞歸
public static int binarySearch1(int[] a, int key, int low, int high) {
while (low <= high) {
int mid = (low + high) / 2;
if (key == a[mid]) {
return mid;
} else if (key > a[mid]) {
low = mid + 1;
} else {
high = mid - 1;
}
}
return -1;
}
//遞歸
public static int binarySearch2(int[] a, int key, int low, int high) {
if (low <= high) {
int mid = (low + high) / 2;
if (key == a[mid]) {
return mid;
} else if (key > a[mid]) {
return binarySearch2(a, key, mid + 1, high);
} else {
return binarySearch2(a, key, low, mid - 1);
}
}
return -1;
}
9、sleep與wait的區別
【1】原理不同
(1)sleep用於線程控制自身的流程,使自己暫停指定的時間,把執行機會讓給其他線程,時間到,則自動甦醒。
(2)wait爲Object類的方法(Object類中的其他方法見Object類的方法簡談),用於線程之間的通信,會使擁有當前對象鎖的線程等待,直到其他線程調用notify或notifyAll方法才醒來。當然也可以指定時間,時間到,則自動醒來。
【2】對鎖的處理機制不同
(1)sleep不涉及線程之間的通信,調用sleep方法不會釋放鎖
(2)wait方法,線程會釋放掉它所佔用的鎖,從而使得該線程所在的對象中的synchronized數據被其他線程使用。
【3】使用區域不同
(1)sleep方法可以放在任何地方使用,不用加任何限制
(2)wait方法必須放在同步控制方法或者同步語句塊中使用
【4】異常捕獲
(1)sleep方法必須捕獲異常,因爲在某個線程的sleep過程中,有可能被其他對象調用它的interrupt方法,從而產生InterruptedException異常,因此需要捕獲。
(2)wait、notify和notifyAll方法都不需要捕獲異常
由於sleep方法不會釋放對象鎖,容易導致死鎖問題的產生。因此,在一般的情況下,不推薦使用sleep方法,而推薦使用wait方法。
10、數據庫事務ACID與隔離級別
如果一個數據庫聲稱支持事務的操作,那麼該數據庫必須要具備以下四個特性:
原子性(Atomicity)
原子性是指事務包含的所有操作要麼全部成功,要麼全部失敗回滾,這和前面兩篇博客介紹事務的功能是一樣的概念,因此事務的操作如果成功就必須要完全應用到數據庫,如果操作失敗則不能對數據庫有任何影響。
一致性(Consistency)
一致性是指事務必須使數據庫從一個一致性狀態變換到另一個一致性狀態,也就是說一個事務執行之前和執行之後都必須處於一致性狀態。
拿轉賬來說,假設用戶A和用戶B兩者的錢加起來一共是5000,那麼不管A和B之間如何轉賬,轉幾次賬,事務結束後兩個用戶的錢相加起來應該還得是5000,這就是事務的一致性。
隔離性(Isolation)
隔離性是當多個用戶併發訪問數據庫時,比如操作同一張表時,數據庫爲每一個用戶開啓的事務,不能被其他事務的操作所幹擾,多個併發事務之間要相互隔離。
即要達到這麼一種效果:對於任意兩個併發的事務T1和T2,在事務T1看來,T2要麼在T1開始之前就已經結束,要麼在T1結束之後纔開始,這樣每個事務都感覺不到有其他事務在併發地執行。
關於事務的隔離性數據庫提供了多種隔離級別,稍後會介紹到。
持久性(Durability)
持久性是指一個事務一旦被提交了,那麼對數據庫中的數據的改變就是永久性的,即便是在數據庫系統遇到故障的情況下也不會丟失提交事務的操作。
例如我們在使用JDBC操作數據庫時,在提交事務方法後,提示用戶事務操作完成,當我們程序執行完成直到看到提示後,就可以認定事務以及正確提交,即使這時候數據庫出現了問題,也必須要將我們的事務完全執行完成,否則就會造成我們看到提示事務處理完畢,但是數據庫因爲故障而沒有執行事務的重大錯誤。
以上介紹完事務的四大特性(簡稱ACID),現在重點來說明下事務的隔離性,當多個線程都開啓事務操作數據庫中的數據時,數據庫系統要能進行隔離操作,以保證各個線程獲取數據的準確性,在介紹數據庫提供的各種隔離級別之前,我們先看看如果不考慮事務的隔離性,會發生的幾種問題:
髒讀
髒讀是指在一個事務處理過程裏讀取了另一個未提交的事務中的數據。
當一個事務正在多次修改某個數據,而在這個事務中這多次的修改都還未提交,這時一個併發的事務來訪問該數據,就會造成兩個事務得到的數據不一致。例如:用戶A向用戶B轉賬100元,對應SQL命令如下
update account set money=money+100 where name=’B’; (此時A通知B)
update account set money=money - 100 where name=’A’;
當只執行第一條SQL時,A通知B查看賬戶,B發現確實錢已到賬(此時即發生了髒讀),而之後無論第二條SQL是否執行,只要該事務不提交,則所有操作都將回滾,那麼當B以後再次查看賬戶時就會發現錢其實並沒有轉。
不可重複讀
不可重複讀是指在對於數據庫中的某個數據,一個事務範圍內多次查詢卻返回了不同的數據值,這是由於在查詢間隔,被另一個事務修改並提交了。
例如事務T1在讀取某一數據,而事務T2立馬修改了這個數據並且提交事務給數據庫,事務T1再次讀取該數據就得到了不同的結果,發送了不可重複讀。
不可重複讀和髒讀的區別是,髒讀是某一事務讀取了另一個事務未提交的髒數據,而不可重複讀則是讀取了前一事務提交的數據。
在某些情況下,不可重複讀並不是問題,比如我們多次查詢某個數據當然以最後查詢得到的結果爲主。但在另一些情況下就有可能發生問題,例如對於同一個數據A和B依次查詢就可能不同,A和B就可能打起來了……
虛讀(幻讀)
幻讀是事務非獨立執行時發生的一種現象。例如事務T1對一個表中所有的行的某個數據項做了從“1”修改爲“2”的操作,這時事務T2又對這個表中插入了一行數據項,而這個數據項的數值還是爲“1”並且提交給數據庫。而操作事務T1的用戶如果再查看剛剛修改的數據,會發現還有一行沒有修改,其實這行是從事務T2中添加的,就好像產生幻覺一樣,這就是發生了幻讀。
幻讀和不可重複讀都是讀取了另一條已經提交的事務(這點就髒讀不同),所不同的是不可重複讀查詢的都是同一個數據項,而幻讀針對的是一批數據整體(比如數據的個數)。
現在來看看MySQL數據庫爲我們提供的四種隔離級別:
① Serializable (串行化):可避免髒讀、不可重複讀、幻讀的發生。
② Repeatable read (可重複讀):可避免髒讀、不可重複讀的發生。
③ Read committed (讀已提交):可避免髒讀的發生。
④ Read uncommitted (讀未提交):最低級別,任何情況都無法保證。
以上四種隔離級別最高的是Serializable級別,最低的是Read uncommitted級別,當然級別越高,執行效率就越低。像Serializable這樣的級別,就是以鎖表的方式(類似於Java多線程中的鎖)使得其他的線程只能在鎖外等待,所以平時選用何種隔離級別應該根據實際情況。在MySQL數據庫中默認的隔離級別爲Repeatable read (可重複讀)。
在MySQL數據庫中,支持上面四種隔離級別,默認的爲Repeatable read (可重複讀);而在Oracle數據庫中,只支持Serializable (串行化)級別和Read committed (讀已提交)這兩種級別,其中默認的爲Read committed級別。
在MySQL數據庫中查看當前事務的隔離級別:
select @@tx_isolation;
在MySQL數據庫中設置事務的隔離 級別:
set [glogal | session] transaction isolation level 隔離級別名稱;
set tx_isolation=’隔離級別名稱;’
例1:查看當前事務的隔離級別:
例2:將事務的隔離級別設置爲Read uncommitted級別:
或:
記住:設置數據庫的隔離級別一定要是在開啓事務之前!
如果是使用JDBC對數據庫的事務設置隔離級別的話,也應該是在調用Connection對象的setAutoCommit(false)方法之前。調用Connection對象的setTransactionIsolation(level)即可設置當前鏈接的隔離級別,至於參數level,可以使用Connection對象的字段:
在JDBC中設置隔離級別的部分代碼:
後記:隔離級別的設置只對當前鏈接有效。對於使用MySQL命令窗口而言,一個窗口就相當於一個鏈接,當前窗口設置的隔離級別只對當前窗口中的事務有效;對於JDBC操作數據庫來說,一個Connection對象相當於一個鏈接,而對於Connection對象設置的隔離級別只對該Connection對象有效,與其他鏈接Connection對象無關。
11、http與https的區別
超文本傳輸協議HTTP協議被用於在Web瀏覽器和網站服務器之間傳遞信息,HTTP協議以明文方式發送內容,不提供任何方式的數據加密,如果攻擊者截取了Web瀏覽器和網站服務器之間的傳輸報文,就可以直接讀懂其中的信息,因此,HTTP協議不適合傳輸一些敏感信息,比如:信用卡號、密碼等支付信息。
爲了解決HTTP協議的這一缺陷,需要使用另一種協議:安全套接字層超文本傳輸協議HTTPS,爲了數據傳輸的安全,HTTPS在HTTP的基礎上加入了SSL協議,SSL依靠證書來驗證服務器的身份,併爲瀏覽器和服務器之間的通信加密。
一、HTTP和HTTPS的基本概念
HTTP:是互聯網上應用最爲廣泛的一種網絡協議,是一個客戶端和服務器端請求和應答的標準(TCP),用於從WWW服務器傳輸超文本到本地瀏覽器的傳輸協議,它可以使瀏覽器更加高效,使網絡傳輸減少。
HTTPS:是以安全爲目標的HTTP通道,簡單講是HTTP的安全版,即HTTP下加入SSL層,HTTPS的安全基礎是SSL,因此加密的詳細內容就需要SSL。
HTTPS協議的主要作用可以分爲兩種:一種是建立一個信息安全通道,來保證數據傳輸的安全;另一種就是確認網站的真實性。
二、HTTP與HTTPS有什麼區別?
HTTP協議傳輸的數據都是未加密的,也就是明文的,因此使用HTTP協議傳輸隱私信息非常不安全,爲了保證這些隱私數據能加密傳輸,於是網景公司設計了SSL(Secure Sockets Layer)協議用於對HTTP協議傳輸的數據進行加密,從而就誕生了HTTPS。
簡單來說,HTTPS協議是由SSL+HTTP協議構建的可進行加密傳輸、身份認證的網絡協議,要比http協議安全。
HTTPS和HTTP的區別主要如下:
1、https協議需要到ca申請證書,一般免費證書較少,因而需要一定費用。
2、http是超文本傳輸協議,信息是明文傳輸,https則是具有安全性的ssl加密傳輸協議。
3、http和https使用的是完全不同的連接方式,用的端口也不一樣,前者是80,後者是443。
4、http的連接很簡單,是無狀態的;HTTPS協議是由SSL+HTTP協議構建的可進行加密傳輸、身份認證的網絡協議,比http協議安全。
三、HTTPS的工作原理
我們都知道HTTPS能夠加密信息,以免敏感信息被第三方獲取,所以很多銀行網站或電子郵箱等等安全級別較高的服務都會採用HTTPS協議。
1、客戶端發起HTTPS請求
這個沒什麼好說的,就是用戶在瀏覽器裏輸入一個https網址,然後連接到server的443端口。
2、服務端的配置
採用HTTPS協議的服務器必須要有一套數字證書,可以自己製作,也可以向組織申請,區別就是自己頒發的證書需要客戶端驗證通過,纔可以繼續訪問,而使用受信任的公司申請的證書則不會彈出提示頁面(startssl就是個不錯的選擇,有1年的免費服務)。
這套證書其實就是一對公鑰和私鑰,如果對公鑰和私鑰不太理解,可以想象成一把鑰匙和一個鎖頭,只是全世界只有你一個人有這把鑰匙,你可以把鎖頭給別人,別人可以用這個鎖把重要的東西鎖起來,然後發給你,因爲只有你一個人有這把鑰匙,所以只有你才能看到被這把鎖鎖起來的東西。
3、傳送證書
這個證書其實就是公鑰,只是包含了很多信息,如證書的頒發機構,過期時間等等。
4、客戶端解析證書
這部分工作是有客戶端的TLS來完成的,首先會驗證公鑰是否有效,比如頒發機構,過期時間等等,如果發現異常,則會彈出一個警告框,提示證書存在問題。
如果證書沒有問題,那麼就生成一個隨機值,然後用證書對該隨機值進行加密,就好像上面說的,把隨機值用鎖頭鎖起來,這樣除非有鑰匙,不然看不到被鎖住的內容。
5、傳送加密信息
這部分傳送的是用證書加密後的隨機值,目的就是讓服務端得到這個隨機值,以後客戶端和服務端的通信就可以通過這個隨機值來進行加密解密了。
6、服務段解密信息
服務端用私鑰解密後,得到了客戶端傳過來的隨機值(私鑰),然後把內容通過該值進行對稱加密,所謂對稱加密就是,將信息和私鑰通過某種算法混合在一起,這樣除非知道私鑰,不然無法獲取內容,而正好客戶端和服務端都知道這個私鑰,所以只要加密算法夠彪悍,私鑰夠複雜,數據就夠安全。
7、傳輸加密後的信息
這部分信息是服務段用私鑰加密後的信息,可以在客戶端被還原。
8、客戶端解密信息
客戶端用之前生成的私鑰解密服務段傳過來的信息,於是獲取瞭解密後的內容,整個過程第三方即使監聽到了數據,也束手無策。
六、HTTPS的優點
正是由於HTTPS非常的安全,攻擊者無法從中找到下手的地方,從站長的角度來說,HTTPS的優點有以下2點:
1、SEO方面
谷歌曾在2014年8月份調整搜索引擎算法,並稱“比起同等HTTP網站,採用HTTPS加密的網站在搜索結果中的排名將會更高”。
2、安全性
儘管HTTPS並非絕對安全,掌握根證書的機構、掌握加密算法的組織同樣可以進行中間人形式的攻擊,但HTTPS仍是現行架構下最安全的解決方案,主要有以下幾個好處:
(1)、使用HTTPS協議可認證用戶和服務器,確保數據發送到正確的客戶機和服務器;
(2)、HTTPS協議是由SSL+HTTP協議構建的可進行加密傳輸、身份認證的網絡協議,要比http協議安全,可防止數據在傳輸過程中不被竊取、改變,確保數據的完整性。
(3)、HTTPS是現行架構下最安全的解決方案,雖然不是絕對安全,但它大幅增加了中間人攻擊的成本。
七、HTTPS的缺點
雖然說HTTPS有很大的優勢,但其相對來說,還是有些不足之處的,具體來說,有以下2點:
1、SEO方面
據ACM CoNEXT數據顯示,使用HTTPS協議會使頁面的加載時間延長近50%,增加10%到20%的耗電,此外,HTTPS協議還會影響緩存,增加數據開銷和功耗,甚至已有安全措施也會受到影響也會因此而受到影響。
而且HTTPS協議的加密範圍也比較有限,在黑客攻擊、拒絕服務攻擊、服務器劫持等方面幾乎起不到什麼作用。
最關鍵的,SSL證書的信用鏈體系並不安全,特別是在某些國家可以控制CA根證書的情況下,中間人攻擊一樣可行。
2、經濟方面
(1)、SSL證書需要錢,功能越強大的證書費用越高,個人網站、小網站沒有必要一般不會用。
(2)、SSL證書通常需要綁定IP,不能在同一IP上綁定多個域名,IPv4資源不可能支撐這個消耗(SSL有擴展可以部分解決這個問題,但是比較麻煩,而且要求瀏覽器、操作系統支持,Windows XP就不支持這個擴展,考慮到XP的裝機量,這個特性幾乎沒用)。
(3)、HTTPS連接緩存不如HTTP高效,大流量網站如非必要也不會採用,流量成本太高。
(4)、HTTPS連接服務器端資源佔用高很多,支持訪客稍多的網站需要投入更大的成本,如果全部採用HTTPS,基於大部分計算資源閒置的假設的VPS的平均成本會上去。
(5)、HTTPS協議握手階段比較費時,對網站的相應速度有負面影響,如非必要,沒有理由犧牲用戶體驗。
12、線程安全的單例模式
(1)線程安全且性能較好的懶漢式(DCL)
public class SingletonDCL {
private static SingletonDCL instance;
private SingletonDCL() {
}
public static SingletonDCL getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new SingletonDCL();
}
}
}
return instance;
}
}
(2)靜態內部類
public class Singleton {
private Singleton() {
}
private static class SingletonHolder {
private static Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.instance;
}
}
這樣做得好處是
(1)靜態內部類不會在類加載時就創建此類的實例,只有在顯示調用此靜態內部類時,此靜態內部類纔會被加載,內部的對象實例纔會被賦值,起到懶加載的作用。
(3)和餓漢式一樣,使用類加載機制,避免多線程下的同步問題。
13、get與post的區別
(1)get請求將數據附在URL之後(以?分割URL與具體數據,參數之間用&隔開,數據放在HTTP協議頭,),而post請求將數據放在請求體中。
(2)get的URL會有長度上的限制,而post理論上不會有長度的限制,還是得看具體瀏覽器和服務器的配置。
(3)post比get稍微安全一點,體現在我們不可以直接看到請求的數據。或者說,get請求一般是用來獲取數據,而post用來更新或修改服務器上的數據,造成的影響不同。
(4)get請求會被瀏覽器緩存,而post請求不會被緩存。
(5)GET和POST還有一個重大區別,簡單的說:GET產生一個TCP數據包;POST產生兩個TCP數據包。對於GET方式的請求,瀏覽器會把http header和data一併發送出去,服務器響應200(返回數據); 而對於POST,瀏覽器先發送header,服務器響應100 continue,瀏覽器再發送data,服務器響應200 ok(返回數據)。
(6)GET方法是安全、冪等、可緩存的(除非有 Cache-ControlHeader的約束),GET方法的報文主體沒有任何語義。POST的語義是根據請求負荷(報文主體)對指定的資源做出處理,具體的處理方式視資源類型而不同。POST不安全,不冪等,(大部分實現)不可緩存。(冪等的概念是指同一個請求方法執行多次和僅執行一次的效果完全相同。)
14、按層遍歷二叉樹
public void layerOrder(TreeNode root) {
LinkedList<TreeNode> list = new LinkedList<>();
TreeNode t;
if (root != null) {
list.push(root);
}
while (!list.isEmpty()) {
t = list.removeFirst();
System.out.print(t.getValue());
if (t.getLeft() != null) {
list.addLast(t.getLeft());
}
if (t.getRight() != null) {
list.addLast(t.getRight());
}
}
}
15、索引優化策略
(1)建立聯合索引時,需滿足最左前綴匹配原則
(2)主鍵外鍵一定要建索引
(3)對 where,on,group by,order by 中出現的列使用索引
(4)儘量選擇區分度高的列作爲索引,區分度的公式是count(distinct col)/count(*),表示字段不重複的比例,比例越大我們掃描的記錄數越少,唯一鍵的區分度是1,而一些狀態、性別字段可能在大數據面前區分度就是0
(5)對較小的數據列使用索引,這樣會使索引文件更小,同時內存中也可以裝載更多的索引鍵
(6)索引列不能參與計算,保持列“乾淨”,比如from_unixtime(create_time) = ’2014-05-29’就不能使用到索引,原因很簡單,b+樹中存的都是數據表中的字段值,但進行檢索時,需要把所有元素都應用函數才能比較,顯然成本太大。所以語句應該寫成create_time = unix_timestamp(’2014-05-29’);
(7)爲較長的字符串使用前綴索引
(8)儘量的擴展索引,不要新建索引。比如表中已經有a的索引,現在要加(a,b)的索引,那麼只需要修改原來的索引即可
(9)不要過多創建索引, 權衡索引個數與DML之間關係,DML也就是插入、刪除數據操作。這裏需要權衡一個問題,建立索引的目的是爲了提高查詢效率的,但建立的索引過多,會影響插入、刪除數據的速度,因爲我們修改的表數據,索引也需要進行調整重建
(10)對於like查詢,”%”不要放在前面。
SELECT * FROM houdunwang WHERE unameLIKE '後盾%' -- 走索引
SELECT * FROM houdunwang WHERE unameLIKE "%後盾%" -- 不走索引
(11)查詢where條件數據類型不匹配也無法使用索引
(12)字符串與數字比較不使用索引;
CREATE TABLEa( a char(10) );
EXPLAIN SELECT * FROM a WHERE a= "1" – 走索引
EXPLAIN SELECT * FROM a WHERE a= 1 – 不走索引
(13)正則表達式不使用索引,這應該很好理解,所以爲什麼在SQL中很難看到regexp關鍵字的原因
語句一:select name from 商品表。不會用到索引,因爲沒有where語句。
語句二:select * from 商品表 where name = ‘Java書’,會用到索引,如果項目裏經常用到name來查詢,且商品表的數據量很大,而name值的重複率又不高,那麼建議建索引。
語句三:select * from 商品表 where name like ‘Java%’ 這是個模糊查詢,會用到索引,請大家記住,用like進行模糊查詢時,如果第一個就是模糊的匹配符,比如where name like ‘%java’,那麼在查詢時不會走索引。在其他情況下,不論用了多少個%,也不論%的位置,只要不出現在第一個位置,那麼都能用到索引。
學生成績表裏有兩個字段:姓名和成績。現在對成績這個整數類型的字段建索引。
第一種情況,當數字型字段遇到非等值操作符時,無法用到索引。比如:
select name from 學生成績表 where 成績>95 , 一旦出現大於符號,就不能用到索引,爲了用到索引,我們應該改一下SQL語句裏的where從句:where 成績 in (96,97,98,99,100)
第二種情況,如果對索引字段進行了某種左值操作,那麼無法用到索引。
能用到索引的寫法:select name from 學生成績表 where 成績 = 60
不能用到索引的寫法:select name from 學生成績表 where 成績+40 = 100
第三種情況,如果對索引字段進行了函數操作,那麼無法用到索引。
比如SQL語句:select * from 商品表 where substr(name) = ‘J’,我們希望查詢商品名首字母是J的記錄,可一旦針對name使用函數,即使name字段上有索引,也無法用到。
16、三次握手與四次揮手是什麼?
三次握手過程圖
最開始的時候客戶端和服務器都是處於CLOSED狀態。主動打開連接的爲客戶端,被動打開連接的是服務器。
(1)TCP服務器進程先創建傳輸控制塊TCB,時刻準備接受客戶進程的連接請求,此時服務器就進入了LISTEN(監聽)狀態;
(2)TCP客戶進程也是先創建傳輸控制塊TCB,然後向服務器發出連接請求報文,這是報文首部中的同部位SYN=1,同時選擇一個初始序列號 seq=x ,此時,TCP客戶端進程進入了 SYN-SENT(同步已發送狀態)狀態。TCP規定,SYN報文段(SYN=1的報文段)不能攜帶數據,但需要消耗掉一個序號。
(3)TCP服務器收到請求報文後,如果同意連接,則發出確認報文。確認報文中應該 ACK=1,SYN=1,確認號是ack=x+1,同時也要爲自己初始化一個序列號 seq=y,此時,TCP服務器進程進入了SYN-RCVD(同步收到)狀態。這個報文也不能攜帶數據,但是同樣要消耗一個序號。
(4)TCP客戶進程收到確認後,還要向服務器給出確認。確認報文的ACK=1,ack=y+1,自己的序列號seq=x+1,此時,TCP連接建立,客戶端進入ESTABLISHED(已建立連接)狀態。TCP規定,ACK報文段可以攜帶數據,但是如果不攜帶數據則不消耗序號。
(5)當服務器收到客戶端的確認後也進入ESTABLISHED狀態,此後雙方就可以開始通信了。
【1】問題一:爲什麼TCP客戶端最後還要發送一次確認呢?
一句話,主要防止已經失效的連接請求報文突然又傳送到了服務器,從而產生錯誤。
如果使用的是兩次握手建立連接,假設有這樣一種場景,客戶端發送了第一個請求連接並且沒有丟失,只是因爲在網絡結點中滯留的時間太長了,由於TCP的客戶端遲遲沒有收到確認報文,以爲服務器沒有收到,此時重新向服務器發送這條報文,此後客戶端和服務器經過兩次握手完成連接,傳輸數據,然後關閉連接。此時此前滯留的那一次請求連接,網絡通暢了到達了服務器,這個報文本該是失效的,但是,兩次握手的機制將會讓客戶端和服務器再次建立連接,這將導致不必要的錯誤和資源的浪費。
如果採用的是三次握手,就算是那一次失效的報文傳送過來了,服務端接受到了那條失效報文並且回覆了確認報文,但是客戶端不會再次發出確認。由於服務器收不到確認,就知道客戶端並沒有請求連接。
四次揮手過程圖
數據傳輸完畢後,雙方都可釋放連接。最開始的時候,客戶端和服務器都是處於ESTABLISHED狀態,然後客戶端主動關閉,服務器被動關閉。
(1)客戶端進程發出連接釋放報文,並且停止發送數據。釋放數據報文首部,FIN=1,其序列號爲seq=u(等於前面已經傳送過來的數據的最後一個字節的序號加1),此時,客戶端進入FIN-WAIT-1(終止等待1)狀態。 TCP規定,FIN報文段即使不攜帶數據,也要消耗一個序號。
(2)服務器收到連接釋放報文,發出確認報文,ACK=1,ack=u+1,並且帶上自己的序列號seq=v,此時,服務端就進入了CLOSE-WAIT(關閉等待)狀態。TCP服務器通知高層的應用進程,客戶端向服務器的方向就釋放了,這時候處於半關閉狀態,即客戶端已經沒有數據要發送了,但是服務器若發送數據,客戶端依然要接受。這個狀態還要持續一段時間,也就是整個CLOSE-WAIT狀態持續的時間。
(3)客戶端收到服務器的確認請求後,此時,客戶端就進入FIN-WAIT-2(終止等待2)狀態,等待服務器發送連接釋放報文(在這之前還需要接受服務器發送的最後的數據)。
(4)服務器將最後的數據發送完畢後,就向客戶端發送連接釋放報文,FIN=1,ack=u+1,由於在半關閉狀態,服務器很可能又發送了一些數據,假定此時的序列號爲seq=w,此時,服務器就進入了LAST-ACK(最後確認)狀態,等待客戶端的確認。
(5)客戶端收到服務器的連接釋放報文後,必須發出確認,ACK=1,ack=w+1,而自己的序列號是seq=u+1,此時,客戶端就進入了TIME-WAIT(時間等待)狀態。注意此時TCP連接還沒有釋放,必須經過2∗∗MSL(最長報文段壽命)的時間後,當客戶端撤銷相應的TCB後,才進入CLOSED狀態。
(6)服務器只要收到了客戶端發出的確認,立即進入CLOSED狀態。同樣,撤銷TCB後,就結束了這次的TCP連接。可以看到,服務器結束TCP連接的時間要比客戶端早一些。
【2】問題二:爲什麼客戶端最後還要等待2MSL?
MSL(Maximum Segment Lifetime),TCP允許不同的實現可以設置不同的MSL值。
第一,保證客戶端發送的最後一個ACK報文能夠到達服務器,因爲這個ACK報文可能丟失,站在服務器的角度看來,我已經發送了FIN+ACK報文請求斷開了,客戶端還沒有給我回應,應該是我發送的請求斷開報文它沒有收到,於是服務器又會重新發送一次,而客戶端就能在這個2MSL時間段內收到這個重傳的報文,接着給出迴應報文,並且會重啓2MSL計時器。
第二,防止類似與“三次握手”中提到了的“已經失效的連接請求報文段”出現在本連接中。客戶端發送完最後一個確認報文後,在這個2MSL時間中,就可以使本連接持續的時間內所產生的所有報文段都從網絡中消失。這樣新的連接中不會出現舊連接的請求報文。
【3】問題三:爲什麼建立連接是三次握手,關閉連接確是四次揮手呢?
建立連接的時候, 服務器在LISTEN狀態下,收到建立連接請求的SYN報文後,把ACK和SYN放在一個報文裏發送給客戶端。
而關閉連接時,服務器收到對方的FIN報文時,僅僅表示對方不再發送數據了但是還能接收數據,而自己也未必全部數據都發送給對方了,所以己方可以立即關閉,也可以發送一些數據給對方後,再發送FIN報文給對方來表示同意現在關閉連接,因此,己方ACK和FIN一般都會分開發送,從而導致多了一次。
【4】問題四:如果已經建立了連接,但是客戶端突然出現故障了怎麼辦?
TCP還設有一個保活計時器,顯然,客戶端如果出現故障,服務器不能一直等下去,白白浪費資源。服務器每收到一次客戶端的請求後都會重新復位這個計時器,時間通常是設置爲2小時,若兩小時還沒有收到客戶端的任何數據,服務器就會發送一個探測報文段,以後每隔75分鐘發送一次。若一連發送10個探測報文仍然沒反應,服務器就認爲客戶端出了故障,接着就關閉連接。