按牛客網討論區筆經面經的發表時間排序。html
1.TCP三次握手?java
(記住1.過程;2.狀態變化;3.幾個常見問題)linux
第一次:客戶端給服務器發送syn包x;SYN_SENTc++
第二次:服務器接收到syn包,返回一個syn包y 和 一個ack包x+1; SYN_RECV面試
第三次:客戶端收到syn+ack包,向服務器發送ack包。ESTABLISHEDredis
爲何三次握手?算法
防止已失效的鏈接請求報文段重傳。spring
四次揮手?數據庫
把三次握手的第二次分解,先發ack包,再發fin包。express
第一次:主動關閉方發送fin包x,關閉數據傳送; FIN_WAIT1 CLOSE_WAIT
第二次:被動方發送ack包x+1; FIN_WAIT2
第三次:被動方發送fin包y,關閉數據傳送; TIME_WAIT LASH_ACK
第四次:主動方發送ack包y+1;
爲何四次握手?
被動方收到FIN包時,並不會當即關閉socket,因此先回復一個ack包。等到被動方全部數據發送完,再發fin包。
爲何TIME_WAIT/等待2MSL?
MSL是報文最大生存時間;主動方發出最後一個ACK包進入TIME_WAIT狀態,目的是防止最後一個ACK包對方沒接收到,那麼對方在超時後將重發第三次握手的FIN包。 A->ACK->B,等待ACK到達對方時間MSL,等待FIN超時重傳MSL,因此若是2MSL時間沒有收到FIN,說明對方安全收到FIN。
2.在瀏覽器訪問一個網址的過程?
1.首先瀏覽器經過DNS解析網址的IP地址,經過IP找到服務器路徑;
2.根據IP地址向服務器發送一個HTTP請求;
3.服務器收到請求,返回響應;
4.瀏覽器對網頁解析,渲染顯示。
涉及各層協議?
應用層:HTTP、DNS、(DNS解析域名爲目的IP,經過IP找到服務器路徑,客戶端向服務器發起HTTP會話)
傳輸層:TCP、 (HTTP會話會被分紅報文段,添加源、目的端口;TCP協議進行主要工做)
網際層:IP、(ARP)、ICMP、(爲數據包選擇路由,IP協議進行主要工做)
鏈路層:PPP、(ARP)(發送IP數據包到達服務器的地址,ARP協議將IP地址轉成MAC地址)
3.Linux文件的權限;
4.排序算法有哪些?時間複雜度是?
(還須要記住最好最壞時間複雜度、穩定性)
O(n^2):
選擇排序:O(n^2)、O(n^2); 不穩定
冒泡排序:O(n)、O(n^2); 穩定
插入排序:O(n)、O(n^2); 穩定
O(nlogn):
快速排序:O(nlogn)、O(n^2);空間:O(logn) 不穩定
歸併排序:O(nlogn)、O(nlogn);空間:O(n) 穩定
堆排序:O(nlogn)、O(nlogn);空間:O(1) 穩定
5.在線編程?
1.==和equals的區別?
(1.基本類型; 2.基本類型封裝;3.String;4.非字符串變量)
equals()是Object類的方法;
(1) 若是是基本類型比較,那麼只能用==來比較,用equals會編譯錯誤,由於不是對象。int a = 3;
(2) 對於基本類型的包裝類型,好比Boolean、Character、Byte、Shot、Integer、Long、Float、Double等的引用變量,==是比較地址的,而equals是比較內容的。Integer n1 = new Integer(30);
(2.5)對於String a = 「a」; Integer b = 1;這種類型的特有對象建立方式,==的時候值是相同的。
(3)對於字符串變量來講,使用「==」和「equals()」方法比較字符串時,其比較方法不一樣。
「==」比較兩個變量自己的值,即兩個對象在內存中的首地址。
「equals()」比較字符串中所包含的內容是否相同。
String s1 = "123";
String s2 = "123";
String s4 = new String("123");
String s5 = new String("123");
s1==s2 true; s1.equals(s2) true;
s4==s5 false; s4.equals(s5) true;
s1==s4 false; s1.equals(s4) true;
s1/s2分別指向字符串常量"123"建立的對象,在常量池裏只有一個對象,內容爲"123";
s4/s5兩個引用對象指向的對象內容相同,可是new操做符建立的,內存中分配兩塊空間給這兩個對象,因此內存地址不一樣。
(4)對於非字符串變量來講,"=="和"equals"方法的做用是相同的都是用來比較其對象在堆內存的首地址,即用來比較兩個引用變量是否指向同一個對象。
2.string是否是基本數據類型,
不是,String是類類型,基本類型有八種:
整型4種:byte/short/int/long 字節數:1/2/4/8
字符型1種:char 2
浮點型2種:float/double 4/8
布爾型1種:boolean 1/8
一個字節等於8位,等於256個數,就是-128到127
大寫的B表示Bytes=字節;小寫的b表示bit=位;1byte=8bit;
自動轉換:(小可轉大,大轉小會失去精度)
byte -> short/char -> int -> long -> float -> double
3.char能不能存放漢字?
能,一個char字符能夠存儲一箇中文漢字。
4.error/exception/runtime exception區別?
Error和Exception都實現了Throwable接口
Error指的是JVM層面的錯誤,好比內存不足OutOfMemoryError
Exception 指的是代碼邏輯的異常,好比下標越界OutOfIndexException
Exception分爲可查異常CheckedException和運行時異常RuntimeException:
可查異常是必須處理的異常,要麼try catch住,要麼往外拋,誰調用,誰處理,好比 FileNotFoundException、IOException、SQLException等。若是不處理,編譯器就不讓你經過。
運行時異常 又叫作非可查異常,在編譯過程當中,不要求必須進行顯示捕捉。
常見的Runtime Excepiton?
NullPointerException 空指針異常
ArithmeticException 算術異常,好比除數爲零
ClassCastException 類型轉換異常
ConcurrentModificationException 同步修改異常,遍歷一個集合的時候,刪除集合的元素,就會拋出該異常
IndexOutOfBoundsException 數組下標越界異常
NegativeArraySizeException 爲數組分配的空間是負數異常
爲何分兩種異常?
Java之因此會設計運行時異常的緣由之一,是由於下標越界,空指針這些運行時異常太過於廣泛,若是都須要進行捕捉,代碼的可讀性就會變得很糟糕。
5.object類的方法?
9種;(簡要介紹各方法)
1.對象的複製/獲取/String/釋放:clone/getClass/toString/finalize;
2.hash:equals/hashCode;
3.多線程:wait/notify/notifyAll
clone:實現對象的淺複製;
getClass:得到運行時類對象;
finalize:用於釋放資源;
equals:比較對象是否相等;基本類型不能夠用equals,對於String類型「equals」和「==」做用不一樣;
hashcode:用於hash尋找;
wait:使當前線程等待該對象的鎖,當前線程必須是該對象的擁有者,也就是具備該對象的鎖;wait()方法一直等待,直到得到鎖或者被中斷。wait(longtimeout)設定一個超時間隔,若是在規定時間內沒有得到鎖就返回。
調用該方法後當前線程進入睡眠狀態,直到如下事件發生:
(1)其餘線程調用了該對象的notify方法。
(2)其餘線程調用了該對象的notifyAll方法。
(3)其餘線程調用了interrupt中斷該線程。
(4)時間間隔到了。
此時該線程就能夠被調度了,若是是被中斷的話就拋出一個InterruptedException異常。
notify:喚醒在該對象上等待的某個線程
6.jvm垃圾回收?
7.linux查看日誌文件的方式?
1.Java都學了些什麼?
答:集合、IO、多線程、框架等等
2.說說多線程吧
答:說了一下多線程的實現,同步,優化
(tips:對於大範圍的內容要整理出目錄,否則會很亂。)
進程和線程的區別 感受有點偏題就不寫了
多線程的實現?
三種方法:1.繼承Thread類;2.實現Runnable接口;3.使用Executor建立線程池;
多線程的同步?
(1)同步方法:synchronized修飾的方法;
(2)同步代碼塊:同步是一種高開銷的操做,所以應該儘可能減小同步的內容。一般沒有必要同步整個方法,使用synchronized代碼塊同步關鍵代碼便可。
同步方法和同步代碼塊的區別是什麼?
答:同步方法默認用this或者當前類class對象做爲鎖; 同步代碼塊能夠選擇以什麼來加鎖,比同步方法要更細顆粒度,咱們能夠選擇只同步會發生同步問題的部分代碼而不是整個方法。
(3)使用volatile實現同步:每次線程要訪問volatile修飾的變量時都是從內存中讀取,而不是從緩存當中讀取,所以每一個線程訪問到的變量值都是同樣的。這樣就保證了同步。
(4)使用重入鎖實現線程同步:ReentrantLock是concurrent包的類;經常使用方法有lock()和unlock();能夠建立公平鎖;支持非阻塞的tryLock(可超時);須要手動釋放鎖。
(5)使用ThreadLocal實現線程同步:每一個線程都建立一個變量副本,修改副本不會影響其餘線程的副本。ThreadLocal並不能替代同步機制,二者面向的問題領域不一樣。同步機制是爲了同步多個線程對相同資源的併發訪問,是爲了多個線程之間進行通訊的有效方式;而ThreadLocal是隔離多個線程的數據共享,從根本上就不在多個線程之間共享資源(變量)。
多線程的優化?
影響多線程性能的問題:死鎖、過多串行化、過多鎖競爭等;
預防和處理死鎖的方法:
1)儘可能不要在釋放鎖以前競爭其餘鎖;通常能夠經過細化同步方法來實現;
2)順序索取鎖資源;
3)嘗試定時鎖tryLock();
下降鎖競爭方法:
1)縮小鎖的範圍,減少鎖的粒度;
2)使用讀寫分離鎖ReadWriteLock來替換獨佔鎖:來實現讀-讀併發,讀-寫串行,寫-寫串行的特性。這種方式更進一步提升了可併發性,由於有些場景大部分是讀操做,所以不必串行工做。
3.說一下線程池,線程池裏面的線程的狀態有哪些?
線程池:
(1.建立;2.參數;)
線程池的頂級接口是Executor,是執行線程的工具;真正的線程池接口是ExecutorService。ThreadPoolExecutor是ExecutorService的默認實現。
ThreadPoolExecutor的參數有:
corePoolSize - 池中所保存的線程數,包括空閒線程。
maximumPoolSize-池中容許的最大線程數。
keepAliveTime - 當線程數大於核心時,此爲終止前多餘的空閒線程等待新任務的最長時間。
unit - keepAliveTime 參數的時間單位。
workQueue - 執行前用於保持任務的隊列。此隊列僅保持由 execute方法提交的 Runnable任務。
threadFactory - 執行程序建立新線程時使用的工廠。
handler - 因爲超出線程範圍和隊列容量而使執行被阻塞時所使用的處理程序。
線程的狀態:(把那張圖將熟悉)
包括New、Runnable、Running、Blocked、Dead狀態;
1)New:Thread t = new Thread();
2)Runnable:t.start()後進入Runnable狀態;位於可運行線程池中,等待被線程調度選中,得到CPU使用權;
3)Running:Runnable狀態的線程得到了cpu時間片;
4)Blocked:三種狀況;
1.等待阻塞:o.wait(),釋放鎖;進入等待隊列;o.notify()/notifyAll() 進入鎖池;
2.同步阻塞:同步鎖被別的線程佔用;進入鎖池;
3.其餘阻塞:1)Thread.sleep();2)t2.join();3)等待用戶輸入(發出I/O請求);不會釋放鎖;當sleep時間結束,t2線程結束,I/O處理完成後進入Runnable狀態;
5)Dead:run()/main()執行結束 或者 異常退出;線程結束生命週期。
4.數據結構學了些什麼?
數組、HashMap、棧、隊列、鏈表、樹;(HashMap也算?)
(把方向引向本身擅長的部分)
5.Hashmap和Hashtable的區別?
相同點:都實現了Map接口;
不一樣點-兩個方面:null值,同步;
HashMap容許鍵和值是null,HashTable不容許;
HashTable是同步的,HashMap不是;
6.Hashmap的數據結構,Hash的具體實現(這塊答得很差)
(講HashMap:1.結構+原理;2.其餘參數-容量、負荷係數、閾值;)
1)HashMap是有數組+鏈表組成,Entry數組是HashMap的主體,鏈表是爲了解決Hash衝突;
2)HashMap的Entry數組的元素能夠看做是一個個散列桶,每一個桶是一個單鏈表;每一個Entry內部類有四個字段:key/value/hash/next;
3)執行put時,根據key的hashcode定位到桶;遍歷單鏈表,利用key.equals()檢查key是否存在;若是存在則覆蓋;不然新建Entry放在頭部;
4)執行get時,根據key的hashcode定位到桶;遍歷單鏈表,利用key.equals()獲取對應的Entry,返回它的value;
5)參數:容量capacity(默認16)、負載係數loadFactor(默認0.75)、閾值threshold=容量*負載係數。數組容量capacity必須是2的n次方,當鍵值對個數>threshold(12)時,擴容:將數組擴容爲原來容量的二倍。
幾點補充:
1)根據hashcode定位桶步驟:
int hash = hash(key.hashCode()); //計算key.hashcode()的hash值,hash函數由hashmap本身實現 int i = indexFor(hash, table.length);//獲取將要存放的數組下標
也就是首先計算key的hashcode(),再對該值map的自定義hash()(將hash值打散,使插入的Entry落在不一樣的桶上,提升查詢效率),再根據獲得的hash值調用indexFor()方法;indexFor(h,length)方法:將hash值與entry數組的長度-1按位與;
/** * "按位與"來獲取數組下標 */ static int indexFor(int h, int length) { return h & (length - 1); }
2)爲何保持Entry數組大小2的n次方?
當length老是2的n次方時,h& (length-1)運算等價於對length取餘,也就是h%length,可是&比%具備更高的效率。
參考:HashMap源碼
7.設計模式有了解嗎?
答:談了一下單例模式、工廠模式、代理模式,順便說了一下Spring的AOP是基於代理模式的,能夠實現日誌記錄等功能。
代理模式
8.數據庫事務你瞭解嗎?髒讀是什麼,幻讀是什麼?
(說一下事務的四個特性+四個衝突+四個隔離級別)
[阿里] [c++]
1.講一下Linux下如何將源文件逐步編譯成目標文件的過程
2.你簡歷上寫熟悉TCP/IP協議,那你說一下TCP的報頭吧。
/*TCP頭定義,共20個字節*/
typedef struct _TCP_HEADER
{
short m_sSourPort; // 源端口號16bit
short m_sDestPort; // 目的端口號16bit
unsigned int m_uiSequNum; // 序列號32bit
unsigned int m_uiAcknowledgeNum; // 確認號32bit
short m_sHeaderLenAndFlag; // 前4位:TCP頭長度;中6位:保留;後6位:標誌位
short m_sWindowSize; // 窗口大小16bit
short m_sCheckSum; // 檢驗和16bit
short m_surgentPointer; // 緊急數據偏移量16bit
}__attribute__((packed))TCP_HEADER, *PTCP_HEADER;
3. 你簡歷上寫的掌握經常使用的數據結構和排序算法,那你說一個你熟悉的排序算法吧,冒泡就不用說了 。(注:原理+複雜度+手寫代碼)
排序主要用這六種:
O(n^2)的有:冒泡排序、插入排序、選擇排序;
O(nlogn)的有:快排、歸併排序、堆排序;
冒泡排序:原理是無序區兩兩比較,每趟獲得一個最大的放在無序區最後;
算法:外循環是趟數[0,n-1), 內循環是無序區個數[0, n-i-1);循環體是比較[j]和[j+1];
插入排序:是把數組分紅有序區和無序區兩部分,每次從無序區取出一位做爲tar值,從後向前遍歷有序區,大於tar則後移,最後放到目標位置;
算法:外循環是無序區長度[1,n);定義a[i]爲tar值;內循環j指向i,比較a[j-1]與tar,大於則日後順移a[j-1];最後a[j]賦值;
選擇排序:也是分爲有序區和無序區,每次從無序區選擇一位最小的放到有序區末尾;
算法:外循環是趟數[0,n-1), 內循環是無序區個數[i+1,n);循環體是比較[i]和[j];
快排:每次排序肯定一個數的位置,比該數小的移到左邊,大的移到右邊。一趟快排的算法是:
上述過程就是partition函數;以partition函數返回的位置,對左右兩邊遞歸。
算法:(如上)partition(a,lo,hi)函數:定義key,while循環(lo<hi),內部(lo<hi && a[hi]>=key);最後a[lo]賦值,返回lo; sort(a,lo,hi)函數:判斷(lo<hi),index,遞歸;
歸併排序:將數組分紅若干個小數組,將已有序的數組兩兩歸併獲得徹底有序數組。每趟歸併的算法是:
算法:merge(a,lo,mid,hi)方法:tmp[]數組,左右指針+臨時指針,比較兩數組小的保存,保存剩餘數組,賦值數組;mergeSort(a,lo,hi) mid, if()判斷:左右遞歸,merge();注意左右遞歸必定是左邊(lo,mid),右邊(mid+1, hi);由於mid偏左,否則會死循環。
堆排序:堆排序包括兩個過程,首先是根據元素建堆,時間複雜度O(n),而後將堆的根節點取出(與最後一個節點交換),將前面N-1個節點進行堆調整。直至全部節點都取出。堆調整時間複雜是O(lgn),調用了n-1次,因此堆排序時間複雜度是O(nlgn);
1.ArrayList和LinkedList的區別
三個方面:1.實現;2.查詢、增刪;3.內存;
2.知道樂觀鎖,悲觀鎖麼?什麼狀況下用樂觀什麼狀況下用悲觀麼?
樂觀鎖:默認讀數據的時候不會修改,因此不會上鎖;
悲觀鎖:默認讀數據的時候會修改,因此會上鎖;
樂觀鎖適用於多讀寫比較少的狀況,省去鎖的開銷,加大系統的吞吐量。
3.volatile關鍵字的做用?i++是原子性的麼?
在當前的Java內存模型下,線程能夠把變量保存在本地內存(好比機器的寄存器)中,而不是直接在主存中進行讀寫。這就可能形成一個線程在主存中修改了一個變量的值,而另一個線程還繼續使用它在寄存器中的變量值的拷貝,形成數據的不一致。
要解決這個問題,只須要像在本程序中的這樣,把該變量聲明爲volatile(不穩定的)便可,這就指示JVM,這個變量是不穩定的,每次使用它都到主存中進行讀取。通常說來,多任務環境下各任務間共享的標誌都應該加volatile修飾。
Volatile修飾的成員變量在每次被線程訪問時,都強迫從共享內存中重讀該成員變量的值。並且,當成員變量發生變化時,強迫線程將變化值回寫到共享內存。這樣在任什麼時候刻,兩個不一樣的線程老是看到某個成員變量的同一個值。
使用volatile關鍵字修飾變量,線程要訪問變量時都是從內存中讀取,而不是從緩存當中讀取,所以每一個線程訪問到的變量值都是同樣的。
i++不是原子操做,分爲三個階段:內存到寄存器、寄存器自增、寫會內存;這三個階段中間均可以被中斷分離開.
4.Java內存模型?爲何設置工做內存和主內存?
PS:JVM內存模型和JMM(Java內存模型)沒有關係。JMM的目的是爲了解決Java多線程對共享數據的讀寫一致性問題。
1.Lambda表達式的形式化表示以下所示
Parameters -> an expression
2.若是Lambda表達式中要執行多個語句塊,須要將多個語句塊以{}進行包裝,若是有返回值,須要顯示指定return語句,以下所示:
Parameters -> {expressions;};
3.若是Lambda表達式不須要參數,可使用一個空括號表示,以下示例所示
() -> {for (int i = 0; i < 1000; i++) doSomething();};
5.若是Lambda表達式只有一個參數,而且參數的類型是能夠由編譯器推斷出來的,則能夠以下所示使用Lambda表達式,便可以省略參數的類型及括號
Stream.of(datas).forEach(param -> {System.out.println(param.length());});
參考:lambda表達式
9.設計模式知道麼?
單例模式、工廠模式、觀察者模式;
單例模式:
1.特色:
2.單例模式有兩種寫法:餓漢式和懶漢式;餓漢式是一旦類加載了,就把單例初始化完成;而懶漢式只有在調用getInstance的時候,才初始化這個單例。
3.懶漢式單例:
a.寫代碼(構造方法,建立實例,get);
b.非線程安全;
c.三種線程安全的代碼+優缺點
syn:每次獲取都須要同步,影響性能;
雙重檢查:判空後鎖住類;爲何要第二次檢查?建立實例的操做非原子化;在getInstance中作了兩次null檢查,確保了只有第一次調用單例的時候纔會作同步,這樣也是線程安全的,同時避免了每次都同步的性能損耗;
靜態內部類:LazyHolder;利用了classloader的機制來保證初始化instance時只有一個線程,因此也是線程安全的,同時沒有性能損耗;
4.餓漢式單例:線程安全;在類建立的同事就實例化一個靜態對象出來。
工廠模式:
見印象筆記/設計模式;工廠方法模式和抽象工廠模式區別;
觀察者模式:
角色:抽象觀察者-update、具體觀察者、抽象被觀察者-attach/detach/notify、具體被觀察者;
使用場景:一個對象狀態更新,其餘對象同步更新,只須要將本身更新通知給其餘對象而不須要知道其餘對象細節。解耦,各自變換互不影響。
10.項目難點?