無論是開發、測試、運維,每一個技術人員內心都有一個成爲技術大牛的夢,畢竟「夢想老是要有的,萬一實現了呢」!正是對技術夢的追求,促使咱們不斷地努力和提高本身。java
今天分享Java重點面試知識 :linux
多線程(線程狀態、線程併發,Synchronized與Lock的區別和底層原理,經常使用的鎖及其使用場景和原理,面試
volatile和ThreadLocal解決了什麼問題,CAS在Java中的實現算法
線程池原理和實現,阻塞隊列和線程安全隊列,sql
線程間通訊: synchronized + wait、notify/notifyAll, Lock + Condition 的多路複用,數據庫
CountDownLatch、CyclicBarrier和Semaphore的做用和用法,使用場景)設計模式
JVM內存管理機制和垃圾回收機制(內存模型、GC策略、算法、分代回收GC類型,Full GC、Minor GC做用範圍和觸發條件)數組
JVM內存調優(內存調整的6個參數,瞭解是怎麼回事,通常作項目過程當中使用較多)緩存
設計模式(熟悉常見設計模式的應用場景,會畫類圖,經常使用:代理,2個工廠,策略,單例,觀察者,適配器,組合與裝飾)安全
JAVA集合類框架(理解框架圖、HashMap、ArrayList、HashSet等的關係和區別,其中HashMap的存儲機制幾乎每次都有問)
HashMap的原理,底層數據結構,rehash的過程,指針碰撞問題HashMap的線程安全問題,爲何會產生這樣的線程安全問題ConcurrentHashMap的數據結構,底層原理,put和get是否線程安全
JAVA的異常處理機制(異常的分類、常見的異常有哪些、Try catch finally的使用)
JVM運行機制(理解JVM是如何運行的,理解類加載機制和類的初始化順序)
Java 的NIO 3個主要概念 Channel、Buffer、Selector,爲什麼提升了性能?加分項:熟悉Netty
Linux基礎(面試筆試中對linux也有必定的要求,建議最好搭建一個linux虛擬機,並練習經常使用的命令)
框架
Spring
Spring IOC原理,Bean的生成和生命週期(工廠模式 + 反射生成 + 單例),Spring用到的設計模式
Spring AOP原理和應用(動態代理與cglib代理,使用場景和代理的本質區別)
Spring如何處理高併發?高併發下,如何保證性能?
單例模式 + ThreadLocal
單例模式大大節省了對象的建立和銷燬,有利於性能提升,ThreadLocal用來保證線程安全性
Spring單例模式下,用ThreadLocal來切換不一樣線程直接的參數,用ThreadLocal是爲了保證線程安全,實際上,ThreadLocal的key就是當前線程的Thread實例
單例模式下,Spring把每一個線程可能存在線程安全問題的參數值放進了ThreadLocal,雖然是一個實例,但在不一樣線程下的數據是相互隔離的,
由於運行時建立和銷燬的bean大大減小了,因此大多數場景下,這種方式對內存資源的消耗較少,而且併發越高,優點越明顯
特別注意:
Spring MVC的Controller不是線程安全的!!!
Spring MVC 是基於方法的攔截,粒度更細,而Spring的Controller默認是Singleton的,即:每一個request請求,系統都會用同一個Controller去處理,
Spring MVC和Servlet都是方法級別的線程安全,若是單例的Controller或Servlet中存在實例變量,都是線程不安全的,而Struts2確實是線程安全的
優勢:不用每次建立Controller,減小了對象建立和銷燬
缺點:Controller是單例的,Controller裏面的變量線程不安全
解決方案:
1.在Controller中使用ThreadLocal變量,把不安全的變量封裝進ThreadLocal,使用ThreadLocal來保存類變量,將類變量保存在線程的變量域中,讓不一樣的請求隔離開來
2.聲明Controller爲原型 scope="prototype",每一個請求都建立新的Controller
3.Controller中不使用實例變量
Spring 事務管理的使用和原理?事務的傳播屬性
聲明式事務管理,在Service之上或Service的方法之上,添加 @Transactional註解
@Transactional如何工做?
Spring在啓動時,會去解析生成相關的Bean,這是會查看擁有相關注解的類和方法,而且爲這些類和方法生成代理,並根據 @Transactional的相關參數進行相關配置注入,這樣就在代理中把相關的事務處理掉了(開啓正常提交事務,異常回滾事務)真正的數據庫層,事務提交和回滾是經過binlog和redo log實現的
Spring如何解決對象的循環依賴引用?( 只支持Singleton做用域的, setter方式的循環依賴!不支持構造器方式和prototype的循環依賴)
原理:
建立Bean A時,先經過無參構造器建立一個A實例,此時屬性都是空的,但對象引用已經建立建立出來,而後把Bean A的引用提早暴露出來,
而後setter B屬性時,建立B對象,此時一樣經過無參構造器,構造一個B對象的引用,並將B對象引用暴露出來。
接着B執行setter方法,去池中找到A(由於此時,A已經暴露出來,有指向該對象的引用了),這樣依賴B就構造完成,也初始化完成,而後A接着初始化完成,循環依賴就這麼解決了!
總結:先建立對象引用,再經過setter()方式,給屬性賦值,層層建立對象 !!!
Bean A初始化時,先對其依賴B進行初始化,同時,經過默認無參構造器,生成本身的引用,而不調用其setter()方法,
當B對象建立時,若是還依賴C,則也經過無參構造器,生成B的引用,
C對象建立時,若是引用了A,則去對象池中查到A的引用,而後調用setter()方式,注入A,完成C對象的建立
C建立完成後,B使用setter()方式,注入C,完成B對象建立,
B對象場景完成後,A使用setter()方式,注入B,完成A對象建立,
最終,完成setter()方式的循環依賴!
數據庫
InnoDB和MyISAM區別和選擇
1.InnoDB不支持FULLTEXT類型的索引。
2.InnoDB 中不保存表的具體行數,也就是說,執行select count() from table時,InnoDB要掃描一遍整個表來計算有多少行,可是MyISAM只要簡單的讀出保存好的行數便可。注意的是,當count()語句包含 where條件時,兩種表的操做是同樣的。
3.對於AUTO_INCREMENT類型的字段,InnoDB中必須包含只有該字段的索引,可是在MyISAM表中,能夠和其餘字段一塊兒創建聯合索引。
4.DELETE FROM table時,InnoDB不會從新創建表,而是一行一行的刪除。
5.LOAD TABLE FROM MASTER操做對InnoDB是不起做用的,解決方法是首先把InnoDB表改爲MyISAM表,導入數據後再改爲InnoDB表,可是對於使用的額外的InnoDB特性(例如外鍵)的表不適用。
另外,InnoDB表的行鎖也不是絕對的,若是在執行一個SQL語句時MySQL不能肯定要掃描的範圍,InnoDB表一樣會鎖全表,例如update table set num=1 where name like 「%aaa%」
任何一種表都不是萬能的,只用恰當的針對業務類型來選擇合適的表類型,才能最大的發揮MySQL的性能優點。
悲觀鎖和樂觀鎖的含義(悲觀鎖:真正的鎖,只容許一個線程操做同一條記錄,
樂觀鎖:一種衝突檢測機制,通常經過版本號或時間戳方式實現,對性能影響較小)
索引使用及其索引原理(索引底層實現:B+樹)
Query查詢優化
1.explain sql查看執行效率,定位優化對象的性能瓶頸
2.永遠用小結果驅動大的結果集
3.儘量在索引中完成排序
4.只取出本身須要的column,而不是*
5.使用最有效的過濾條件
6.用錶鏈接代替子查詢
7.當只要一行數據時,使用limit 1
8.爲搜索字段創建索引
9.千萬不要ORDER BY RAND(),避免select *
10.儘量使用NOT NULL
11.開啓查詢緩存,併爲查詢緩存優化查詢語句
eg:
select username from user where add_time >= now()
注意:
1.這樣的語句不會使用查詢緩存,
2.像NOW()和RAND()或是其它的諸如此類的SQL函數都不會開啓查詢緩存,由於這些函數的返回是會不定的易變的。因此,你所須要的就是用一個變量來代替MySQL的函數,從而開啓緩存
3.修改, 對now()進行處理,只取年月日 yyyy-MM-dd,變爲一個不衣變的值
Redis的5種數據結構和使用場景
Redis的持久化機制
Redis中Hash類型的底層2種實現區別(壓縮表: 省內存 和 跳躍表:查詢更快)
Redis做爲分佈式消息隊列使用,性能和注意點
在此我向你們推薦一個Java高級羣 :725633148 裏面會分享一些資深架構師錄製的視頻錄像:(有Spring,MyBatis,Netty源碼分析,高併發、高性能、分佈式、微服務架構的原理,JVM性能優化、分佈式架構、面試資料)等這些成爲架構師必備的知識體系 進羣立刻免費領取,目前受益良多!
數據結構和算法
常見的排序算法就不說了,須要理解其原理和會寫代碼,還有時間空間複雜度也要知道
隊列、棧:須要理解其存取結構,並能在某些場景下使用
二叉樹:樹的遍歷、樹的深度、按層次輸出、平衡二叉樹、逆序打印樹等
鏈表:逆序、合併兩有序的鏈表、判斷鏈表是否又環、鏈表倒數第K個元素等
字符串:KMP算法、動態規劃(這個是重點,須要好好理解動態規劃,常見的題有:求解最長迴文子串、求解最長公共子串等)
海量數據處理:如今好多大公司都會問海量數據的處理,因此須要掌握常見的處理方法,好比Bit-map、分而治之、hash映射等,能夠百度看看相關的文章,加深理解
經常使用算法
冒泡排序
快速排序
插入排序
希爾排序
歸併排序
堆排序
桶排序
動態規劃
最長公共子串
最長迴文子串
數組的最大k個值
數字的最大連續子數組之和
左旋轉字符串
字符串匹配算法:KMP算法
二分查找
鏈表
單鏈表逆序
兩個有序單鏈表合併
兩個單鏈表是否相交
相交處的節點
單鏈表倒數第K個數
單鏈表排序
棧和隊列
設計包含min函數的棧
兩個隊列實現棧
兩個棧實現隊列
一個數組實現棧和隊列
樹
前序、中序、後續遍歷
求二叉樹的深度
按層次遍歷二叉樹
判斷二叉樹是否爲徹底二叉樹
判斷二叉樹是否鏡面對稱
判斷兩顆樹是否相等
設計模式6大原則
單一職責原則(SRP)
定義:就一個類而言,應該僅有一個引發它變化的緣由。
從這句定義咱們很難理解它的含義,通俗講就是咱們不要讓一個類承擔過多的職責。若是一個類承擔的職責過多,就等於把這些職責耦合在一塊兒,一個職責的變化可能會削弱或者抑制這個類完成其餘職責的能力。這種耦合會致使脆弱的設計,當變化發生時,設計會遭受到破壞。
好比我常常看到一些Android開發在Activity中寫Bean文件,網絡數據處理,若是有列表的話Adapter 也寫在Activity中,問他們爲何除了好找也沒啥理由了,把他們拆分到其餘類豈不是更好找,若是Activity過於臃腫行數過多,顯然不是好事,若是咱們要修改Bean文件,網絡處理和Adapter都須要上這個Activity來修改,就會致使引發這個Activity變化的緣由太多,咱們在版本維護時也會比較頭疼。也就嚴重違背了定義「就一個類而言,應該僅有一個引發它變化的緣由」。
固然若是想爭論的話,這個模式是能夠引發不少爭論的,但請記住一點,你寫代碼不僅是爲了你也是爲了其餘人。
開放封閉原則(ASD)
定義:類、模塊、函數等等等應該是能夠拓展的,可是不可修改。
開放封閉有兩個含義,一個是對於拓展是開放的,另外一個是對於修改是封閉的。對於開發來講需求確定是要變化的,可是新需求一來,咱們就要把類從新改一遍這顯然是使人頭疼的,因此咱們設計程序時面對需求的改變要儘量的保證相對的穩定,儘可能用新代碼實現拓展來修改需求,而不是經過修改原有的代碼來實現。
假設咱們要實現一個列表,一開始只有查詢的功能,若是產品又要增長添加功能,過幾天又要增長刪除功能,大多數人的作法是寫個方法而後經過傳入不一樣的值來控制方法來實現不一樣的功能,可是若是又要新增功能咱們還得修改咱們的方法。用開發封閉原則解決就是增長一個抽象的功能類,讓增長和刪除和查詢的做爲這個抽象功能類的子類,這樣若是咱們再添加功能,你會發現咱們不須要修改原有的類,只須要添加一個功能類的子類實現功能類的方法就能夠了。
3.里氏替換原則(LSP)
定義:全部引用基類(父類)的地方必須能透明地使用其子類的對象
里氏代換原則告訴咱們,在軟件中將一個基類對象替換成它的子類對象,程序將不會產生任何錯誤和異常,反過來則不成立,若是一個軟件實體使用的是一個子類對象的話,那麼它不必定可以使用基類對象。
里氏代換原則是實現開閉原則的重要方式之一,因爲使用基類對象的地方均可以使用子類對象,所以在程序中儘可能使用基類類型來對對象進行定義,而在運行時再肯定其子類類型,用子類對象來替換父類對象。
在使用里氏代換原則時須要注意以下幾個問題:
子類的全部方法必須在父類中聲明,或子類必須實現父類中聲明的全部方法。根據里氏代換原則,爲了保證系統的擴展性,在程序中一般使用父類來進行定義,若是一個方法只存在子類中,在父類中不提供相應的聲明,則沒法在以父類定義的對象中使用該方法。
咱們在運用里氏代換原則時,儘可能把父類設計爲抽象類或者接口,讓子類繼承父類或實現父接口,並實如今父類中聲明的方法,運行時,子類實例替換父類實例,咱們能夠很方便地擴展系統的功能,同時無須修改原有子類的代碼,增長新的功能能夠經過增長一個新的子類來實現。里氏代換原則是開閉原則的具體實現手段之一。
Java語言中,在編譯階段,Java編譯器會檢查一個程序是否符合里氏代換原則,這是一個與實現無關的、純語法意義上的檢查,但Java編譯器的檢查是有侷限的。
4.依賴倒置原則(DIP)
定義:高層模塊不該該依賴低層模塊,兩個都應該依賴於抽象。抽象不該該依賴於細節,細節應該依賴於抽象。
在Java中,抽象就是指接口或者抽象類,二者都是不能直接被實例化的;細節就是實現類,實現接口或者繼承抽象類而產生的就是細節,也就是能夠加上一個關鍵字new產生的對象。高層模塊就是調用端,低層模塊就是具體實現類。
依賴倒置原則在Java中的表現就是:模塊間經過抽象發生,實現類之間不發生直接依賴關係,其依賴關係是經過接口或者抽象類產生的。若是類與類直接依賴細節,那麼就會直接耦合,那麼當修改時,就會同時修改依賴者代碼,這樣限制了可擴展性。
5.迪米特原則(LOD)
定義:一個軟件實體應當儘量少地與其餘實體發生相互做用。
也稱爲最少知識原則。若是一個系統符合迪米特法則,那麼當其中某一個模塊發生修改時,就會盡可能少地影響其餘模塊,擴展會相對容易,這是對軟件實體之間通訊的限制,迪米特法則要求限制軟件實體之間通訊的寬度和深度。迪米特法則可下降系統的耦合度,使類與類之間保持鬆散的耦合關係。
迪米特法則要求咱們在設計系統時,應該儘可能減小對象之間的交互,若是兩個對象之間沒必要彼此直接通訊,那麼這兩個對象就不該當發生任何直接的相互做用,若是其中的一個對象須要調用另外一個對象的某一個方法的話,能夠經過第三者轉發這個調用。簡言之,就是經過引入一個合理的第三者來下降現有對象之間的耦合度。
在將迪米特法則運用到系統設計中時,要注意下面的幾點:在類的劃分上,應當儘可能建立鬆耦合的類,類之間的耦合度越低,就越有利於複用,一個處在鬆耦合中的類一旦被修改,不會對關聯的類形成太大波及;在類的結構設計上,每個類都應當儘可能下降其成員變量和成員函數的訪問權限;在類的設計上,只要有可能,一個類型應當設計成不變類;在對其餘類的引用上,一個對象對其餘對象的引用應當降到最低。
6.接口隔離原則(ISP)
定義:一個類對另外一個類的依賴應該創建在最小的接口上。
創建單一接口,不要創建龐大臃腫的接口,儘可能細化接口,接口中的方法儘可能少。也就是說,咱們要爲各個類創建專用的接口,而不要試圖去創建一個很龐大的接口供全部依賴它的類去調用。
採用接口隔離原則對接口進行約束時,要注意如下幾點:
接口儘可能小,可是要有限度。對接口進行細化能夠提升程序設計靈活性,可是若是太小,則會形成接口數量過多,使設計複雜化。因此必定要適度。
爲依賴接口的類定製服務,只暴露給調用的類它須要的方法,它不須要的方法則隱藏起來。只有專一地爲一個模塊提供定製服務,才能創建最小的依賴關係。
提升內聚,減小對外交互。使接口用最少的方法去完成最多的事情。
concurrent包的實現基礎
因爲java的CAS同時具備 volatile 讀和volatile寫的內存語義,所以Java線程之間的通訊如今有了下面四種方式:
A線程寫volatile變量,隨後B線程讀這個volatile變量。
A線程寫volatile變量,隨後B線程用CAS更新這個volatile變量。
A線程用CAS更新一個volatile變量,隨後B線程用CAS更新這個volatile變量。
A線程用CAS更新一個volatile變量,隨後B線程讀這個volatile變量。
Java的CAS會使用現代處理器上提供的高效機器級別原子指令,這些原子指令以原子方式對內存執行讀-改-寫操做,這是在多處理器中實現同步的關鍵(從本質上來講,可以支持原子性讀-改-寫指令的計算機器,是順序計算圖靈機的異步等價機器,所以任何現代的多處理器都會去支持某種能對內存執行原子性讀-改-寫操做的原子指令)。同時,volatile變量的讀/寫和CAS能夠實現線程之間的通訊。把這些特性整合在一塊兒,就造成了整個concurrent包得以實現的基石。若是咱們仔細分析concurrent包的源代碼實現,會發現一個通用化的實現模式:
首先,聲明共享變量爲volatile;
而後,使用CAS的原子條件更新來實現線程之間的同步;
同時,配合以volatile的讀/寫和CAS所具備的volatile讀和寫的內存語義來實現線程之間的通訊。
AQS,非阻塞數據結構和原子變量類(java.util.concurrent.atomic包中的類),這些concurrent包中的基礎類都是使用這種模式來實現的,而concurrent包中的高層類又是依賴於這些基礎類來實現的。
在此我向你們推薦一個Java高級羣 :725633148 裏面會分享一些資深架構師錄製的視頻錄像:(有Spring,MyBatis,Netty源碼分析,高併發、高性能、分佈式、微服務架構的原理,JVM性能優化、分佈式架構、面試資料)等這些成爲架構師必備的知識體系 進羣立刻免費領取,目前受益良多!