本文系掘金首發,禁止轉載哦! 若是以爲文章內容不錯的話,歡迎爲我轉身,啊!不對,是給我一個贊!點贊以後會有驚喜哦!html
看本文以前,推薦給你們一個阿里雲雙11活動,真的很是很是很是推薦,對於新人阿里雲真的是下血本了,建議阿里雲新人必定必定必定不要錯過。若是以爲這單純是廣告的話,你能夠直接跳過看正文。前端
阿里雲雙11最新活動(僅限阿里雲新用戶購買,老用戶拉新用戶能夠得到返現紅包,後續有機會平分百萬紅包),優惠力度很是很是很是大,另外加入拼團,後續還有機會平分100w紅包!目前個人戰隊已經有12位新人了,如今是折上5折了也就是1折購買!!!。 劃重點了: 1核2G雲服務器1年僅需99.5元!!!1核2G雲服務器3年僅需298.50元!!!一個月僅需8.2元 該折扣僅限新人!這是個人拼團團隊地址:m.aliyun.com/act/team111… !java
寫本文以前,其實我本身已經開源了一個 Java學習指南的文檔,裏面包含了一些基礎知識和一些後端(偏 Java 方向)的知識。到目前爲止收穫了 6.1k star 以及 1.5 k fork,close 了 10個 pr 以及 10 個issue。開源只是爲了讓更多的人看到和參與進來,這樣文檔的正確性和質量才能很好的保證。畢竟,我我的能力、時間以及知識廣度和深度有限,一份好的項目的誕生確定離不開和其餘人的共同努力。linux
另外,我我的以爲不論你是前端仍是後端(部份內容可能會偏 Java 方向一點)都能從本文中學到東西。git
本人技術水平有限,歡迎各位指正!寫的很差的話,請多見諒!程序員
不管是校招仍是社招都避免不了各類面試、筆試,如何去準備這些東西就顯得格外重要。不管是筆試仍是面試都是有章可循的,我這個「有章可循」說的意思只是說應對技術面試是能夠提早準備。 我其實特別不喜歡那種臨近考試就提早背啊記啊各類題的行爲,很是反對!我以爲這種方法特別極端,並且在稍有一點經驗的面試官面前是根本沒有用的。建議你們仍是一步一個腳印踏踏實實地走。github
指揮若定以後,決勝千里以外!不打毫無準備的仗,我以爲你們能夠先從下面幾個方面來準備面試:面試
「80%的offer掌握在20%的人手中」 這句話也不是不無道理的。決定你面試可否成功的因素中實力當然佔有很大一部分比例,可是若是你的心態或者說運氣很差的話,依然沒法拿到滿意的 offer。運氣暫且不談,就拿心態來講,千萬不要由於面試失敗而氣餒或者說懷疑本身的能力,面試失敗以後多總結一下失敗的緣由,後面你就會發現本身會愈來愈強大。redis
另外,你們要明確的很重要的幾點是:算法
筆主能力有限,若是有不對的地方或者和你想法不一樣的地方,敬請雅正、不捨賜教。
若是想了解個人更多信息,能夠關注個人 Github 或者微信公衆號:"Java面試通關手冊"。
俗話說的好:「工欲善其事,必先利其器」。準備一份好的簡歷對於能不能找到一份好工做起到了相當重要的做用。
假如你是網申,你的簡歷必然會通過HR的篩選,一張簡歷HR可能也就花費10秒鐘看一下,而後HR就會決定你這一關是Fail仍是Pass。
假如你是內推,若是你的簡歷沒有什麼優點的話,就算是內推你的人再用心,也無能爲力。
另外,就算你經過了篩選,後面的面試中,面試官也會根據你的簡從來判斷你到底是否值得他花費不少時間去面試。
目前寫簡歷的方式有兩種廣泛被承認,一種是 STAR, 一種是 FAB。
STAR法則(Situation Task Action Result):
FAB 法則(Feature Advantage Benefit):
簡歷上有一兩個項目經歷很正常,可是真正能把項目經歷很好的展現給面試官的很是少。對於項目經歷你們能夠考慮從以下幾點來寫:
先問一下你本身會什麼,而後看看你意向的公司須要什麼。通常HR可能並不太懂技術,因此他在篩選簡歷的時候可能就盯着你專業技能的關鍵詞來看。對於公司有要求而你不會的技能,你能夠花幾天時間學習一下,而後在簡歷上能夠寫上本身瞭解這個技能。好比你能夠這樣寫:
分享一個Github上開源的程序員簡歷模板。包括PHP程序員簡歷模板、iOS程序員簡歷模板、Android程序員簡歷模板、Web前端程序員簡歷模板、Java程序員簡歷模板、C/C++程序員簡歷模板、NodeJS程序員簡歷模板、架構師簡歷模板以及通用程序員簡歷模板 。 Github地址:github.com/geekcompany…
若是想學如何用 Markdown 寫簡歷寫一份高質量簡歷,請看這裏:github.com/Snailclimb/…
下面列舉幾個常見問題的回答!
UDP 在傳送數據以前不須要先創建鏈接,遠地主機在收到 UDP 報文後,不須要給出任何確認。雖然 UDP 不提供可靠交付,但在某些狀況下 UDP 確是一種最有效的工做方式(通常用於即時通訊),好比: QQ 語音、 QQ 視頻 、直播等等
TCP 提供面向鏈接的服務。在傳送數據以前必須先創建鏈接,數據傳送結束後要釋放鏈接。 TCP 不提供廣播或多播服務。因爲 TCP 要提供可靠的,面向鏈接的運輸服務(TCP的可靠體如今TCP在傳遞數據以前,會有三次握手來創建鏈接,並且在數據傳遞時,有確認、窗口、重傳、擁塞控制機制,在數據傳完後,還會斷開鏈接用來節約系統資源),這一難以免增長了許多開銷,如確認,流量控制,計時器以及鏈接管理等。這不只使協議數據單元的首部增大不少,還要佔用許多處理機資源。TCP 通常用於文件傳輸、發送和接收郵件、遠程登陸等場景。
百度好像最喜歡問這個問題。
打開一個網頁,整個過程會使用哪些協議
圖片來源:《圖解HTTP》
整體來講分爲如下幾個過程:
具體能夠參考下面這篇文章:
通常面試官會經過這樣的問題來考察你對計算機網絡知識體系的理解。
圖片來源:《圖解HTTP》
在HTTP/1.0中默認使用短鏈接。也就是說,客戶端和服務器每進行一次HTTP操做,就創建一次鏈接,任務結束就中斷鏈接。當客戶端瀏覽器訪問的某個HTML或其餘類型的Web頁中包含有其餘的Web資源(如JavaScript文件、圖像文件、CSS文件等),每遇到這樣一個Web資源,瀏覽器就會從新創建一個HTTP會話。
而從HTTP/1.1起,默認使用長鏈接,用以保持鏈接特性。使用長鏈接的HTTP協議,會在響應頭加入這行代碼:
Connection:keep-alive
複製代碼
在使用長鏈接的狀況下,當一個網頁打開完成後,客戶端和服務器之間用於傳輸HTTP數據的TCP鏈接不會關閉,客戶端再次訪問這個服務器時,會繼續使用這一條已經創建的鏈接。Keep-Alive不會永久保持鏈接,它有一個保持時間,能夠在不一樣的服務器軟件(如Apache)中設定這個時間。實現長鏈接須要客戶端和服務端都支持長鏈接。
HTTP協議的長鏈接和短鏈接,實質上是TCP協議的長鏈接和短鏈接。
爲了準確無誤地把數據送達目標處,TCP協議採用了三次握手策略。
漫畫圖解:
圖片來源:《圖解HTTP》
簡單示意圖:
爲何要三次握手?
三次握手的目的是創建可靠的通訊信道,說到通信,簡單來講就是數據的發送與接收,而三次握手最主要的目的就是雙方確認本身與對方的發送與接收是正常的。
第一次握手:Client 什麼都不能確認;Server 確認了對方發送正常
第二次握手:Client 確認了:本身發送、接收正常,對方發送、接收正常;Server 確認了:本身接收正常,對方發送正常
第三次握手:Client 確認了:本身發送、接收正常,對方發送、接收正常;Server 確認了:本身發送、接收正常,對方發送接收正常
因此三次握手就能確認雙發收發功能都正常,缺一不可。
爲何要傳回 SYN
接收端傳回發送端所發送的 SYN 是爲了告訴發送端,我接收到的信息確實就是你所發送的信號了。
SYN 是 TCP/IP 創建鏈接時使用的握手信號。在客戶機和服務器之間創建正常的 TCP 網絡鏈接時,客戶機首先發出一個 SYN 消息,服務器使用 SYN-ACK 應答表示接收到了這個消息,最後客戶機再以 ACK(Acknowledgement[漢譯:確認字符 ,在數據通訊傳輸中,接收站發給發送站的一種傳輸控制字符。它表示確認發來的數據已經接受無誤。 ])消息響應。這樣在客戶機和服務器之間才能創建起可靠的TCP鏈接,數據才能夠在客戶機和服務器之間傳遞。
傳了 SYN,爲啥還要傳 ACK
雙方通訊無誤必須是二者互相發送信息都無誤。傳了 SYN,證實發送方到接收方的通道沒有問題,可是接收方到發送方的通道還須要 ACK 信號來進行驗證。
斷開一個 TCP 鏈接則須要「四次揮手」:
爲何要四次揮手
任何一方均可以在數據傳送結束後發出鏈接釋放的通知,待對方確認後進入半關閉狀態。當另外一方也沒有數據再發送的時候,則發出鏈接釋放通知,對方確認後就徹底關閉了TCP鏈接。
舉個例子:A 和 B 打電話,通話即將結束後,A 說「我沒啥要說的了」,B回答「我知道了」,可是 B 可能還會有要說的話,A 不能要求 B 跟着本身的節奏結束通話,因而 B 可能又巴拉巴拉說了一通,最後 B 說「我說完了」,A 回答「知道了」,這樣通話纔算結束。
上面講的比較歸納,推薦一篇講的比較細緻的文章:
Linux文件系統簡介
在Linux操做系統中,全部被操做系統管理的資源,例如網絡接口卡、磁盤驅動器、打印機、輸入輸出設備、普通文件或是目錄都被看做是一個文件。
也就是說在LINUX系統中有一個重要的概念:一切都是文件。其實這是UNIX哲學的一個體現,而Linux是重寫UNIX而來,因此這個概念也就傳承了下來。在UNIX系統中,把一切資源都看做是文件,包括硬件設備。UNIX系統把每一個硬件都當作是一個文件,一般稱爲設備文件,這樣用戶就能夠用讀寫文件的方式實現對硬件的訪問。
文件類型與目錄結構
Linux支持5種文件類型 :
Linux的目錄結構以下:
Linux文件系統的結構層次鮮明,就像一棵倒立的樹,最頂層是其根目錄:
常見目錄說明:
目錄切換命令
cd usr
: 切換到該目錄下usr目錄cd ..(或cd../)
: 切換到上一層目錄cd /
: 切換到系統根目錄cd ~
: 切換到用戶主目錄cd -
: 切換到上一個所在目錄目錄的操做命令(增刪改查)
mkdir 目錄名稱
: 增長目錄
ls或者ll
(ll是ls -l的縮寫,ll命令以看到該目錄下的全部目錄和文件的詳細信息):查看目錄信息
find 目錄 參數
: 尋找目錄(查)
mv 目錄名稱 新目錄名稱
: 修改目錄的名稱(改)
注意:mv的語法不只能夠對目錄進行重命名並且也能夠對各類文件,壓縮包等進行 重命名的操做。mv命令用來對文件或目錄從新命名,或者將文件從一個目錄移到另外一個目錄中。後面會介紹到mv命令的另外一個用法。
mv 目錄名稱 目錄的新位置
: 移動目錄的位置---剪切(改)
注意:mv語法不只能夠對目錄進行剪切操做,對文件和壓縮包等均可執行剪切操做。另外mv與cp的結果不一樣,mv好像文件「搬家」,文件個數並未增長。而cp對文件進行復制,文件個數增長了。
cp -r 目錄名稱 目錄拷貝的目標位置
: 拷貝目錄(改),-r表明遞歸拷貝
注意:cp命令不只能夠拷貝目錄還能夠拷貝文件,壓縮包等,拷貝文件和壓縮包時不 用寫-r遞歸
rm [-rf] 目錄
: 刪除目錄(刪)
注意:rm不只能夠刪除目錄,也能夠刪除其餘文件或壓縮包,爲了加強你們的記憶, 不管刪除任何目錄或文件,都直接使用rm -rf
目錄/文件/壓縮包
文件的操做命令(增刪改查)
touch 文件名稱
: 文件的建立(增)
cat/more/less/tail 文件名稱
文件的查看(查)
cat
: 只能顯示最後一屏內容more
: 能夠顯示百分比,回車能夠向下一行, 空格能夠向下一頁,q能夠退出查看less
: 能夠使用鍵盤上的PgUp和PgDn向上 和向下翻頁,q結束查看tail-10
: 查看文件的後10行,Ctrl+C結束注意:命令 tail -f 文件 能夠對某個文件進行動態監控,例如tomcat的日誌文件, 會隨着程序的運行,日誌會變化,能夠使用tail -f catalina-2016-11-11.log 監控 文 件的變化
vim 文件
: 修改文件的內容(改)
vim編輯器是Linux中的強大組件,是vi編輯器的增強版,vim編輯器的命令和快捷方式有不少,但此處不一一闡述,你們也無需研究的很透徹,使用vim編輯修改文件的方式基本會使用就能夠了。
在實際開發中,使用vim編輯器主要做用就是修改配置文件,下面是通常步驟:
vim 文件------>進入文件----->命令模式------>按i進入編輯模式----->編輯文件 ------->按Esc進入底行模式----->輸入:wq/q! (輸入wq表明寫入內容並退出,即保存;輸入q!表明強制退出不保存。)
rm -rf 文件
: 刪除文件(刪)
同目錄刪除:熟記 rm -rf
文件 便可
壓縮文件的操做命令
1)打包並壓縮文件:
Linux中的打包文件通常是以.tar結尾的,壓縮的命令通常是以.gz結尾的。
而通常狀況下打包和壓縮是一塊兒進行的,打包並壓縮後的文件的後綴名通常.tar.gz。 命令:tar -zcvf 打包壓縮後的文件名 要打包壓縮的文件
其中:
z:調用gzip壓縮命令進行壓縮
c:打包文件
v:顯示運行過程
f:指定文件名
好比:加入test目錄下有三個文件分別是 :aaa.txt bbb.txt ccc.txt,若是咱們要打包test目錄並指定壓縮後的壓縮包名稱爲test.tar.gz能夠使用命令:tar -zcvf test.tar.gz aaa.txt bbb.txt ccc.txt
或:tar -zcvf test.tar.gz /test/
2)解壓壓縮包:
命令:tar [-xvf] 壓縮文件
其中:x:表明解壓
示例:
1 將/test下的test.tar.gz解壓到當前目錄下能夠使用命令:tar -xvf test.tar.gz
2 將/test下的test.tar.gz解壓到根目錄/usr下:tar -xvf xxx.tar.gz -C /usr
(- C表明指定解壓的位置)
其餘經常使用命令
pwd
: 顯示當前所在位置
grep 要搜索的字符串 要搜索的文件 --color
: 搜索命令,--color表明高亮顯示
ps -ef
/ps aux
: 這兩個命令都是查看當前系統正在運行進程,二者的區別是展現格式不一樣。若是想要查看特定的進程能夠使用這樣的格式:ps aux|grep redis
(查看包括redis字符串的進程)
注意:若是直接用ps((Process Status))命令,會顯示全部進程的狀態,一般結合grep命令查看某進程的狀態。
kill -9 進程的pid
: 殺死進程(-9 表示強制終止。)
先用ps查找進程,而後用kill殺掉
網絡通訊命令:
shutdown
: shutdown -h now
: 指定如今當即關機;shutdown +5 "System will shutdown after 5 minutes"
:指定5分鐘後關機,同時送出警告信息給登入用戶。
reboot
: reboot
: 重開機。reboot -w
: 作個重開機的模擬(只有紀錄並不會真的重開機)。
關於兩者的對比與總結:
MyISAM更適合讀密集的表,而InnoDB更適合寫密集的的表。 在數據庫作主從分離的狀況下,常常選擇MyISAM做爲主庫的存儲引擎。 通常來講,若是須要事務支持,而且有較高的併發讀取頻率(MyISAM的表鎖的粒度太大,因此當該表寫併發量較高時,要等待的查詢就會不少了),InnoDB是不錯的選擇。若是你的數據量很大(MyISAM支持壓縮特性能夠減小磁盤的空間佔用),並且不須要支持事務時,MyISAM是最好的選擇。
Mysql索引使用的數據結構主要有BTree索引 和 哈希索引 。對於哈希索引來講,底層的數據結構就是哈希表,所以在絕大多數需求爲單條記錄查詢的時候,能夠選擇哈希索引,查詢性能最快;其他大部分場景,建議選擇BTree索引。
Mysql的BTree索引使用的是B數中的B+Tree,但對於主要的兩種存儲引擎的實現方式是不一樣的。
另外,再推薦幾篇比較好的關於索引的文章:
當MySQL單表記錄數過大時,數據庫的CRUD性能會明顯降低,一些常見的優化措施以下:
限定數據的範圍: 務必禁止不帶任何限制數據範圍條件的查詢語句。好比:咱們當用戶在查詢訂單歷史的時候,咱們能夠控制在一個月的範圍內。;
讀/寫分離: 經典的數據庫拆分方案,主庫負責寫,從庫負責讀;
緩存: 使用MySQL的緩存,另外對重量級、更新少的數據能夠考慮使用應用級別的緩存;
垂直分區:
根據數據庫裏面數據表的相關性進行拆分。 例如,用戶表中既有用戶的登陸信息又有用戶的基本信息,能夠將用戶表拆分紅兩個單獨的表,甚至放到單獨的庫作分庫。
簡單來講垂直拆分是指數據表列的拆分,把一張列比較多的表拆分爲多張表。 以下圖所示,這樣來講你們應該就更容易理解了。
垂直拆分的優勢: 能夠使得行數據變小,在查詢時減小讀取的Block數,減小I/O次數。此外,垂直分區能夠簡化表的結構,易於維護。
垂直拆分的缺點: 主鍵會出現冗餘,須要管理冗餘列,並會引發Join操做,能夠經過在應用層進行Join來解決。此外,垂直分區會讓事務變得更加複雜;
水平分區:
保持數據表結構不變,經過某種策略存儲數據分片。這樣每一片數據分散到不一樣的表或者庫中,達到了分佈式的目的。 水平拆分能夠支撐很是大的數據量。
水平拆分是指數據錶行的拆分,表的行數超過200萬行時,就會變慢,這時能夠把一張的表的數據拆成多張表來存放。舉個例子:咱們能夠將用戶信息表拆分紅多個用戶信息表,這樣就能夠避免單一表數據量過大對性能形成影響。
水品拆分能夠支持很是大的數據量。須要注意的一點是:分表僅僅是解決了單一表數據過大的問題,但因爲表的數據仍是在同一臺機器上,其實對於提高MySQL併發能力沒有什麼意義,因此 水品拆分最好分庫 。
水平拆分可以 支持很是大的數據量存儲,應用端改造也少,但 分片事務難以解決 ,跨界點Join性能較差,邏輯複雜。《Java工程師修煉之道》的做者推薦 儘可能不要對數據進行分片,由於拆分會帶來邏輯、部署、運維的各類複雜度 ,通常的數據表在優化得當的狀況下支撐千萬如下的數據量是沒有太大問題的。若是實在要分片,儘可能選擇客戶端分片架構,這樣能夠減小一次和中間件的網絡I/O。
下面補充一下數據庫分片的兩種常見方案:
關於 redis 必知必會的11個問題!後兩個問題,暫未更新!若有須要,能夠關注個人 Github 或者微信公衆號:「Java面試通關手冊」獲取後續更新內容。
簡單來講 redis 就是一個數據庫,不過與傳統數據庫不一樣的是 redis 的數據是存在內存中的,因此存寫速度很是快,所以 redis 被普遍應用於緩存方向。另外,redis 也常常用來作分佈式鎖。redis 提供了多種數據類型來支持不一樣的業務場景。除此以外,redis 支持事務 、持久化、LUA腳本、LRU驅動事件、多種集羣方案。
主要從「高性能」和「高併發」這兩點來看待這個問題。
高性能:
假如用戶第一次訪問數據庫中的某些數據。這個過程會比較慢,由於是從硬盤上讀取的。將該用戶訪問的數據存在數緩存中,這樣下一次再訪問這些數據的時候就能夠直接從緩存中獲取了。操做緩存就是直接操做內存,因此速度至關快。若是數據庫中的對應數據改變的以後,同步改變緩存中相應的數據便可!
高併發:
直接操做緩存可以承受的請求是遠遠大於直接訪問數據庫的,因此咱們能夠考慮把數據庫中的部分數據轉移到緩存中去,這樣用戶的一部分請求會直接到緩存這裏而不用通過數據庫。
下面的內容來自 segmentfault 一位網友的提問,地址:segmentfault.com/q/101000000…
緩存分爲本地緩存和分佈式緩存。以java爲例,使用自帶的map或者guava實現的是本地緩存,最主要的特色是輕量以及快速,生命週期隨着 jvm 的銷燬而結束,而且在多實例的狀況下,每一個實例都須要各自保存一份緩存,緩存不具備一致性。
使用 redis 或 memcached 之類的稱爲分佈式緩存,在多實例的狀況下,各實例共用一份緩存數據,緩存具備一致性。缺點是須要保持 redis 或 memcached服務的高可用,整個程序架構上較爲複雜。
對於 redis 和 memcached 我總結了下面四點。如今公司通常都是用 redis 來實現緩存,並且 redis 自身也愈來愈強大了!
來自網絡上的一張圖,這裏分享給你們!
經常使用命令: set,get,decr,incr,mget 等。
String數據結構是簡單的key-value類型,value其實不只能夠是String,也能夠是數字。 常規key-value緩存應用; 常規計數:微博數,粉絲數等。
經常使用命令: hget,hset,hgetall 等。
Hash 是一個 string 類型的 field 和 value 的映射表,hash 特別適合用於存儲對象,後續操做的時候,你能夠直接僅僅修改這個對象中的某個字段的值。 好比咱們能夠Hash數據結構來存儲用戶信息,商品信息等等。好比下面我就用 hash 類型存放了我本人的一些信息:
key=JavaUser293847
value={
「id」: 1,
「name」: 「SnailClimb」,
「age」: 22,
「location」: 「Wuhan, Hubei」
}
複製代碼
經常使用命令: lpush,rpush,lpop,rpop,lrange等
list就是鏈表,Redis list的應用場景很是多,也是Redis最重要的數據結構之一,好比微博的關注列表,粉絲列表,消息列表等功能均可以用Redis的 list 結構來實現。
Redis list 的實現爲一個雙向鏈表,便可以支持反向查找和遍歷,更方便操做,不過帶來了部分額外的內存開銷。
另外能夠經過 lrange 命令,就是從某個元素開始讀取多少個元素,能夠基於 list 實現分頁查詢,這個很棒的一個功能,基於 redis 實現簡單的高性能分頁,能夠作相似微博那種下拉不斷分頁的東西(一頁一頁的往下走),性能高。
經常使用命令: sadd,spop,smembers,sunion 等
set對外提供的功能與list相似是一個列表的功能,特殊之處在於set是能夠自動排重的。
當你須要存儲一個列表數據,又不但願出現重複數據時,set是一個很好的選擇,而且set提供了判斷某個成員是否在一個set集合內的重要接口,這個也是list所不能提供的。能夠基於 set 輕易實現交集、並集、差集的操做。
好比:在微博應用中,能夠將一個用戶全部的關注人存在一個集合中,將其全部粉絲存在一個集合。Redis能夠很是方便的實現如共同關注、共同粉絲、共同喜愛等功能。這個過程也就是求交集的過程,具體命令以下:
sinterstore key1 key2 key3 將交集存在key1內
複製代碼
經常使用命令: zadd,zrange,zrem,zcard等
和set相比,sorted set增長了一個權重參數score,使得集合中的元素可以按score進行有序排列。
舉例: 在直播系統中,實時排行信息包含直播間在線用戶列表,各類禮物排行榜,彈幕消息(能夠理解爲按消息維度的消息排行榜)等信息,適合使用 Redis 中的 SortedSet 結構進行存儲。
Redis中有個設置時間過時的功能,即對存儲在 redis 數據庫中的值能夠設置一個過時時間。做爲一個緩存數據庫,這是很是實用的。如咱們通常項目中的token或者一些登陸信息,尤爲是短信驗證碼都是有時間限制的,按照傳統的數據庫處理方式,通常都是本身判斷過時,這樣無疑會嚴重影響項目性能。
咱們set key的時候,均可以給一個expire time,就是過時時間,經過過時時間咱們能夠指定這個 key 能夠存貨的時間。
若是假設你設置一個一批 key 只能存活1個小時,那麼接下來1小時後,redis是怎麼對這批key進行刪除的?
按期刪除+惰性刪除。
經過名字大概就能猜出這兩個刪除方式的意思了。
可是僅僅經過設置過時時間仍是有問題的。咱們想一下:若是按期刪除漏掉了不少過時 key,而後你也沒及時去查,也就沒走惰性刪除,此時會怎麼樣?若是大量過時key堆積在內存裏,致使redis內存塊耗盡了。怎麼解決這個問題呢?
redis 內存淘汰機制。
redis 配置文件 redis.conf 中有相關注釋,我這裏就不貼了,你們能夠自行查閱或者經過這個網址查看: download.redis.io/redis-stabl…
redis 提供 6種數據淘汰策略:
備註: 關於 redis 設置過時時間以及內存淘汰機制,我這裏只是簡單的總結一下,後面會專門寫一篇文章來總結!
不少時候咱們須要持久化數據也就是將內存中的數據寫入到硬盤裏面,大部分緣由是爲了以後重用數據(好比重啓機器、機器故障以後回覆數據),或者是爲了防止系統故障而將數據備份到一個遠程位置。
Redis不一樣於Memcached的很重一點就是,Redis支持持久化,並且支持兩種不一樣的持久化操做。Redis的一種持久化方式叫快照(snapshotting,RDB),另外一種方式是隻追加文件(append-only file,AOF).這兩種方法各有千秋,下面我會詳細這兩種持久化方法是什麼,怎麼用,如何選擇適合本身的持久化方法。
快照(snapshotting)持久化(RDB)
Redis能夠經過建立快照來得到存儲在內存裏面的數據在某個時間點上的副本。Redis建立快照以後,能夠對快照進行備份,能夠將快照複製到其餘服務器從而建立具備相同數據的服務器副本(Redis主從結構,主要用來提升Redis性能),還能夠將快照留在原地以便重啓服務器的時候使用。
快照持久化是Redis默認採用的持久化方式,在redis.conf配置文件中默認有此下配置:
save 900 1 #在900秒(15分鐘)以後,若是至少有1個key發生變化,Redis就會自動觸發BGSAVE命令建立快照。
save 300 10 #在300秒(5分鐘)以後,若是至少有10個key發生變化,Redis就會自動觸發BGSAVE命令建立快照。
save 60 10000 #在60秒(1分鐘)以後,若是至少有10000個key發生變化,Redis就會自動觸發BGSAVE命令建立快照。
複製代碼
AOF(append-only file)持久化
與快照持久化相比,AOF持久化 的實時性更好,所以已成爲主流的持久化方案。默認狀況下Redis沒有開啓AOF(append only file)方式的持久化,能夠經過appendonly參數開啓:
appendonly yes
複製代碼
開啓AOF持久化後每執行一條會更改Redis中的數據的命令,Redis就會將該命令寫入硬盤中的AOF文件。AOF文件的保存位置和RDB文件的位置相同,都是經過dir參數設置的,默認的文件名是appendonly.aof。
在Redis的配置文件中存在三種不一樣的 AOF 持久化方式,它們分別是:
appendfsync always #每次有數據修改發生時都會寫入AOF文件,這樣會嚴重下降Redis的速度
appendfsync everysec #每秒鐘同步一次,顯示地將多個寫命令同步到硬盤
appendfsync no #讓操做系統決定什麼時候進行同步
複製代碼
爲了兼顧數據和寫入性能,用戶能夠考慮 appendfsync everysec選項 ,讓Redis每秒同步一次AOF文件,Redis性能幾乎沒受到任何影響。並且這樣即便出現系統崩潰,用戶最多隻會丟失一秒以內產生的數據。當硬盤忙於執行寫入操做的時候,Redis還會優雅的放慢本身的速度以便適應硬盤的最大寫入速度。
補充內容:AOF 重寫
AOF重寫能夠產生一個新的AOF文件,這個新的AOF文件和原有的AOF文件所保存的數據庫狀態同樣,但體積更小。
AOF重寫是一個有歧義的名字,該功能是經過讀取數據庫中的鍵值對來實現的,程序無須對現有AOF文件進行任伺讀入、分析或者寫人操做。
在執行 BGREWRITEAOF 命令時,Redis 服務器會維護一個 AOF 重寫緩衝區,該緩衝區會在子進程建立新AOF文件期間,記錄服務器執行的全部寫命令。當子進程完成建立新AOF文件的工做以後,服務器會將重寫緩衝區中的全部內容追加到新AOF文件的末尾,使得新舊兩個AOF文件所保存的數據庫狀態一致。最後,服務器用新的AOF文件替換舊的AOF文件,以此來完成AOF文件重寫操做
更多內容能夠查看個人這篇文章:
緩存雪崩
簡介:緩存同一時間大面積的失效,因此,後面的請求都會落到數據庫上,形成數據庫短期內承受大量請求而崩掉。
解決辦法(中華石杉老師在他的視頻中提到過):
緩存穿透
簡介:通常是黑客故意去請求緩存中不存在的數據,致使全部的請求都落到數據庫上,形成數據庫短期內承受大量請求而崩掉。
解決辦法: 有不少種方法能夠有效地解決緩存穿透問題,最多見的則是採用布隆過濾器,將全部可能存在的數據哈希到一個足夠大的bitmap中,一個必定不存在的數據會被 這個bitmap攔截掉,從而避免了對底層存儲系統的查詢壓力。另外也有一個更爲簡單粗暴的方法(咱們採用的就是這種),若是一個查詢返回的數據爲空(無論是數 據不存在,仍是系統故障),咱們仍然把這個空結果進行緩存,但它的過時時間會很短,最長不超過五分鐘。
參考:
所謂 Redis 的併發競爭 Key 的問題也就是多個系統同時對一個 key 進行操做,可是最後執行的順序和咱們指望的順序不一樣,這樣也就致使告終果的不一樣!
推薦一種方案:分佈式鎖(zookeeper 和 redis 均可以實現分佈式鎖)。(若是不存在 Redis 的併發競爭 Key 問題,不要使用分佈式鎖,這樣會影響性能)
基於zookeeper臨時有序節點能夠實現的分佈式鎖。大體思想爲:每一個客戶端對某個方法加鎖時,在zookeeper上的與該方法對應的指定節點的目錄下,生成一個惟一的瞬時有序節點。 判斷是否獲取鎖的方式很簡單,只須要判斷有序節點中序號最小的一個。 當釋放鎖的時候,只需將這個瞬時節點刪除便可。同時,其能夠避免服務宕機致使的鎖沒法釋放,而產生的死鎖問題。完成業務流程後,刪除對應的子節點釋放鎖。
在實踐中,固然是從以可靠性爲主。因此首推Zookeeper。
參考:
你只要用緩存,就可能會涉及到緩存與數據庫雙存儲雙寫,你只要是雙寫,就必定會有數據一致性的問題,那麼你如何解決一致性問題?
通常來講,就是若是你的系統不是嚴格要求緩存+數據庫必須一致性的話,緩存能夠稍微的跟數據庫偶爾有不一致的狀況,最好不要作這個方案,讀請求和寫請求串行化,串到一個內存隊列裏去,這樣就能夠保證必定不會出現不一致的狀況
串行化以後,就會致使系統的吞吐量會大幅度的下降,用比正常狀況下多幾倍的機器去支撐線上的一個請求。
參考:
重載和重寫的區別
重載: 發生在同一個類中,方法名必須相同,參數類型不一樣、個數不一樣、順序不一樣,方法返回值和訪問修飾符能夠不一樣,發生在編譯時。
重寫: 發生在父子類中,方法名、參數列表必須相同,返回值範圍小於等於父類,拋出的異常範圍小於等於父類,訪問修飾符範圍大於等於父類;若是父類方法訪問修飾符爲 private 則子類就不能重寫該方法。
String 和 StringBuffer、StringBuilder 的區別是什麼?String 爲何是不可變的?
可變性
簡單的來講:String 類中使用 final 關鍵字字符數組保存字符串,private final char value[]
,因此 String 對象是不可變的。而StringBuilder 與 StringBuffer 都繼承自 AbstractStringBuilder 類,在 AbstractStringBuilder 中也是使用字符數組保存字符串char[]value
可是沒有用 final 關鍵字修飾,因此這兩種對象都是可變的。
StringBuilder 與 StringBuffer 的構造方法都是調用父類構造方法也就是 AbstractStringBuilder 實現的,你們能夠自行查閱源碼。
AbstractStringBuilder.java
abstract class AbstractStringBuilder implements Appendable, CharSequence { char[] value; int count; AbstractStringBuilder() { } AbstractStringBuilder(int capacity) { value = new char[capacity]; } 複製代碼
線程安全性
String 中的對象是不可變的,也就能夠理解爲常量,線程安全。AbstractStringBuilder 是 StringBuilder 與 StringBuffer 的公共父類,定義了一些字符串的基本操做,如 expandCapacity、append、insert、indexOf 等公共方法。StringBuffer 對方法加了同步鎖或者對調用的方法加了同步鎖,因此是線程安全的。StringBuilder 並無對方法進行加同步鎖,因此是非線程安全的。
性能
每次對 String 類型進行改變的時候,都會生成一個新的 String 對象,而後將指針指向新的 String 對象。StringBuffer 每次都會對 StringBuffer 對象自己進行操做,而不是生成新的對象並改變對象引用。相同狀況下使用 StirngBuilder 相比使用 StringBuffer 僅能得到 10%~15% 左右的性能提高,但卻要冒多線程不安全的風險。
對於三者使用的總結:
自動裝箱與拆箱
裝箱:將基本類型用它們對應的引用類型包裝起來;
拆箱:將包裝類型轉換爲基本數據類型;
== 與 equals
== : 它的做用是判斷兩個對象的地址是否是相等。即,判斷兩個對象是否是同一個對象。(基本數據類型==比較的是值,引用數據類型==比較的是內存地址)
equals() : 它的做用也是判斷兩個對象是否相等。但它通常有兩種使用狀況:
舉個例子:
public class test1 { public static void main(String[] args) { String a = new String("ab"); // a 爲一個引用 String b = new String("ab"); // b爲另外一個引用,對象的內容同樣 String aa = "ab"; // 放在常量池中 String bb = "ab"; // 從常量池中查找 if (aa == bb) // true System.out.println("aa==bb"); if (a == b) // false,非同一對象 System.out.println("a==b"); if (a.equals(b)) // true System.out.println("aEQb"); if (42 == 42.0) { // true System.out.println("true"); } } } 複製代碼
說明:
關於 final 關鍵字的一些總結
final關鍵字主要用在三個地方:變量、方法、類。
Arraylist 與 LinkedList 異同
add(E e)
方法的時候, ArrayList 會默認在將指定的元素追加到此列表的末尾,這種狀況時間複雜度就是O(1)。可是若是要在指定位置 i 插入和刪除元素的話(add(int index, E element)
)時間複雜度就爲 O(n-i)。由於在進行上述操做的時候集合中第 i 和第 i 個元素以後的(n-i)個元素都要執行向後位/向前移一位的操做。 ② LinkedList 採用鏈表存儲,因此插入,刪除元素時間複雜度不受元素位置的影響,都是近似 O(1)而數組爲近似 O(n)。get(int index)
方法)。補充:數據結構基礎之雙向鏈表
雙向鏈表也叫雙鏈表,是鏈表的一種,它的每一個數據結點中都有兩個指針,分別指向直接後繼和直接前驅。因此,從雙向鏈表中的任意一個結點開始,均可以很方便地訪問它的前驅結點和後繼結點。通常咱們都構造雙向循環鏈表,以下圖所示,同時下圖也是LinkedList 底層使用的是雙向循環鏈表數據結構。
ArrayList 與 Vector 區別
Vector類的全部方法都是同步的。能夠由兩個線程安全地訪問一個Vector對象、可是一個線程訪問Vector的話代碼要在同步操做上耗費大量的時間。
Arraylist不是同步的,因此在不須要保證線程安全時時建議使用Arraylist。
HashMap的底層實現
①JDK1.8以前
JDK1.8 以前 HashMap 底層是 數組和鏈表 結合在一塊兒使用也就是 鏈表散列。HashMap 經過 key 的 hashCode 通過擾動函數處理事後獲得 hash 值,而後經過 (n - 1) & hash
判斷當前元素存放的位置(這裏的 n 指的時數組的長度),若是當前位置存在元素的話,就判斷該元素與要存入的元素的 hash 值以及 key 是否相同,若是相同的話,直接覆蓋,不相同就經過拉鍊法解決衝突。
所謂擾動函數指的就是 HashMap 的 hash 方法。使用 hash 方法也就是擾動函數是爲了防止一些實現比較差的 hashCode() 方法 換句話說使用擾動函數以後能夠減小碰撞。
JDK 1.8 HashMap 的 hash 方法源碼:
JDK 1.8 的 hash方法 相比於 JDK 1.7 hash 方法更加簡化,可是原理不變。
static final int hash(Object key) { int h; // key.hashCode():返回散列值也就是hashcode // ^ :按位異或 // >>>:無符號右移,忽略符號位,空位都以0補齊 return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); } 複製代碼
對比一下 JDK1.7的 HashMap 的 hash 方法源碼.
static int hash(int h) { // This function ensures that hashCodes that differ only by // constant multiples at each bit position have a bounded // number of collisions (approximately 8 at default load factor). h ^= (h >>> 20) ^ (h >>> 12); return h ^ (h >>> 7) ^ (h >>> 4); } 複製代碼
相比於 JDK1.8 的 hash 方法 ,JDK 1.7 的 hash 方法的性能會稍差一點點,由於畢竟擾動了 4 次。
所謂 「拉鍊法」 就是:將鏈表和數組相結合。也就是說建立一個鏈表數組,數組中每一格就是一個鏈表。若遇到哈希衝突,則將衝突的值加到鏈表中便可。
②JDK1.8以後
相比於以前的版本, JDK1.8以後在解決哈希衝突時有了較大的變化,當鏈表長度大於閾值(默認爲8)時,將鏈表轉化爲紅黑樹,以減小搜索時間。
TreeMap、TreeSet以及JDK1.8以後的HashMap底層都用到了紅黑樹。紅黑樹就是爲了解決二叉查找樹的缺陷,由於二叉查找樹在某些狀況下會退化成一個線性結構。
推薦閱讀:
HashMap 和 Hashtable 的區別
synchronized
修飾。(若是你要保證線程安全的話就使用 ConcurrentHashMap 吧!);HashMap 的長度爲何是2的冪次方
爲了能讓 HashMap 存取高效,儘可能較少碰撞,也就是要儘可能把數據分配均勻。咱們上面也講到了過了,Hash 值的範圍值-2147483648到2147483648,先後加起來大概40億的映射空間,只要哈希函數映射得比較均勻鬆散,通常應用是很難出現碰撞的。但問題是一個40億長度的數組,內存是放不下的。因此這個散列值是不能直接拿來用的。用以前還要先作對數組的長度取模運算,獲得的餘數才能用來要存放的位置也就是對應的數組下標。
這個算法應該如何設計呢?
咱們首先可能會想到採用%取餘的操做來實現。可是,重點來了:「取餘(%)操做中若是除數是2的冪次則等價於與其除數減一的與(&)操做(也就是說 hash%length==hash&(length-1)的前提是 length 是2的 n 次方;)。」 而且 採用二進制位操做 &,相對於%可以提升運算效率,這就解釋了 HashMap 的長度爲何是2的冪次方。
HashMap 多線程操做致使死循環問題
在多線程下,進行 put 操做會致使 HashMap 死循環,緣由在於 HashMap 的擴容 resize()方法。因爲擴容是新建一個數組,複製原數據到數組。因爲數組下標掛有鏈表,因此須要複製鏈表,可是多線程操做有可能致使環形鏈表。複製鏈表過程以下:
如下模擬2個線程同時擴容。假設,當前 HashMap 的空間爲2(臨界值爲1),hashcode 分別爲 0 和 1,在散列地址 0 處有元素 A 和 B,這時候要添加元素 C,C 通過 hash 運算,獲得散列地址爲 1,這時候因爲超過了臨界值,空間不夠,須要調用 resize 方法進行擴容,那麼在多線程條件下,會出現條件競爭,模擬過程以下:
線程一:讀取到當前的 HashMap 狀況,在準備擴容時,線程二介入
線程二:讀取 HashMap,進行擴容
線程一:繼續執行
這個過程爲,先將 A 複製到新的 hash 表中,而後接着複製 B 到鏈頭(A 的前邊:B.next=A),原本 B.next=null,到此也就結束了(跟線程二同樣的過程),可是,因爲線程二擴容的緣由,將 B.next=A,因此,這裏繼續複製A,讓 A.next=B,由此,環形鏈表出現:B.next=A; A.next=B
HashSet 和 HashMap 區別
若是你看過 HashSet 源碼的話就應該知道:HashSet 底層就是基於 HashMap 實現的。(HashSet 的源碼很是很是少,由於除了 clone() 方法、writeObject()方法、readObject()方法是 HashSet 本身不得不實現以外,其餘方法都是直接調用 HashMap 中的方法。)
ConcurrentHashMap 和 Hashtable 的區別
ConcurrentHashMap 和 Hashtable 的區別主要體如今實現線程安全的方式上不一樣。
二者的對比圖:
圖片來源:www.cnblogs.com/chengxiao/p…
HashTable:
JDK1.7的ConcurrentHashMap:
ConcurrentHashMap線程安全的具體實現方式/底層具體實現
①JDK1.7(上面有示意圖)
首先將數據分爲一段一段的存儲,而後給每一段數據配一把鎖,當一個線程佔用鎖訪問其中一個段數據時,其餘段的數據也能被其餘線程訪問。
ConcurrentHashMap 是由 Segment 數組結構和 HahEntry 數組結構組成。
Segment 實現了 ReentrantLock,因此 Segment 是一種可重入鎖,扮演鎖的角色。HashEntry 用於存儲鍵值對數據。
static class Segment<K,V> extends ReentrantLock implements Serializable { } 複製代碼
一個 ConcurrentHashMap 裏包含一個 Segment 數組。Segment 的結構和HashMap相似,是一種數組和鏈表結構,一個 Segment 包含一個 HashEntry 數組,每一個 HashEntry 是一個鏈表結構的元素,每一個 Segment 守護着一個HashEntry數組裏的元素,當對 HashEntry 數組的數據進行修改時,必須首先得到對應的 Segment的鎖。
②JDK1.8 (上面有示意圖)
ConcurrentHashMap取消了Segment分段鎖,採用CAS和synchronized來保證併發安全。數據結構跟HashMap1.8的結構相似,數組+鏈表/紅黑二叉樹。
synchronized只鎖定當前鏈表或紅黑二叉樹的首節點,這樣只要hash不衝突,就不會產生併發,效率又提高N倍。
集合框架底層數據結構總結
Collection
1.List
Arraylist: Object數組
Vector: Object數組
LinkedList: 雙向循環鏈表 2.Set
HashSet(無序,惟一): 基於 HashMap 實現的,底層採用 HashMap 來保存元素
LinkedHashSet: LinkedHashSet 繼承與 HashSet,而且其內部是經過 LinkedHashMap 來實現的。有點相似於咱們以前說的LinkedHashMap 其內部是基於 Hashmap 實現同樣,不過仍是有一點點區別的。
TreeSet(有序,惟一): 紅黑樹(自平衡的排序二叉樹。)
Map
關於 Java多線程,在面試的時候,問的比較多的就是①悲觀鎖和樂觀鎖( 具體能夠看個人這篇文章:面試必備之樂觀鎖與悲觀鎖)、②synchronized和lock區別以及volatile和synchronized的區別,③可重入鎖與非可重入鎖的區別、④多線程是解決什麼問題的、⑤線程池解決什麼問題、⑥線程池的原理、⑦線程池使用時的注意事項、⑧AQS原理、⑨ReentranLock源碼,設計原理,總體過程 等等問題。
面試官在多線程這一部分極可能會問你有沒有在項目中實際使用多線程的經歷。因此,若是你在你的項目中有實際使用Java多線程的經歷 的話,會爲你加分很多哦!
關於Java虛擬機,在面試的時候通常會問的大多就是①Java內存區域、②虛擬機垃圾算法、③虛擬機垃圾收集器、④JVM內存管理、⑤JVM調優這些問題了。 具體能夠查看個人這兩篇文章:
設計模式比較常見的就是讓你手寫一個單例模式(注意單例模式的幾種不一樣的實現方法)或者讓你說一下某個常見的設計模式在你的項目中是如何使用的,另外面試官還有可能問你抽象工廠和工廠方法模式的區別、工廠模式的思想這樣的問題。
建議把代理模式、觀察者模式、(抽象)工廠模式好好看一下,這三個設計模式也很重要。
數據結構比較常問的就是:二叉樹、紅黑樹(極可能讓你手繪一個紅黑樹出來哦!)、二叉查找樹(BST)、平衡二叉樹(Self-balancing binary search tree)、B-樹,B+樹與B*樹的優缺點比較、 LSM 樹這些知識點。
數據結構很重要,並且學起來也相對要難一些。建議學習數據結構必定要按部就班的來,一步一個腳印的走好。必定要搞懂原理,最好本身能用代碼實現一遍。
常見的加密算法、排序算法都須要本身提早了解一下,排序算法最好本身可以獨立手寫出來。
我以爲面試中最刺激、最有壓力或者說最有挑戰的一個環節就是手撕算法了。面試中大部分算法題目都是來自於Leetcode、劍指offer上面,建議你們能夠天天擠出一點時間刷一下算法題。
推薦兩個刷題必備網站:
LeetCode:
牛客網:
面試官可能會問你,瞭解哪些排序方法啊?除了冒泡排序和選擇排序能不能給我手寫一個其餘的排序算法。或者面試官可能會直接問你:「能不能給我手寫一個快排出來?」。
快排的基本思想: 經過選擇的參考值將待排序記錄分割成獨立的兩部分,一部分全小於選取的參考值,另外一部分全大於選取的參考值。對分割以後的部分再進行一樣的操做直到沒法再進行該操做位置(能夠使用遞歸)。
下面是我寫的一個簡單的快排算法,我選擇的參考值是數組的第一個元素。
import java.util.Arrays; public class QuickSort { public static void main(String[] args) { // TODO Auto-generated method stub int[] num = { 1, 3, 4, 8, 5, 10, 22, 15, 16 }; QuickSort.quickSort(num, 0, num.length - 1); System.out.println(Arrays.toString(num)); } public static void quickSort(int[] a, int start, int end) { // 該值定義了從哪一個位置開始分割數組 int ref; if (start < end) { // 調用partition方法對數組進行排序 ref = partition(a, start, end); // 對分割以後的兩個數組繼續進行排序 quickSort(a, start, ref - 1); quickSort(a, ref + 1, end); } } /** * 選定參考值對給定數組進行一趟快速排序 * * @param a * 數組 * @param start * (切分)每一個數組的第一個的元素的位置 * @param end * (切分)每一個數組的最後一個的元素位置 * @return 下一次要切割數組的位置 */ public static int partition(int[] a, int start, int end) { // 取數組的第一個值做爲參考值(關鍵數據) int refvalue = a[start]; // 從數組的右邊開始往左遍歷,直到找到小於參考值的元素 while (start < end) { while (end > start && a[end] >= refvalue) { end--; } // 將元素直接賦予給左邊第一個元素,即pivotkey所在的位置 a[start] = a[end]; // 從序列的左邊邊開始往右遍歷,直到找到大於基準值的元素 while (end > start && a[start] <= refvalue) { start++; } a[end] = a[start]; return end; } // 最後的start是基準值所在的位置 a[start] = refvalue; return start; } } 複製代碼
時間複雜度分析:
空間複雜度分析:
一種簡單優化的方式:
三向切分快速排序 :核心思想就是將待排序的數據分爲三部分,左邊都小於比較值,右邊都大於比較值,中間的數和比較值相等.三向切分快速排序的特性就是遇到和比較值相同時,不進行數據交換, 這樣對於有大量重複數據的排序時,三向切分快速排序算法就會優於普通快速排序算法,但因爲它總體判斷代碼比普通快速排序多一點,因此對於常見的大量非重複數據,它並不能比普通快速排序多大多的優點 。
Spring通常是不可避免的,若是你的簡歷上註明了你會Spring Boot或者Spring Cloud的話,那麼面試官也可能會同時問你這兩個技術,好比他可能會問你springboot和spring的區別。 因此,必定要謹慎對待寫在簡歷上的東西,必定要對簡歷上的東西很是熟悉。
另外,AOP實現原理、動態代理和靜態代理、Spring IOC的初始化過程、IOC原理、本身怎麼實現一個IOC容器? 這些東西都是常常會被問到的。
TransactionDefinition 接口中定義了五個表示隔離級別的常量:
支持當前事務的狀況:
不支持當前事務的狀況:
其餘狀況:
AOP思想的實現通常都是基於 代理模式 ,在JAVA中通常採用JDK動態代理模式,可是咱們都知道,JDK動態代理模式只能代理接口而不能代理類。所以,Spring AOP 會這樣子來進行切換,由於Spring AOP 同時支持 CGLIB、ASPECTJ、JDK動態代理。
這部份內容能夠查看下面這幾篇文章:
Spring IOC的初始化過程:
IOC源碼閱讀
我以爲實際場景題就是對你的知識運用能力以及思惟能力的考察。建議你們在平時養成多思考問題的習慣,這樣面試的時候碰到這樣的問題就不至於慌了。另外,若是本身實在不會就給面試官委婉的說一下,面試官可能會給你提醒一下。切忌不懂裝懂,亂答一氣。 面試官可能會問你相似這樣的問題:①假設你要作一個銀行app,有可能碰到多我的同時向一個帳戶打錢的狀況,有可能碰到什麼問題,如何解決(鎖)②你是怎麼保證你的代碼質量和正確性的?③下單過程當中是下訂單減庫存仍是付款減庫存,分析一下二者的優劣;④同時給10萬我的發工資,怎麼樣設計併發方案,能確保在1分鐘內所有發完。⑤若是讓你設計xxx系統的話,你會如何設計。
最後,再強調幾點:
另外,我我的以爲面試也像是一場全新的征程,失敗和勝利都是日常之事。因此,勸各位不要由於面試失敗而灰心、喪失鬥志。也不要由於面試經過而沾沾自喜,等待你的將是更美好的將來,繼續加油!
初次以外,筆主也在這裏給本身挖一個坑,關於 dubbo、zookeeper 等內容我會在後續作一個系統總結。保證你們看了以後,必定有收穫!
最後,附上徵文連接。
還有三個本次活動的合做夥伴: