1. 程式人生 > >Java中高階面試總結-更新中

Java中高階面試總結-更新中

陣列中簡單的值型別陣列型別,每個陣列是一個引用,引用到棧上的空間,引用型別,類型別的陣列,每個陣列成員任是一個引用,引用到堆上的空間,因為類的例項分配在堆上。String和StringBuffer,StringBuilder的區別?答:其實討論String到底是可變還是不可變,本質上是值物件中的value[]字元陣列可不可變,而不是物件的引用可不可變。關於StringBuilder和StringBuffer的效率問題,兩者可謂是前世今生,StringBuilder的前世是StringBuffer,自1.5引入,StringBuilder的效率比StringBuffer稍微高,如果不考慮執行緒安全,StringBuilder應該是首選。JVM執行程式主要耗費在物件的建立和回收物件上。
HTTP的三次握手和四次揮手HTTP的三次握手:第一次:客戶端向伺服器傳送SYN報文(SYN一般是用來進行同步的)此時客戶端進入了 SYN_SEND狀態,伺服器進入SYN_RECE等待確認狀態。第二次:伺服器接收到SYN報文後同時向客戶端傳送接收確認報文,並帶上ACK報文,(用於應答的)。第三次:客戶端接收到ACK報文並檢查ACK報文是否正確,如果正確的話客戶端再次傳送ACK,伺服器收到後確認驗證通過,表示連線已經成功建立,可以傳送資料包了。斷開的四次揮手:由於TCP是全雙工的,因此每個方向都必須單獨進行關閉才可,這個原則是當一方完成資料傳送任務後就能傳送一個FIN的標誌來結束這個傳送通道,收到FIN後只意味著這一個方向上沒有資料流動(沒有需要的資料要傳送了),一個TCP可以在收到一個FIN後仍然能傳送資料。
第一次揮手:客戶端向伺服器傳送FIN,用來告訴伺服器你發的東西我收到了,我要關閉通道了。第二次揮手:伺服器從客戶度收到FIN,併發回一個ACK報文(應答用的),確認收到客戶端的訊息。第三次揮手:伺服器關閉與客戶端的連線,傳送一個FIN給客戶端。第四次關閉連線:客戶端收到後發回ACK報文確認。Spring AOP要理解AOP我們可以對照著OOP(面向物件的思想來說) 面向物件是在萬物皆是物件 利用面向物件的特性 繼承 多型 抽象 封裝 來構建物件,是一種自上而下的物件層次結構,但是細粒度到每個物件事務的內部,OOP就顯得有些力不從心,考慮到系統的高聚集 低耦合,例如系統日誌功能 許可權判斷 事務管理(日誌)它幾乎是水平雜湊分佈在每個物件中,卻與核心功能毫無關係。這樣會導致了大量的程式碼重複,不利於各個模組的重用。
AOP思想:將通用的邏輯從程式碼業務邏輯中分離出來。是一種程式設計正規化,也是一種程式設計思想,跟語言無關。基於註解的方式:首先在一個類上@Aspect 代表的是一個切面類,@Pointcut 要切入的點是什麼 一般用在方法上,還有@Advice 表示需要在方法的哪個時機進行切入。例如:@pointcut("within(com.immoc.service.PointcutService)")或者@PointCut("within(com.immoc..*)")掃描這個包下面的所有子包及包下的所有方法。AOP中 萬用字元* 用於匹配一些任意數量的字元+ 用於指定類及其子類.. 用於匹配人意數量的子包或者引數
基本型別==equals
字串變數物件在記憶體中的首地址字串內容
非字串變數物件在字串中的首地址物件在字串中的首地址
基本型別【原生型別】不可用
包裝類地址內容
== 和equals區別?答:注意要點:當“==”運算子的兩個運算元都是包裝器型別的引用,則是比較執行的是否是同於個物件,而如果兩者比較中有一個運算元是表示式(含運算子)則比較的是數值(會自動觸發拆箱的過程),裝箱:即呼叫包裝型別valueOf(),拆箱即XXXValue()。XXX可對應到不同的包裝型別。注意:Integer,Short,Byte,Charater,Long這幾個類的valueOf()實現是類似的,Double,Float的valueOf方法的實現是類似的。Session和Cookie的區別?答:首先我先不比較Session和Cookie的區別,先說一些關於Session的主要知識點,在伺服器的叢集中Session的安全和同步是最大的問題,一般是使用客戶端cookie加密方式,用的較少,不推薦,另一種是Session的複製---參與叢集的節點上的session狀態需要同步到其他所有的節點上,只要是session的狀態一經改變,session資料都要被複制到其餘節點上,如果伺服器叢集數量過大,在session複製的過程中會消耗大量的頻寬,是伺服器的效率明顯降低;另一種方式是session共享,將所有的session資訊一臺伺服器進行統一的管理,session是存放在伺服器端的,cookie是存放在客戶端的,Session作為兩個兩個裝置之間的狀態保持者,至少需要一方需要保持另一方的會話狀態,我們可以把使用者訪問頁面產生的session放到cookie裡面,就是以cookie為中轉站。你訪問web伺服器A,產生了session然後把它放到cookie裡面,當你的請求被分配到B伺服器時,伺服器B先判斷伺服器有沒有這個session,如果沒有,再去看看客戶端的cookie裡面有沒有這個session,如果也沒有,說明session真的不存,如果cookie裡面有,就把cookie裡面的sessoin同步到伺服器B,這樣就可以實現session的同步了。說明:這種方法實現起來簡單,方便,也不會加大資料庫的負擔,但是如果客戶端把cookie禁掉了的話,那麼session就無從同步了,這樣會給網站帶來損失;cookie的安全性不高,雖然它已經加了密,但是還是可以偽造的。由於Http是無狀態的協議,但是多數需要保持應用狀態的程式,需要保證客戶端和服務端的互動狀態一致,對於瀏覽器發起的請求,僅基於http協議,比如:客戶端對伺服器請求同一個URL,請求1次和一萬次伺服器是無法記住使用者身份的,是無法識別出是否為同一個使用者的請求,為了保持這種互動的狀態,就需要採取一系列的措施:如:Cookie,隱藏form表單域,Session,URL,Https。那就先說說Tomcat中session的用法。分散式Session中的處理方式:第一:粘性Session,原理是把一個使用者鎖定在都一臺伺服器上,當用戶第一次請求時,負載均衡器將使用者的請求轉發到了A伺服器上,以後每次使用者請求都會打在A伺服器上,這就是Session的粘性原理,其現方式為在nginx的配置檔案中這樣配置:upstearm mycluster{  ip_hash; ## 粘性Session server 192.168.0.1:8080 weight:1; server 192.168.0.2:9090 weight:1;}第二種是Session的複製:參與叢集的節點上的session狀態需要同步到其他所有的節點上,只要是session的狀態一經改變,session資料都要被複制到其餘節點上,如果伺服器叢集數量過大,在session複製的過程中會消耗大量的頻寬,是伺服器的效率明顯降低;另一種方式是session共享,將所有的session資訊一臺伺服器進行統一的管理,第三種是Sessio共享機制:使用分散式的Redis和Memcache實現,兩者必須是叢集方案。也可細分為兩種Session共享方式:死鎖產生髮生:死鎖檢測演算法:設定一張程序等待表【程序號等待資源號】和一張資源等待表【資源號和程序號】執行緒死鎖是指由於兩個或者多個執行緒互相持有對方所需要的資源,導致這些執行緒處於等待狀態,無法前往執行。當執行緒進入物件的synchronized程式碼塊時,便佔有了資源,直到它退出該程式碼塊或者呼叫wait方法,才釋放資源,在此期間,其他執行緒將不能進入該程式碼塊。當執行緒互相持有對方所需要的資源時,會互相等待對方釋放資源,如果執行緒都不主動釋放所佔有的資源,將產生死鎖。死鎖的產生的一些特定條件:1、互斥條件:程序對於所分配到的資源具有排它性,即一個資源只能被一個程序佔用,直到被該程序釋放 。2、請求和保持條件:一個程序因請求被佔用資源而發生阻塞時,對已獲得的資源保持不放。3、不剝奪條件:任何一個資源在沒被該程序釋放之前,任何其他程序都無法對他剝奪佔用。4、迴圈等待條件:當發生死鎖時,所等待的程序必定會形成一個環路(類似於死迴圈),造成永久阻塞。如何避免:1、加鎖順序:當多個執行緒需要相同的一些鎖,但是按照不同的順序加鎖,死鎖就很容易發生。如果能確保所有的執行緒都是按照相同的順序獲得鎖,那麼死鎖就不會發生。當然這種方式需要你事先知道所有可能會用到的鎖,然而總有些時候是無法預知的。2、加鎖時限:加上一個超時時間,若一個執行緒沒有在給定的時限內成功獲得所有需要的鎖,則會進行回退並釋放所有已經獲得的鎖,然後等待一段隨機的時間再重試。但是如果有非常多的執行緒同一時間去競爭同一批資源,就算有超時和回退機制,還是可能會導致這些執行緒重複地嘗試但卻始終得不到鎖。3、死鎖檢測:死鎖檢測即每當一個執行緒獲得了鎖,會線上程和鎖相關的資料結構中(map、graph等等)將其記下。除此之外,每當有執行緒請求鎖,也需要記錄在這個資料結構中。死鎖檢測是一個更好的死鎖預防機制,它主要是針對那些不可能實現按序加鎖並且鎖超時也不可行的場景。談談你對MysqlInnoDB的認識?InnoDB是mysql一個重要的儲存引擎,跟其他的儲存引擎相比較,其特點是:
  1. 具有較好的事務支援,事務的原子性,支援四個事務隔離級別,多版本讀。
  2. 行級鎖定:通過索引實現,全表掃描仍然會表鎖,注意間隙鎖的影響。
  3. 讀寫阻塞與事務隔離級別相關。
  4. 整個表和主鍵以Cluster方式儲存,組成一顆平衡樹。
  5. 具有能快取索引,也能快取資料的效能
      6. 所有的Secondary Index都會儲存主鍵資訊。使用場景:
  1. 需要事務支援
  2. 行級鎖對高併發有很好的適應能力,但是要確保查詢是通過索引完成的。
  3. 資料更新較為頻繁。
  4. 資料一致性要求較高。
  5. 硬體裝置記憶體較大,可以使用InnoDB較高的快取能力來提高記憶體利用率,儘量減少磁碟的IO。
資料庫事務的隔離級別?
  1. 讀未提交:一個事務可以讀取另一個未提交事務的資料。【read uncommited】。
  2. 讀提交:一個事務只有能到另一個事務提交完之後才可讀取該資料【read commited】。
  3. 重複讀:一個事務在開啟之時,不允許修改操作【Reattable read】。
  4. 序列化:事務序列執行,可避免 髒讀,患讀,不可重複讀,是資料庫事務的最高隔離級別,但是會影響資料庫效能,代價過大【Serializable】。
可重複讀:(MySql的預設隔離級別)表示同一個事務中多次讀取同樣的記錄的結果是一致的。但在理論上,可重複讀還是無法解決幻讀問題,幻讀(當某個事務在讀取某個範圍內的記錄時另一個事務又在該範圍內插入了新的記錄,當之前的事務再次讀取該範圍的記錄時產生幻讀)在可重複讀中,該sql第一次讀取到資料後,就將這些資料加鎖,其它事務無法修改這些資料,就可以實現可重複 讀了。但這種方法卻無法鎖住insert的資料,所以當事務A先前讀取了資料,或者修改了全部資料,事務B還是可以insert資料提交,這時事務A就會 發現莫名其妙多了一條之前沒有的資料,這就是幻讀,不能通過行鎖來避免。需要Serializable隔離級別 ,讀用讀鎖,寫用寫鎖,讀鎖和寫鎖互斥,這麼做可以有效的避免幻讀、不可重複讀、髒讀等問題,但會極大的降低資料庫的併發能力。RPC 言外之意就是遠端呼叫,也就是說兩臺伺服器A和B,一個應用部署在A伺服器上,想要呼叫B伺服器上提供的方法,由於不在同一個記憶體空間內,不能直接呼叫,需要通過網路來表達呼叫的語義和傳達呼叫的資料,在RPC中所有的函式都有自己的一個ID,這個ID在所有的程序中是唯一的,客戶端在做遠端呼叫時必須帶上這個ID,服務端和客戶端都要維持這一個 函式  《---》ID的對應表名稱,兩者的表不一定完全相同,但是對應的方法名成和ID一定是相同的,當客戶端需要呼叫遠端的伺服器上的函式時候,帶上ID發起請求,伺服器接到請求後也會查詢相關表來確定相關的函式,確保呼叫的正確性。

#######################五月份更新##############################################

Linux程序間通訊的幾種方式:
  1.  管道(pipe)以及有名管道:管道可用於有親緣關係程序間通訊,有名管道克服了管道沒有名字的限制,因此具有管道的所有功能之外,它還允許無親緣關係程序間通訊。
  2. 訊號(Signal)。
  3. 報文(Message)佇列(訊息佇列):訊息佇列是訊息的連結表,包括Posix訊息佇列和System V訊息佇列。
  4. 共享記憶體:使得多個程序間可訪問同一塊記憶體空間,是最快的IPC形式,往往是跟訊號量結合使用,來達到程序間的同步以及互斥。
  5. 訊號量(semaphore):主要作為程序間以及同一程序不同執行緒之間的同步手段。
  6. 套接字(Socket):可用於不同機器之間的程序間通訊。
執行緒間的通訊:通訊是指執行緒間通過何種機制來交換資訊,在指令式程式設計中,執行緒之間的通訊有兩種:共享記憶體和訊息傳遞。同步:同步是指程式用於控制不同的執行緒之間操作發生相對順序的機制。java的併發採用的是共享記憶體的模型(在共享記憶體模型裡,執行緒之間通過寫-讀記憶體中的公共狀態來隱式通訊,但是在訊息傳遞模型裡面,執行緒之間沒有公共狀態,執行緒之間必須通過明確的傳送訊息來顯式進行通訊),java執行緒之間的通訊總是隱式進行的。Java中典型的訊息傳遞方式就是wait()和notify().java 記憶體模型的抽象:java中所有的例項域,靜態域和陣列元素儲存在堆記憶體中,堆記憶體線上程之間是共享的。區域性變數,方法引數和異常處理引數不會再執行緒間共享,沒有記憶體可見性問題。java執行緒之間的通訊有java記憶體模型來控制,簡稱JMM,JMM決定一個執行緒對共享變數的寫入適合對另一個執行緒可見,從抽象的角度來看,JMM定義了執行緒和主記憶體之間的抽象關係,執行緒之間的共享變數儲存在主記憶體中(Main memory),每個執行緒都有一個私有的本地記憶體(local memory),本地記憶體中儲存了該執行緒以讀/寫共享變數的副本。JMM是一個抽象的事務,本身並不真實存在,涵蓋了快取,寫緩衝區,暫存器以及其他的硬體和編譯器優化。一個本地變數如果是原始型別,那麼他完全會儲存到棧區。一個本地變數也可能是一個物件的引用,這種情況下,本地引用會儲存在棧中,但是物件本身會儲存在堆區。對於一個物件的成員方法,這些方法中包含的本地變數,仍需要儲存在棧區,即使所屬物件在堆區。對於一個物件的成員變數,不管他是原始型別還是包裝型別,都會儲存在堆區。static變數和類本身資訊都會儲存在堆區。硬體記憶體架構不管是什麼記憶體模型,最終還是要執行在計算機的硬體上的,所以我們很有必要了解一下計算機的硬體模型,現在的計算機中一般都是有多個CPU,同時CPU會有多個核心,java在執行多執行緒的時,這些執行緒會在核心裡面並行執行。在計算機的硬體記憶體模型中,絕大多數的資料會儲存在計算機的主存中,CPU上會有一組暫存器,部分堆和棧中的資料會儲存在CPU的暫存器中,CPU操作暫存器的速度要遠遠的高於操作主存,在CPU暫存器和主存之間還存在一層CPU Cache,CPU 的快取分為一級快取,二級快取,CPU操作快取的速度要稍微慢於暫存器,主存要比暫存器和快取大很多。Java的垃圾回收很多回收器都是通過分代技術來進行實現的,所以我們很有必要了解一下回收器的各個生命週期所做的事情。年輕代:該劃分為三個區域,一個是原始區域(Eden)和Survivor(存活區),存活區根據功能又可劃分為From和To兩個區域,所有的新生物件會分配在Eden原始區,如果經過一次垃圾回收後,仍然存活的物件會儲存在其中一個Survivor區,複製到另一個Survivor 區,當這個Survivor區也滿的時候,從第一個區複製過來的並且仍然存活的物件會複製到年久代。絕大多數的垃圾回收是發生在年輕代。需要注意的是:兩個Survivor並沒有先後關係,一個Survivor可能同時來自Eden區和從前一個Survivor複製過來的物件,而複製到年久區只有從第一個Survivor區中複製過來的物件。在年輕代發生的垃圾回收叫做Minor GC,年久代:如果經過多次垃圾回收週期後仍然存活的物件會存放在年久代,或者剛開始分配的較大記憶體也可直接儲存在年久代。持久代:靜態資訊、儲存類,方法和他們的描述資訊,基本不會發生垃圾回收。什麼時候GC開始工作?答案是:從root開始搜尋,而且經過一次標記、清理後,仍然沒有復活的物件。GC演算法:Serial收集器(複製演算法)     新生代單執行緒收集器,標記清理都是單執行緒,優點是高效。Serial Old收集器(標記-清理演算法)    老年代單執行緒收集器,Serial收集器的老年代版本。ParNew收集器(停止-複製演算法)    新生代收集器,可理解為Serial收集器的多執行緒版本,在多核環境下有著更優良的效能表現。Parallel Scavenge收集器(停止-複製演算法)   並行收集器,追求高吞吐量,高效利用CPU,吞吐量一般能到達99%。Parallel Old收集器(停止-複製演算法)   Paralle Scavenge收集器的老年代版本,並行收集器,吞吐量優先。CMS(Concurrent Mark Sweep)收集器(標記-清理演算法)   高併發,低停頓,追求最短GC回收停頓時間,CPU佔用較高,響應時間快,停頓時間短,多核CPU追求高響應時間的選擇。GC的執行機制:    物件進行了分代處理,因此垃圾回收區域、時間也不一樣,GC有兩種型別的:Scavenge GC和Full GC。Scavenge GC :當物件在申請Eden空間失敗的時候,就會觸發Scavenge GC對Eden區域進行垃圾回收,清理掉未存活的物件,並且把存活的物件移動到Survivor區,然後整理Survivor區域,這種方式是對Eden區域進行清理,是不會影響到年久代的。因為GC回收絕大多數都發生在Eden區域,頻率較高,對回收的演算法需要高效,速度快。Full GC:對整個堆進行垃圾回收,不管是年輕代還是持久代,速度相對要慢很多,因此儘量減少使用Full GC的次數,可能導致Full GC的情況:
  • 年久代被寫滿;
  • 持久代被寫滿
  • System.gc()被顯示呼叫
  • 上一次GC之後Heap的各個域分配策略動態變化