java面試題(上)

 
1:  自我介紹
  自我介紹首先描述本身的基本狀況,其次是描述本身的技術亮點,作過的亮點項目或產品。若是沒有作過有技術亮點的事,天天都在作增刪改查功能或重複性的工做,須要好好反思下,這樣下去技術上沒有多少增加。若是工做中就是作這個怎麼辦?能夠考慮利用業餘時間參與開源項目或本身作一些工具或框架。
2:  HashMap怎麼解決Hash衝突的
  一、HashMap的數據結構是:數組Node[](桶)與鏈表Node中有next Node.(繼承map.entity:hashcode,key,value,next)
  二、在put元素時,先根據key的hashCode從新計算出hash值,而後再根據hash值找出這個元素在數組中的位置,若是數組的此處已經存放了數據,那麼這個元素會以鏈表的形式保存,新的在鏈頭,舊的在鏈尾(經過hash,key對比來判別兩個節點是否相同);
  三、若是數組的此處沒有存放數據,則直接將該元素保存在數組的此位置上。
  四、大於增加因子或一個鏈表超過8時,經過resize擴充一倍容量,前者是爲了擴充容量,後者是爲了將鏈表上的值經過hash取模放到別的桶上去。
  五、當列表長度超過8時則將桶轉爲紅黑樹(hashmap:當鏈表的長度超過8且桶的大小超過64=4*16)。
   ps:
    二叉樹:
      一、每一個節點最多隻有兩顆子樹,即節點的度最大爲2
      二、左子樹和右子樹是有順序的,次序不能顛倒
      三、即便某個節點只有一個子樹,也要區分左右子樹
    二叉查詢樹(BST):
      一、節點的左子樹所包含的節點的值都小於該節點的值。
      二、節點的右子樹所包含的節點的值都大於等於該節點的值。
      三、左子樹和右子樹也都是二叉查詢樹
      四、優勢:查詢方便;缺點:以便會變瘸,如7-》6-》5-》4

 

 

    平衡二叉樹:
      一、任何節點的左右子樹的高度之差的絕對值最可能是1

 

    紅黑樹:一種近似平衡二叉查找樹,在進行插入和刪除時經過特定操做保持二叉樹的平衡,從而得到更高的查找性能。
      一、節點是紅色或者黑色
      二、跟節點是黑色的
      三、每一個葉子節點都是黑色的空節點
      四、每一個紅色節點的兩個子節點都是黑色(根到葉子,不存在兩個連續的紅色節點)
      五、從任一節點到該節點的每一個葉子節點的全部路徑都包含相同數目的黑色節點
      六、經過着色和旋轉(左旋/右旋)修正更新後的紅黑樹,插入的節點必定是紅色的。
        a、若是父類是黑色則不影響
        b、若是父類是紅色則要進行旋轉並修改父類的顏色爲黑色

 

 

    
3:  ConcurrentHashMap怎麼解決線程安全
  一、Segment分段鎖功能,每個Segment 都想的於小的hash table而且都有本身鎖,只要修改再也不同一個段上就不會引發併發問題。
  二、Segment繼承了ReentrantLock,會根據hash獲取指定的分段鎖。
  三、1.8的分段實現是直接將鎖加在段中第一個元素上。
 
4:  常見的排序有沒有了解過
排序方法        平均狀況        最好狀況        最壞狀況        輔助空間        穩定性
冒泡排序         O(n^2)           O(n)              O(n^2)            O(1)                穩定
選擇排序         O(n^2)          O(n^2)            O(n^2)            O(1)              不穩定
插入排序         O(n^2)           O(n)              O(n^2)            O(1)                穩定
希爾排序O(n*log(n))~O(n^2) O(n^1.3)       O(n^2)            O(1)              不穩定
堆排序          O(n*log(n))     O(n*log(n))    O(n*log(n))       O(1)              不穩定
歸併排序       O(n*log(n))     O(n*log(n))    O(n*log(n))       O(n)                穩定
快速排序       O(n*log(n))     O(n*log(n))      O(n^2)            O(1)              不穩定
 
冒泡排序通過優化之後,最好時間複雜度能夠達到O(n)。設置一個標誌位,若是有一趟比較中沒有發生任何交換,可提早結束,所以在正序狀況下,時間複雜度爲O(n)。
選擇排序在最壞和最好狀況下,都必須在剩餘的序列中選擇最小(大)的數,與已排好序的序列後一個位置元素作交換,依次最好和最壞時間複雜度均爲O(n^2)。
插入排序是在把已排好序的序列的後一個元素插入到前面已排好序(須要選擇合適的位置)的序列中,在正序狀況下時間複雜度爲O(n)。
堆是徹底二叉樹,所以樹的深度必定是log(n)+1,最好和最壞時間複雜度均爲O(n*log(n))。
歸併排序是將大數組分爲兩個小數組,依次遞歸,至關於二叉樹,深度爲log(n)+1,所以最好和最壞時間複雜度都是O(n*log(n))。
快速排序在正序或逆序狀況下,每次劃分只獲得比上一次劃分少一個記錄的子序列,用遞歸樹畫出來,是一棵斜樹,此時須要n-1次遞歸,且第i次劃分要通過n-i次關鍵字比較才能找到第i個記錄,所以時間複雜度是\sum_{i=1}^{n-1}(n-i)=n(n-1)/2,即O(n^2)。
 
5:  一堆基本有序的數組,用哪一種排序效率最高
  歸併排序
 
6:  JDK1.6到JDK1.8 GC上面最大作了什麼變化
  一、去除了方法區,將常量、靜態變量移到了堆中;將元數據移到了本地緩存中,稱元空間。
  二、原來每一個程序都有本身的永久代,如今移到本地緩存中,能夠實現多個程序的共享,提升了內存的利用率。
  三、默認狀況下,元空間的大小受內存限制。經過-XX:MetaspaceSize等設置大小
  好處:
    一、元空間能夠動態增加了,再也不是固定的了。
    二、提升了內存的利用率。
    三、它由元空間虛擬機管理,和原來的不一樣收集器回收相比,如今使用類加載器來判斷過時的對象(類加載器標記不在存活則其加載的類也被釋放)
   PS:元空間虛擬機負責元空間的分配,其採用的形式爲組塊分配。組塊的大小因類加載器的類型而異。在元空間虛擬機中存在一個全局的空閒組塊列表。當一個類加載器須要組塊時,它就會從這個全局的組塊列表中獲取並維持一個本身的組塊列表。當一個類加載器再也不存活,那麼其持有的組塊將會被釋放,並返回給全局組塊列表。類加載器持有的組塊又會被分紅多個塊,每個塊存儲一個單元的元信息。組塊中的塊是線性分配(指針碰撞分配形式)。組塊分配自內存映射區域。這些全局的虛擬內存映射區域以鏈表形式鏈接,一旦某個虛擬內存映射區域清空,這部份內存就會返回給操做系統
 
7:  CMS怎麼進行垃圾收集的
  CMS(Concurrent Mark Sweep)是一種以獲取最短回收停頓時間爲目標的收集器,工做在老年代,基於「標記-清除」算法實現,整個過程分爲如下4步:
  1. 初始標記:這個過程只是標記如下GC Roots可以直接關聯的對象,可是仍然會Stop The World;
  2. 併發標記:進行GC Roots Tracing的過程,能夠和用戶線程一塊兒工做。
  3. 從新標記:用於修正併發標記期間因爲用戶程序繼續運行而致使標記產生變更的那部分記錄,這個過程會暫停全部線程,但其停頓時間遠比並發標記的時間短;
  4. 併發清理:能夠和用戶線程一塊兒工做。
  CMS收集器的缺點:
  1. 對CPU資源比較敏感,在併發階段,雖然不會致使用戶線程停頓,可是會佔用一部分線程資源,下降系統的總吞吐量。
  2. 沒法處理浮動垃圾,在併發清理階段,用戶線程的運行依然會產生新的垃圾對象,這部分垃圾只能在下一次GC時收集。
  3. CMS是基於標記-清除算法實現的,意味着收集結束後會形成大量的內存碎片,可能致使出現老年代剩餘空間很大,卻沒法找到足夠大的連續空間分配當前對象,不得不提早觸發一次Full GC。
 
  JDK1.5實現中,當老年代空間使用率達到68%時,就會觸發CMS收集器,若是應用中老年代增加不是太快,能夠經過-XX:CMSInitiatingOccupancyFraction參數提升觸發百分比,從而下降內存回收次數提升系統性能。
  JDK1.6實現中,觸發CMS收集器的閾值已經提高到92%,要是CMS運行期間預留的內存沒法知足用戶線程須要,會出現一次」Concurrent Mode Failure」失敗,這是虛擬機會啓動Serial Old收集器對老年代進行垃圾收集,固然,這樣應用的停頓時間就更長了,因此這個閾值也不能設置的過高,若是致使了」Concurrent Mode Failure」失敗,反而會下降性能,至於如何設置這個閾值,還得長時間的對老年代空間的使用狀況進行監控。
 
 
8:  G1怎麼進行垃圾收集的
  G1(Garbage First)是JDK1.7提供的一個工做在新生代和老年代的收集器,基於「標記-整理」算法實現,在收集結束後能夠避免內存碎片問題。
  G1優勢:
  1. 並行與併發:充分利用多CPU來縮短Stop The World的停頓時間;
  2. 分代收集:不須要其餘收集配合就能夠管理整個Java堆,採用不一樣的方式處理新建的對象、已經存活一段時間和經歷過屢次GC的對象獲取更好的收集效果;
  3. 空間整合:與CMS的」標記-清除」算法不一樣,G1在運行期間不會產生內存空間碎片,有利於應用的長時間運行,且分配大對象時,不會致使因爲沒法申請到足夠大的連續內存而提早觸發一次Full GC;
  4. 停頓預測:G1中能夠創建可預測的停頓時間模型,能讓使用者明確指定在M毫秒的時間片斷內,消耗在垃圾收集上的時間不得超過N毫秒。
  

 

  使用G1收集器時,Java堆的內存佈局與其餘收集器有很大區別,整個Java堆會被劃分爲多個大小相等的獨立區域Region,新生代和老年代再也不是物理隔離了,都是一部分Region(不須要連續)的集合。G1會跟蹤各個Region的垃圾收集狀況(回收空間大小和回收消耗的時間),維護一個優先列表,根據容許的收集時間,優先回收價值最大的Region,避免在整個Java堆上進行全區域的垃圾回收,確保了G1收集器能夠在有限的時間內儘量收集更多的垃圾。
  不過問題來了:使用G1收集器,一個對象分配在某個Region中,能夠和Java堆上任意的對象有引用關係,那麼如何斷定一個對象是否存活,是否須要掃描整個Java堆?其實這個問題在以前收集器中也存在,若是回收新生代的對象時,不得不一樣時掃描老年代的話,會大大下降Minor GC的效率。
  針對這種狀況,虛擬機提供了一個解決方案:G1收集器中Region之間的對象引用關係和其餘收集器中新生代與老年代之間的對象引用關係被保存在Remenbered Set數據結構中,用來避免全堆掃描。G1中每一個Region都有一個對應的Remenbered Set(記錄誰引用了它),當虛擬機發現程序對Reference類型的數據進行寫操做時,會產生一個Write Barrier暫時中斷寫操做,檢查Reference引用的對象是否處於相同的Region中,若是不是,則經過CardTable把相關引用信息記錄到被引用對象所屬Region的Remenbered Set中。
   PS:回收過程
    一、初始化標記:G1 GC 對根進行標記
    二、根區域掃描:在初始標記的存活區掃描對老年代的引用,並標記被引用的對象
    三、併發標記:在整個堆中查找可訪問的(存活的)對象
    四、最終標記:和GMS的從新標記類似
    五、篩選回收:對個Region的回收價值和成本進行派去,根據用戶所指望的GC停頓時間制定回收計劃。
   參見:https://www.cnblogs.com/ASPNET2008/p/6496481.html
9:  G1相比於CMS有哪些優點
  一、回收算法不一樣:與CMS的」標記-清除」算法不一樣,G1能夠看出是基於「複製」算法的,在運行期間不會產生內存空間碎片,有利於應用的長時間運行,且分配大對象時,不會致使因爲沒法申請到足夠大的連續內存而提早觸發一次Full GC;
 
 
10: 哪些狀況會致使Full GC
   一、full gc會將對中全部垃圾對象清理掉,perm區的被卸載的classloader中加載的類的數據;它是相對minor gc來講的,後者是eden去空間不足是觸發,除了回收eden區不活動的對象,還會把老對象複製到old區。
      二、System.gc()方法的調用
      三、老年代代或永生區空間空間不足
      四、統計獲得的Minor GC晉升到舊生代的平均大小大於老年代的剩餘空間
      五、CMS GC時出現promotion failed和concurrent mode failure
    a、promotion failed:在進行Minor GC時,Survivor Space放不下,對象只能放入老年代,而此時老年代也放不下形成的
    b、concurrent mode failure:執行CMS GC的過程當中同時業務線程將對象放入老年代,而此時老年代空間不足,或者在作Minor GC的時候,新生代Survivor空間放不下,須要放入老年代,而老年代也放不下而產生的
      六、堆中分配很大的對象
   ps:和full gc、minor gc 並列的還有major gc,它專門用來清理老年代
11: 新new的對象放在哪裏
    一、對象內容和數組類型這些須要動態申請內存的存放在堆區。
    二、基本類型和對象引用存放棧區。
    三、堆區是在運行過程當中會變化的
 
12: 哪些東西放在棧區(虛擬機棧)
  一、棧區即虛擬機棧,是線程私有的。棧會爲每一個線程建立一個棧幀,每一個方法從調用到執行完成的過程,就對應棧幀一個棧幀在虛擬機中的入棧到出棧的過程。
       二、棧幀中存儲局部變量表,操做數棧,動態連接、方法出入口等信息。
    三、局部變量表存放基本數據類型、及對象引用類型和returnAddress類型(程序return後須要執行的指令引用,也是一種基本數據類型),這些數據也是操做過程當中動態加載進來的。
    四、操做數棧,當對兩個值進行算術運算時要先加載到這個棧中,結果值也是先放到這,結束時才放到局部變量表中。
  五、動態連接:方法字節碼的引用
  六、方法出入口信息
  七、棧區的內容都是建立時就能肯定的
  ps:
  1.寄存器:最快的存儲區, 由編譯器根據需求進行分配,咱們在程序中沒法控制;
  1. 棧:存放基本類型 的變量數據和對象的引用,但對象自己不存放在棧中,而是存放在堆(new出來的對象)或者常量池中(字符串常量對象存放的常量池中),局部變量【注意:(方法中的局部變量使用final修飾後,放在堆中,而不是棧中)】
  2.堆:存放使用new建立的對象,全局變量
  3. 靜態域(方法區):存放靜態成員(static定義的)
  4. 常量池(方法區):字符串常量和基本類型常量(public static final)。有時,在嵌入式系統中,常量自己會和其餘部分分割離開(因爲版權等其餘緣由),因此在這種狀況下,能夠選擇將其放在ROM中 ;
  5. 非RAM存儲:硬盤等永久存儲空間
 
13: 雙親委派模型, 有什麼好處
  全盤負責和雙親委託加載,原則:由下向上詢問加載,由上向下嘗試加載。
  分類:引導類加載器、擴展類加載器、系統類加載器、用戶自定義類加載器;線程上下文加載器
  雙親委派模型的優勢:
  一、構造一種加載順序,防止不可信類扮演可信任的類。  
  二、類重用,避免重複加載
14: wait和sleep有什麼區別
 
 一、來自不一樣的類:,sleep來自Thread類,和wait來自Object類,且sleep是Thread的靜態類方法。
 二、對鎖的操做:最主要是sleep方法沒有釋放鎖,而wait方法釋放了鎖。
 三、喚醒方式不一樣:sleep本身醒了,wait須要同一個監視器上操做喚醒
 三、使用範圍:wait,notify和notifyAll是操控對象監視器的,因此只能在同步控制方法或者同步控制塊裏面使用,而sleep能夠在任何地方使用 
     synchronized(x){ 
        x.notify() 
       //或者wait() 
     }
 
15: 線程池幾個參數
 
 
ThreadPoolExecutor mExecutor = new ThreadPoolExecutor(
                corePoolSize,// 核心線程數                          
                maximumPoolSize, // 最大線程數                          
                keepAliveTime, // 閒置線程存活時間                          
                TimeUnit.MILLISECONDS,// 時間單位                         
                new LinkedBlockingDeque<Runnable>(),// 線程隊列                          
                Executors.defaultThreadFactory(),// 線程工廠                          
                new AbortPolicy()// 隊列已滿,並且當前線程數已經超過最大線程數時的異常處理策略                 
        );  

 

線程池任務執行流程:
  1. 當線程池小於corePoolSize時,新提交任務將建立一個新線程執行任務,即便此時線程池中存在空閒線程。
  2. 當線程池達到corePoolSize時,新提交任務將被放入workQueue中,等待線程池中任務調度執行
  3. 當workQueue已滿,且maximumPoolSize>corePoolSize時,新提交任務會建立新線程執行任務
  4. 當提交任務數超過maximumPoolSize時,新提交任務由RejectedExecutionHandler處理
  5. 當線程池中超過corePoolSize線程,空閒時間達到keepAliveTime時,關閉空閒線程
  6. 當設置allowCoreThreadTimeOut(true)時,線程池中corePoolSize線程空閒時間達到keepAliveTime也將關閉
16: 怎麼評估線程數大小 
  1、cup滿載角度
    一、衡量標準有三個:cpu個數、等待時間、計算時間(主要是計算所在的比重P)、CPU的利用率、線程數T
    二、目的:防止CPU過載,也就是利用率超過100%
    三、使用的公式:T=C/P
    四、CUP計算時間:ManagementFactory.getThreadMXBean().getCurrentThreadCpuTime(),計算時間能夠經過執行CPU開始減結束時間得出
    五、等待時間:
      a、要預熱省去加載類什麼的時間(不計入統計範圍),而後調用垃圾回收器
         b、固定時間範圍內一直執行任務,最後拿中的事件減去CPU時間就是等待實現。但過程當中要考慮線程切換等耗時
    六、線程等待時間所佔比例越高,須要越多線程。線程CPU時間所佔比例越高,須要越少線程
  2、內存容量角度
    一、計算每一個人物的內存大小,Runtime.getRuntime().totalMemory()
    ps:CPU是受操做系統調用的,BIO只會形成線程阻塞。
17: 幾個線程訪問同一個東西,怎麼保證安全
  1、若是都是讀取:無所謂了
  2、若是一個寫多個讀
    一、volatile
    二、讀寫鎖
  3、即讀又寫
    一、同步鎖
    二、併發鎖
  通常說來,確保線程安全的方法有這幾個:競爭與原子操做、同步與鎖、可重入、過分優化。
 
  18: Spring幾個特色說下
  1--核心容器
    核心容器提供spring框架的基本功能,核心容器的主要組件是BeanFactory, 他是工廠模式的實現. 
    BeanFactory使用控制反轉(IOC)模式將應用程序的配置和依賴性與實際的應用程序代碼分開
  2--Spring上下文
    是一個配置文件,該配置文件向spring框架提供上下文信息
  3--Spring AOP
    經過配置管理特性,Spring AOP 模塊直接將面向切面(方面)編程功能集成到spring框架中
  4--spring DAO
    JDBC DAO抽象層提供了有意義的已成層次結構, 可用該結構管理異常處理和不一樣數據庫拋出的錯誤信息,極大的下降了異常代碼數量
  5--Spring ORM
    spring框架插入了若干個ORM框架, 從而提供了ORM的對象工具,其中包括了Hibernate, Mybatis
  6--Spring Web
    web上下文模塊創建在應用程序上下文模塊之上,爲基於web的應用程序提供上下文
  7--Spring MVC
    該框架是一個全功能的構建web應用程序的MVC實現. 經過策略接口,MVC框架變成高度可配置的. MVC容納了大量視圖技術. 其中包括JSP、Velocity和POI
  Spring 框架的好處
    spring是最大的工廠
      spring負責業務邏輯組件的框架和生成, 並管理業務邏輯組件的生命週期 
    spring能夠生產全部實例, 從控制器、 業務邏輯組件、 持久層組件
  Spring特色
    1--下降了組件之間的耦合性, 實現了軟件各個層之間的解耦
    2--能夠使用spring容器提供的服務, 如: 事務管理, 消息服務
    3--容器提供單例模式支持
    4--容器提供AOP技術, 利用它很容易實現權限攔截, 運行期監控
    5--容器提供了衆多的輔助類, 能加快應用的開發(org.springframework.jdbc.core.JDBCTemplate 等)
    6--spring對主流的應用框架提供了集成支持, 例如: hibernate,JPA, Struts, Mybatis(IBatis)
    7--Spring屬於低侵入式設計, 代碼污染度極低
    8--獨立於各類應用服務器
    9--spring的DI機制下降了業務對象替換的複雜性
    10--spring的高度開發性, 並不強制應用徹底依賴於spring, 開發者能夠自由選擇spring的部分或者所有
19: CGLib有沒有了解過(CGlib是什麼? )
  CGlib是一個強大的,高性能,高質量的Code生成類庫。它能夠在運行期擴展Java類與實現Java接口。 
  固然這些實際的功能是asm所提供的,asm又是什麼?Java字節碼操控框架,具體是什麼你們能夠上網查一查,畢竟咱們這裏所要討論的是cglib, 
  cglib就是封裝了asm,簡化了asm的操做,實現了在運行期動態生成新的class。 
  可能你們還感受不到它的強大,如今就告訴你。 
  實際上CGlib爲spring aop提供了底層的一種實現;爲hibernate使用cglib動態生成VO/PO (接口層對象)。 
  CGLIB是一個功能強大,高性能的代碼生成包。它爲沒有實現接口的類提供代理,爲JDK的動態代理提供了很好的補充。一般能夠使用Java的動態代理建立代理,但當要代理的類沒有實現接口或者爲了更好的性能,CGLIB是一個好的選擇。
  CGLIB原理:動態生成一個要代理類的子類,子類重寫要代理的類的全部不是final的方法。在子類中採用方法攔截的技術攔截全部父類方法的調用,順勢織入橫切邏輯。它比使用java反射的JDK動態代理要快。
  CGLIB底層:使用字節碼處理框架ASM,來轉換字節碼並生成新的類。不鼓勵直接使用ASM,由於它要求你必須對JVM內部結構包括class文件的格式和指令集都很熟悉。
  CGLIB缺點:對於final方法,沒法進行代理。
 
 20: Spring支持哪幾種切片
  1.前置加強(@Before):org.springframework.aop.BeforeAdvice表明前置加強,由於Spring只支持方法級的加強,因此MethodBeforeAdvice是目前可的的前置加強,表示在目標方法執行前實施加強,而BeforeAdvice是爲了未來版本擴展須要而定義的;  
  2.後置加強(@After):org.springframework.aop.AfterReturningAdvice表明後加強,表示在目標方法執行後實施加強;
    a、在方法執行以後,還沒法獲取返回結果
  3.環繞加強(@Around):org.aopalliance.intercept.MethodInterceptor表明環繞加強,表示在目標方法執行先後實施加強;
     a、其中能夠決定是否調用原來的方法
     b、整個請求處理鏈中只會調用一次
     c、經常使用於打印日誌、設置緩存
  4.返回加強(@AfterRunning):是返回通知,能夠獲取返回結果
  5.異常拋出加強(@AfterThrowing):org.springframework.aop.ThrowsAdvice表明拋出異常加強,表示在目標方法拋出異常後實施加強;
  6.引介加強:org.springframework.aop.InteoductionInterceptor表明引介加強,表示在目標類中添加一些新的方法和屬性;
    a、新的類實現DelegatingIntroductionInterceptor的invoke接口(這類和原類能夠徹底無關係)
    b、以下配置後applicationContext中獲取getCar實例,則該對象便可轉爲car類型也可轉爲新定義類的類型
    c、1-5的都是目標方法範圍內織入,而引介加強是直接在類級別上添加目標未實現的接口方法

 

  ps:
    a、這些加強接口都有一些方法,經過實現這些接口方法,在接口方法中這義橫切邏輯,就能夠將它們織入到目標類的方法的相應鏈接點的位置。
    b、加強執行順序:環繞->前置->方法執行->環繞->後置->返回
 
21: SpringBoot和Spring有什麼區別
  Spring Boot是最近這幾年才火起來的,那麼它到底與Spring有啥區別呢?
  想了解區別,其實就是Spring Boot提供了哪些特徵:
  1. 直接運行:Spring Boot能夠創建獨立的Spring應用程序;
  2. 簡化部署:內嵌瞭如Tomcat,Jetty和Undertow這樣的容器,也就是說能夠直接跑起來,用不着再作部署工做了。
  3. 簡化配置:POM,提供的POM能夠簡化Maven的配置;XML,無需再像Spring那樣搞一堆繁瑣的xml文件的配置;
  4. 能夠自動配置Spring;
  5. 提供了一些現有的功能,如量度工具,表單數據驗證以及一些外部配置這樣的一些第三方功能;
22: SpringBoot和Spring啓動有什麼區別
  一、IDEA啓動 SpringBootApplication
  二、命令行啓動首先將命令行位置跳轉到當前項目的根目錄下,再輸入「mvn spring-boot:run」命令,初次操做maven須要下載插件等待幾分鐘。
  三、命令行編譯爲jar啓動首先命令行在當前項目根目錄運行編譯命令「mvn install」,以後跳轉到當前項目的target文件夾下(cd target)多出兩個文件
 
 
spring
  1. 首先,對於一個web應用,其部署在web容器中,web容器提供其一個全局的上下文環境,這個上下文就是ServletContext,其爲後面的spring IoC容器提供宿主環境;
  2. 其次,在web.xml中會提供有contextLoaderListener。在web容器啓動時,會觸發容器初始化事件,此時contextLoaderListener會監聽到這個事件,其contextInitialized方法會被調用,在這個方法中,spring會初始化一個啓動上下文,這個上下文被稱爲根上下文,即WebApplicationContext,這是一個接口類,確切的說,其實際的實現類是XmlWebApplicationContext。這個就是spring的IoC容器,其對應的Bean定義的配置由web.xml中的context-param標籤指定。在這個IoC容器初始化完畢後,spring以WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE爲屬性Key,將其存儲到ServletContext中,便於獲取;
  3. 再次,contextLoaderListener監聽器初始化完畢後,開始初始化web.xml中配置的Servlet,這個servlet能夠配置多個,以最多見的DispatcherServlet爲例,這個servlet其實是一個標準的前端控制器,用以轉發、匹配、處理每一個servlet請求。DispatcherServlet上下文在初始化的時候會創建本身的IoC上下文,用以持有spring mvc相關的bean。在創建DispatcherServlet本身的IoC上下文時,會利用WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE先從ServletContext中獲取以前的根上下文(即WebApplicationContext)做爲本身上下文的parent上下文。有了這個parent上下文以後,再初始化本身持有的上下文。這個DispatcherServlet初始化本身上下文的工做在其initStrategies方法中能夠看到,大概的工做就是初始化處理器映射、視圖解析等。這個servlet本身持有的上下文默認實現類也是mlWebApplicationContext。初始化完畢後,spring以與servlet的名字相關(此處不是簡單的以servlet名爲Key,而是經過一些轉換,具體可自行查看源碼)的屬性爲屬性Key,也將其存到ServletContext中,以便後續使用。這樣每一個servlet就持有本身的上下文,即擁有本身獨立的bean空間,同時各個servlet共享相同的bean,即根上下文(第2步中初始化的上下文)定義的那些bean。
 
23: Spring啓動生命週期 
 
 
 
一、Spring IoC容器容許BeanFactoryPostProcessor在容器實例化任何bean以前讀取bean的定義(配置元數據),並能夠修改它。如hsf中根據配置構造bean。
二、BeanPostProcessor,咱們想在Spring容器中完成bean實例化、配置以及其餘初始化方法先後要添加一些本身邏輯處理
   a、postProcessorBeforeInitailization方法是在bean實例化,依賴注入以後及自定義初始化方法(例如:配置文件中bean標籤添加init-method屬性指定Java類中初始化方法、@PostConstruct註解指定初始化方法(在BeanPostProcessor中執行),Java類實現InitailztingBean接口)以前調用
   b、後置處理器的postProcessorAfterInitailization方法是在bean實例化、依賴注入及自定義初始化方法以後調用
三、InstantiationAwareBeanPostProcessor是BeanPostProcessor的子接口,能夠在Bean生命週期的另外兩個時期提供擴展的回調接口,即實例化Bean以前(調用postProcessBeforeInstantiation方法)和實例化Bean以後(調用postProcessAfterInstantiation方法)
四、BeanNameAware獲取bean的名字
五、BeanFactoryAware獲取beanfactory的引用,beanfactory中通常用做調用getBean來當即實例化bean
六、InitializingBean 設置完依賴後調用
七、InstantiationAwareBeanPostProcessorAdapter是postProcessorBeforeInitailization的實現適配類,咱們通常使用adapter對其來擴展。
八、Constructor > @PostConstruct > InitializingBean > init-method ,反過來也同樣,beanPostProcessor中執行@PreDestroy
九、BeanFactoryAware後有個ApplicationContextAware
相關鏈接:
https://www.cnblogs.com/zrtqsk/p/3735273.html
https://www.cnblogs.com/april-chen/p/8182631.html
 
24: Spring註解@Resource和@Autowired區別對比  => 優先級不同
  一、@Autowired與@Resource均可以用來裝配bean. 均可以寫在字段上,或寫在setter方法上。 
  二、@Autowired默認按類型裝配(這個註解屬於Spring),默認狀況下必需要求依賴對象必須存在,若是要容許null 值,能夠設置它的required屬性爲false,如:@Autowired(required=false) ,若是咱們想使用名稱裝配能夠結合@Qualifier註解進行使用,以下: 
  @Autowired() @Qualifier("baseDao")     
  private BaseDao baseDao;    
   三、@Resource(這個註解屬於J2EE),默認按照名稱進行裝配,名稱能夠經過name屬性進行指定, 
  若是沒有指定name屬性,當註解寫在字段上時,默認取字段名進行按照名稱查找,若是註解寫在setter方法上默認取屬性名進行裝配。 當找不到與名稱匹配的bean時才按照類型進行裝配。可是須要注意的是,若是name屬性一旦指定,就只會按照名稱進行裝配。
  @Resource(name="baseDao")     
  private BaseDao baseDao;    
  我喜歡用 @Resource註解在字段上,且這個註解是屬於J2EE的,減小了與spring的耦合。最重要的這樣代碼看起就比較優雅。
 
 
25: spring @service @controller @componet 三者區別
  @controller用來定義控制層的組件 
       @service用來定義業務層的組件
       @repository用來定義持久層的組件
       @ component用來定義不在上述範圍內的通常性組件
上面組件的名稱默認是類名的首字母小寫,若是要重命名,則這樣@controller("beanName")
當在spring中配置了<context:annotation-config/> 和<context:component-scan base-package="*">時,上述四種註解的組件都會由spring容器來建立爲bean並由本身來管理.
那麼建立了上面這些組件後,又是如何來注入的呢,這時就由@autowired來配置了。
只須要在private的屬性上加上@autowired就能夠自動把接口屬性的實現類的bean注入,注意不須要setter,getter方法
上面若是一個接口屬性有兩個實現類,怎麼辦,這時就要用@qualifier來特別說明要注入哪一個bean了。
 
     26: Http和Https協議有什麼區別,證書瞭解不
  HTTPS和HTTP的區別:
    a、定義:
      http:是超文本傳輸協議,是客戶端和服務端請求和應答的標準,過程當中明文傳輸。
      http:是安全版的http,以安全傳輸爲目標,具體就是http中加入ssl層,機密傳輸。
    b、具體使用:
      http使用簡單,http則須要身份認證,須要用到數字證書
      http默認使用80端口,https則使用443
    c、證書
       是一串身份信息按照指定的格式排列後,包含證書頒發機構進行數字簽名的數據。常見的數字證書格  式X.509 V3,存儲到文件上通常一種爲不含私鑰的DER格式,以cer或者crt爲文件後綴名,另外一種爲包含私鑰的PKCS格式,以pfx或者p12爲後綴。
       數字證書中包括(不是所有):
    (1)數字證書持有者身份信息 網站就包括所對應的URL或IP
    (2)該證書擁有者對應的該證書公鑰
    (3)證書頒發者信息
    (4)證書頒發者用私鑰對證書持有者身份信息和公鑰的簽名 使用的摘要和簽名算法
   PS:
    優勢
      https可認證用戶和服務,確保發送正確客戶機和服務器
      https協議能夠確保數據傳輸過程當中的安全性和完整性。
      https構建的網站在各大搜索引擎中排名更高
    缺點:
      增長資源消耗:https協議握手階段比較費時,頁面加載時間延長50%,增長10%到20%的耗電。
      增長成本:ssl證書須要錢,ssl證書須要綁定IP
    握手:(4步)
      一、瀏覽器說「你好,我是瀏覽器」,將直接的加密算法和一個隨機值發送服務器。
      二、服務器說「你好,我是服務器」,從客戶端提供的機密算法中選擇一種,並添加一個本身生成的隨機值和本身的證書一塊兒發送瀏覽器端。此階段服務端能夠要求客戶端提供證書。
      三、客戶端收到證書後,先從ca校驗該證書的合法性,並從證書中取出服務端公鑰,而後生成一個隨機值,用服務端公鑰加密後傳給服務端。
      四、服務端使用私鑰對這個隨機值進行解密,而後將三次的隨機值拼到一塊兒做爲之後溝通的加密密鑰。
      五、此時的客戶端也會將三次隨機值拼成一個密鑰。
     使用:
      一、org.springframework.web.client.RestTemplate 封裝了一個http/https的請求操做       
      二、先構造一個SSLContext,其中設置相信策略TrustStrategy默認是true.       
      三、構造一個SSL的鏈接工廠:SSLConnectionSocketFactory
      四、將註冊到註冊中心中(Registry),並基於此構建一個客戶端連池(PoolingHttpClientConnectionManager)
      五、構造一個org.springframework.http.client.ClientHttpRequestFactory
      六、將請求工廠注入到resTemplate中
    27: 介紹下Redis設計實現
Redis是基於內存、可持久化的日誌型、Key-Value 數據庫 高性能存儲系統,
 
  
 
 
    28: Redis的細節源碼看過沒有
1. 支持5種數據結構
支持strings, hashes, lists, sets, sorted sets 
string是很好的存儲方式,用來作計數存儲。sets用於創建索引庫很是棒;
2. K-V 存儲 vs K-V 緩存
新浪微博目前使用的98%都是持久化的應用,2%的是緩存,用到了600+服務器 
Redis中持久化的應用和非持久化的方式不會差異很大: 
非持久化的爲8-9萬tps,那麼持久化在7-8萬tps左右; 
當使用持久化時,須要考慮到持久化和寫性能的配比,也就是要考慮redis使用的內存大小和硬盤寫的速率的比例計算;
3. 社區活躍
Redis目前有3萬多行代碼, 代碼寫的精簡,有不少巧妙的實現,做者有技術潔癖 
Redis的社區活躍度很高,這是衡量開源軟件質量的重要指標,開源軟件的初期通常都沒有商業技術服務支持,若是沒有活躍社區作支撐,一旦發生問題都無處求救;
Redis基本原理
redis持久化(aof) append online file: 
寫log(aof), 到必定程度再和內存合併. 追加再追加, 順序寫磁盤, 對性能影響很是小
1. 單實例單進程
Redis使用的是單進程,因此在配置時,一個實例只會用到一個CPU; 
在配置時,若是須要讓CPU使用率最大化,能夠配置Redis實例數對應CPU數, Redis實例數對應端口數(8核Cpu, 8個實例, 8個端口), 以提升併發: 
單機測試時, 單條數據在200字節, 測試的結果爲8~9萬tps;
2. Replication
過程: 數據寫到master–>master存儲到slave的rdb中–>slave加載rdb到內存。 
存儲點(save point): 當網絡中斷了, 連上以後, 繼續傳. 
Master-slave下第一次同步是全傳,後面是增量同步;、
3. 數據一致性
長期運行後多個結點之間存在不一致的可能性; 
開發兩個工具程序: 
1.對於數據量大的數據,會週期性的全量檢查; 
2.實時的檢查增量數據,是否具備一致性;
對於主庫未及時同步從庫致使的不一致,稱之爲延時問題; 
對於一致性要求不是那麼嚴格的場景,咱們只須要要保證最終一致性便可; 
對於延時問題,須要根據業務場景特色分析,從應用層面增長策略來解決這個問題; 
例如: 
1.新註冊的用戶,必須先查詢主庫; 
2.註冊成功以後,須要等待3s以後跳轉,後臺此時就是在作數據同步。
    29: Redis分佈式緩存
1.架構設計
因爲redis是單點,項目中須要使用,必須本身實現分佈式。基本架構圖以下所示:
 
 
2.分佈式實現
經過key作一致性哈希,實現key對應redis結點的分佈。
一致性哈希的實現:
l        hash值計算:經過支持MD5與MurmurHash兩種計算方式,默認是採用MurmurHash,高效的hash計算。
l        一致性的實現:經過java的TreeMap來模擬環狀結構,實現均勻分佈
3.client的選擇
對於jedis修改的主要是分區模塊的修改,使其支持了跟據BufferKey進行分區,跟據不一樣的redis結點信息,能夠初始化不一樣的ShardInfo,同時也修改了JedisPool的底層實現,使其鏈接pool池支持跟據key,value的構造方法,跟據不一樣ShardInfos,建立不一樣的jedis鏈接客戶端,達到分區的效果,供應用層調用
4.模塊的說明
l        髒數據處理模塊,處理失敗執行的緩存操做。
l        屏蔽監控模塊,對於jedis操做的異常監控,當某結點出現異常可控制redis結點的切除等操做。
整個分佈式模塊經過hornetq,來切除異常redis結點。對於新結點的增長,也能夠經過reload方法實現增長。(此模塊對於新增結點也能夠很方便實現)
對於以上分佈式架構的實現知足了項目的需求。另外使用中對於一些比較重要用途的緩存數據能夠單獨設置一些redis結點,設定特定的優先級。另外對於緩存接口的設計,也能夠跟據需求,實現基本接口與一些特殊邏輯接口。對於cas相關操做,以及一些事物操做能夠經過其watch機制來實現。(參考我之前寫的 redis事物介紹
    30: 線程在頻繁的Full GC 怎麼排查
咱們知道Full GC的觸發條件大體狀況有如下幾種狀況: 
1. 程序執行了System.gc() //建議jvm執行fullgc,並不必定會執行 
2. 執行了jmap -histo:live pid命令 //這個會當即觸發fullgc 
3. 在執行minor gc的時候進行的一系列檢查
a、執行Minor GC的時候,JVM會檢查老年代中最大連續可用空間是否大於了當前新生代全部對象的總大小。b、若是大於,則直接執行Minor GC(這個時候執行是沒有風險的)。c、若是小於了,JVM會檢查是否開啓了空間分配擔保機制,若是沒有開啓則直接改成執行Full GC。d、若是開啓了,則JVM會檢查老年代中最大連續可用空間是否大於了歷次晉升到老年代中的平均大小,若是小於則執行改成執行Full GC。e、若是大於則會執行Minor GC,若是Minor GC執行失敗則會執行Full GC
a、使用了大對象 //大對象會直接進入老年代
b、在程序中長期持有了對象的引用 //對象年齡達到指定閾值也會進入老年代
對於咱們的狀況,能夠初步排除a,b兩種狀況,最有多是d和e這兩種狀況。爲了進一步排查緣由,咱們在線上開啓了 -XX:+HeapDumpBeforeFullGC。
JVM在執行dump操做的時候是會發生stop the word事件的,也就是說此時全部的用戶線程都會暫停運行。 爲了在此期間也能對外正常提供服務,建議採用分佈式部署,並採用合適的負載均衡算法
 
 
1)FULL GC先後Java堆大小有變化;經研究發現是因爲Java應用JVM參數XMS設置爲默認值,在咱們的系統環境下,Hotspot的Xms默認值爲50M(-Xms默認是物理內存的1/64);每次GC時,JVM會根據各類條件調節Java堆的大小,Java堆的取值範圍爲[Xms, Xmx]。根據以上分析,修改Xms值與Xmx相等,這樣就不會由於所使用的Java堆不夠用而進行調節,通過測試後發現FULL GC次數從四位數減小至個位數。
 
2)關鍵詞「System」讓我想到了System.gc調用,System.gc調用只是建議JVM執行年老代GC,而年老代GC觸發FULL GC,JVM會根據系統條件決定是否執行FULL GC,正由於系統條件很差判斷,因此很難構造System.gc調用觸發FULL GC,幾經周折終於成功,當System.gc觸發FULL  GC時都會有關鍵詞「(System)」,而 JVM自動觸發的FULL GC卻不帶關鍵詞「(System)」,能夠判定是Java應用存在「System.gc」代碼。通過本次測試我也發現System.gc的真正含義,通俗言之,「System.gc」 就是FULL GC觸發的最後一根稻草。 
 
從本次分析中,咱們能夠得出以下的經驗: 
1)Java應用的jvm參數Xms與Xmx保持一致,避免因所使用的Java堆內存不夠致使頻繁full gc以及full gc中因動態調節Java堆大小而耗費延長其週期。 
2)建議不要調用System.gc或者Runtime.getRuntime().gc,不然本次調用可能會成爲「壓死駱駝的最後一根稻草」。固然咱們能夠經過設置jvm參數禁止這種調用生效,可是除非特別有把握該參數有必要添加,不然不推薦這麼設置。
    31: JVM一些工具,jps, jmap
 -v 輸出傳遞給JVM的參數
$> jps -v
23789 BossMain
28802 Jps -Denv.class.path=/data/aoxj/bossbi/twsecurity/java/trustwork140.jar:/data/aoxj/bossbi/twsecurity/java/:/data/aoxj/bossbi/twsecurity/java/twcmcc.jar:/data/aoxj/jdk15/lib/rt.jar:/data/aoxj/jdk15/lib/tools.jar -Dapplication.home=/data/aoxj/jdk15 -Xms8m
23651 Resin -Xss1m -Dresin.home=/data/aoxj/resin -Dserver.root=/data/aoxj/resin -Djava.util.logging.manager=com.caucho.log.LogManagerImpl -Djavax.management.builder.initial=com.caucho.jmx.MBeanServerBuilderImpl
 
jmap
JVM Memory Map命令用於生成heap dump文件,若是不使用這個命令,還能夠使用-XX:+HeapDumpOnOutOfMemoryError參數來讓虛擬機出現OOM的時候自動生成dump文件。 jmap不只能生成dump文件,還能夠查詢finalize執行隊列、Java堆和永久代的詳細信息,如當前使用率、當前使用的是哪一種收集器等。【內存分析】
  
 
32:  海量日誌數據,提取出某日訪問百度次數最多的那個IP?

此題,在我以前的一篇文章算法裏頭有所提到,當時給出的方案是:IP的數目仍是有限的,最多2^32個,因此能夠考慮使用hash將ip直接存入內存,而後進行統計。html

  再詳細介紹下此方案:首先是這一天,而且是訪問百度的日誌中的IP取出來,逐個寫入到一個大文件中。注意到IP是32位的,最多有個2^32個IP。一樣能夠採用映射的方法,好比模1000,把整個大文件映射爲1000個小文件,再找出每一個小文中出現頻率最大的IP(能夠採用hash_map進行頻率統計,而後再找出頻率最大的幾個)及相應的頻率。而後再在這1000個最大的IP中,找出那個頻率最大的IP,即爲所求。  前端

   
34:  垃圾回收的原理
     優勢:a.不須要考慮內存管理, b.能夠有效的防止內存泄漏,有效的利用可以使用的內存, c.因爲有垃圾回收機制,Java中的對象再也不有"做用域"的概念,只有對象的引用纔有"做用域"
     原理:垃圾回收器是做爲一個單獨的低級別的線程運行,在不可知的狀況下對內存堆中已死亡的或者長期沒有使用的對象回收,可是不能實時的對某一對象或者全部對象進行垃圾回收。
     垃圾回收機制:分代複製垃圾回收、標記垃圾回收、增量垃圾回收
    35:  你寫過Java的Web系統
 
    36:  簡單介紹一下你的項目
 
    37:  兩個有序的數組,合成一個有序的數組,怎麼合併效率高 
歸併排序
    38:  淘寶的登錄頁面,怎麼保證他安全

使用哈希加鹽法來爲密碼加密
      解決的辦法是將密碼加密後再存儲進數據庫,比較經常使用的加密方法是使用哈希函數(Hash Function)。哈希函數的具體定義,你們能夠在網上或者相關書籍中查閱到,簡單地說,它的特性以下:  
     (1)原始密碼經哈希函數計算後獲得一個哈希值  
     (2)改變原始密碼,哈希函數計算出的哈希值也會相應改變 
     (3) 一樣的密碼,哈希值也是相同的 
     (4) 哈希函數是單向、不可逆的。也就是說從哈希值,你沒法推算出原始的密碼是多少 
    最簡單、常見的破解方式當屬字典破解(Dictionary Attack)和暴力破解(Brute Force Attack)方式。這兩種方法說白了就是猜密碼。

    字典破解和暴力破解都是效率比較低的破解方式。若是你知道了數據庫中密碼的哈希值,你就能夠採用一種更高效的破解方式,查表法(Lookup Tables)。還有一些方法,好比逆向查表法(Reverse Look     up Tables)、彩虹表(Rainbow Tables)等,都和查表法大同小異。如今咱們來看一下查表法的原理。java

     查表法不像字典破解和暴力破解那樣猜密碼,它首先將一些比較經常使用的密碼的哈希值算好,而後創建一張表,固然密碼越多,這張表就越大。當你知道某個密碼的哈希值時,你只須要在你創建好的表中查找       該哈希值,若是找到了,你就知道對應的密碼了。node

      從上面的查表法能夠看出,即使是將原始密碼加密後的哈希值存儲在數據庫中依然是不夠安全的。那麼有什麼好的辦法來解決這個問題呢?答案是加鹽。mysql

      鹽(Salt)是什麼?就是一個隨機生成的字符串。咱們將鹽與原始密碼鏈接(concat)在一塊兒(放在前面或後面均可以),而後將concat後的字符串加密。採用這種方式加密密碼,查表法就不靈了(由於鹽是隨機生成的)。 nginx

     單單使用哈希函數來爲密碼加密是不夠的,須要爲密碼加鹽來提升安全性,鹽的長度不能太短,而且鹽的產生應該是隨機的。git

 
    39:  你有最新半年用戶的訂單,天天的用戶訂單量有上億,預測下將來一週哪些商品最容易被購買
數據建模-分析
   40: 你有啥問題
「入職後有沒有培訓活動?」
「公司對個人指望是什麼?」
「這個部門或團隊有多少人?主要是負責哪方面的?」
 
一、synchronized關鍵字原理?
    原理:synchronized底層是經過一個monitor的對象阻塞和獲取。
    對代碼同步:指令執行時,monitor的進入數減1,若是減1後進入數爲0,那線程退出monitor,再也不是這個monitor的全部者。其餘被這個monitor阻塞的線程能夠嘗試去獲取這個 monitor 的全部權。 
    對方法同步:常量池中多了ACC_SYNCHRONIZED標示符。JVM就是根據該標示符來實現方法的同步的:當方法調用時,調用指令將會檢查方法的 ACC_SYNCHRONIZED 訪問標誌是否被設置,若是設置了,執行線程將先獲取monitor,獲取成功以後才能執行方法體,方法執行完後再釋放monitor。在方法執行期間,其餘任何線程都沒法再得到同一個monitor對象。
    重量級鎖:Mutex Lock 監視器鎖monitor本質就是依賴於底層的操做系統的Mutex Lock來實現的。
    
 
二、hashMap底層實現。
  • HashMap 的基本組成成員
        首先,HashMap 是 Map 的一個實現類,它表明的是一種鍵值對的數據存儲形式。Key 不容許重複出現,Value 隨意。jdk 8 以前,其內部是由數組+鏈表來實現的,而 jdk 8 對於鏈表長度超過 8 的鏈表將轉儲爲紅黑樹。大體的數據存儲形式以下:
    
  從上圖中能夠看出,HashMap底層就是一個數組結構,數組中的每一項又是一個鏈表。當新建一個HashMap的時候,就會初始化一個數組。
   源碼以下:
Java代碼  
  1. /** 
  2.  * The table, resized as necessary. Length MUST Always be a power of two. 
  3.  */  
  4. transient Entry[] table;  
  5.   
  6. static class Entry<K,V> implements Map.Entry<K,V> {  
  7.     final K key;  
  8.     V value;  
  9.     Entry<K,V> next;  
  10.     final int hash;  
  11.     ……  
  12. }  
   能夠看出,Entry就是數組中的元素,每一個 Map.Entry 其實就是一個key-value對,它持有一個指向下一個元素的引用,這就構成了鏈表。
 
  • put 方法的具體實現
Java代碼  
  1. public V put(K key, V value) {  
  2.     // HashMap容許存放null鍵和null值。  
  3.     // 當key爲null時,調用putForNullKey方法,將value放置在數組第一個位置。  
  4.     if (key == null)  
  5.         return putForNullKey(value);  
  6.     // 根據key的keyCode從新計算hash值。  
  7.     int hash = hash(key.hashCode());  
  8.     // 搜索指定hash值在對應table中的索引。  
  9.     int i = indexFor(hash, table.length);  
  10.     // 若是 i 索引處的 Entry 不爲 null,經過循環不斷遍歷 e 元素的下一個元素。  
  11.     for (Entry<K,V> e = table[i]; e != null; e = e.next) {  
  12.         Object k;  
  13.         if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {  
  14.             V oldValue = e.value;  
  15.             e.value = value;  
  16.             e.recordAccess(this);  
  17.             return oldValue;  
  18.         }  
  19.     }  
  20.     // 若是i索引處的Entry爲null,代表此處尚未Entry。  
  21.     modCount++;  
  22.     // 將key、value添加到i索引處。  
  23.     addEntry(hash, key, value, i);  
  24.     return null;  
  25. }  
      當咱們往HashMap中put元素的時候,先根據key的hashCode從新計算hash值,根據hash值獲得這個元素在數組中的位置(即下標),若是數組該位置上已經存放有其餘元素了,那麼在這個位置上的元素將以鏈表的形式存放,新加入的放在鏈頭,最早加入的放在鏈尾。若是數組該位置上沒有元素,就直接將該元素放到此數組中的該位置上。
    
    
 
  • remove 方法的具體實現。
        採用迭代器遍歷,不只適用於HashMap,對其它類型的容器一樣適用。
   採用這種方法的遍歷,能夠用下文說起的方式安全地對HashMap內的元素進行修改,並不會對後續的刪除操做形成影響。
  若是使用foreach遍歷方法刪除HashMap中的元素,Java頗有可能會在運行時拋出異常。
    爲何呢?
    一、使用iterator迭代刪除時沒有問題的,在每一次迭代時都會調用hasNext()方法判斷是否有下一個,是容許集合中數據增長和減小的。
    二、使用forEach刪除時,會報錯ConcurrentModificationException,由於在forEach遍歷時,是不容許map元素進行刪除和增長。
    因此,遍歷刪除map集合中元素時,必須使用迭代iterator
 
 
 
 
  • 爲何時HashMap的容量老是2的n次方?
    若是不是2的2次冪,空間浪費至關大,更糟的是這種狀況中,數組能夠使用的位置比數組長度小了不少,這意味着進一步增長了碰撞的概率,減慢了查詢的效率!而當數組長度爲16時,即爲2的n次方時,2n-1獲得的二進制數的每一個位上的值都爲1,這使得在低位上&時,獲得的和原hash的低位相同,加之hash(int h)方法對key的hashCode的進一步優化,加入了高位計算,就使得只有相同的hash值的兩個值纔會被放到數組中的同一個位置上造成鏈表。
    
    
    
 
  • 其餘一些基本方法的基本介紹
 
   HashMap 包含以下幾個構造器:
   HashMap():構建一個初始容量爲 16,負載因子爲 0.75 的 HashMap。
   HashMap(int initialCapacity):構建一個初始容量爲 initialCapacity,負載因子爲 0.75 的 HashMap。
   HashMap(int initialCapacity, float loadFactor):以指定初始容量、指定的負載因子建立一個 HashMap。
   HashMap的基礎構造器HashMap(int initialCapacity, float loadFactor)帶有兩個參數,它們是初始容量initialCapacity和加載因子loadFactor。
   initialCapacity:HashMap的最大容量,即爲底層數組的長度。
   loadFactor:負載因子loadFactor定義爲:散列表的實際元素數目(n)/ 散列表的容量(m)。
 
 
  • 什麼是紅黑樹
紅黑樹本質上是一種二叉查找樹,但它在二叉查找樹的基礎上額外添加了一個標記(顏色),同時具備必定的規則。這些規則使紅黑樹保證了一種平衡,插入、刪除、查找的最壞時間複雜度都爲 O(logn)。
 
 
 
三、TCP與UDP的區別
  一、基於鏈接與無鏈接
  二、TCP要求系統資源較多,UDP較少; 
  三、UDP程序結構較簡單 
  四、流模式(TCP)與數據報模式(UDP); 
  五、TCP保證數據正確性,UDP可能丟包 
  六、TCP保證數據順序,UDP不保證 
 
 
四、TCP三次握手說一下。
 
簡單說,讓雙方都證明對方能發收。
知道對方能收是由於收到對方的由於收到而發的迴應。
具體:
1:A發,B收, B知道A能發
2:B發,A收, A知道B能發收
3:A發,B收, B知道A能收
 
五、看你項目用到線程池,說一下線程池工做原理,任務拒接策略有哪幾種?
一個線程從被提交(submit)到執行共經歷如下流程:
  • 線程池判斷核心線程池裏是的線程是否都在執行任務,若是不是,則建立一個新的工做線程來執行任務。若是核心線程池裏的線程都在執行任務,則進入下一個流程
  • 線程池判斷工做隊列是否已滿。若是工做隊列沒有滿,則將新提交的任務儲存在這個工做隊列裏。若是工做隊列滿了,則進入下一個流程。
  • 線程池判斷其內部線程是否都處於工做狀態。若是沒有,則建立一個新的工做線程來執行任務。若是已滿了,則交給飽和策略來處理這個任務。
 
任務拒接策略?
 有4種內置的實現策略和一個用戶自定義拒絕策略。
AbortPolicy       爲java線程池默認的阻塞策略,不執行此任務,並且直接拋出一個運行時異常,切記ThreadPoolExecutor.execute須要try catch,不然程序會直接退出。 
DiscardPolicy        直接拋棄,任務不執行,空方法 。
DiscardOldestPolicy   從隊 列裏面拋棄head的一個任務,並再次execute 此task。
CallerRunsPolicy        在調用execute的線程裏面執行此command,會阻塞入口 。 
用戶自定義拒絕策略   實現RejectedExecutionHandler,並本身定義策略模式。
 
再次須要注意的是,ThreadPoolExecutor.submit() 函數,此方法內部調用的execute方法,並把execute執行完後的結果給返回,但若是任務並無執行的話(被拒絕了),則submit返回的future.get()會一直等到。
future 內部其實仍是一個runnable,並把command給封裝了下,當command執行完後,future會返回一個值。
 
 
六、進程和線程的區別?
進程和線程的主要差異在於它們是不一樣的操做系統資源管理方式。進程有獨立的地址空間,一個進程崩潰後,在保護模式下不會對其它進程產生影響,而線程只是一個進程中的不一樣執行路徑。線程有本身的堆棧和局部變量,但線程之間沒有單獨的地址空間,一個線程死掉就等於整個進程死掉,因此多進程的程序要比多線程的程序健壯,但在進程切換時,耗費資源較大,效率要差一些。但對於一些要求同時進行而且又要共享某些變量的併發操做,只能用線程,不能用進程。
 
1) 簡而言之,一個程序至少有一個進程,一個進程至少有一個線程.
2) 線程的劃分尺度小於進程,使得多線程程序的併發性高。
3) 另外,進程在執行過程當中擁有獨立的內存單元,而多個線程共享內存,從而極大地提升了程序的運行效率。
4) 線程在執行過程當中與進程仍是有區別的。每一個獨立的線程有一個程序運行的入口、順序執行序列和程序的出口。可是線程不可以獨立執行,必須依存在應用程序中,由應用程序提供多個線程執行控制。
5) 從邏輯角度來看,多線程的意義在於一個應用程序中,有多個執行部分能夠同時執行。但操做系統並無將多個線程看作多個獨立的應用,來實現進程的調度和管理以及資源分配。這就是進程和線程的重要區別。
 
 
七、ArrayList與LinkedList的區別?
  一、ArrayList是實現了基於動態數組的數據結構,LinkedList基於鏈表的數據結構。 (LinkedList是雙向鏈表,有next也有previous)
  二、對於隨機訪問get和set,ArrayList以爲優於LinkedList,由於LinkedList要移動指針。 
  三、對於新增和刪除操做add和remove,LinedList比較佔優點,由於ArrayList要移動數據。 
 
 
 
八、線程安全與非線程安全集合說一下,底層怎麼實現的(hashmap,concurrenthashmap)?
     Hashmap本質是數組加鏈表。根據key取得hash值,而後計算出數組下標,若是多個key對應到同一個下標,就用鏈表串起來,新插入的在前面。
     ConcurrentHashMap:在hashMap的基礎上,ConcurrentHashMap將數據分爲多個segment,默認16個(concurrency level),而後每次操做對一個segment加鎖,避免多線程鎖的概率,提升併發效率。
    
 
九、Hashtable、ConcurrentHashMap、TreeMap、HashMap的key,value都是不爲空的嗎?
 
HashMap的key和value都容許爲空,treeMap的value容許爲空。
 
十、單例模式  
  •  有幾種實現方式 ?
        5種  
1.餓漢模式(調用效率高,可是不能延時加載):
2.懶漢模式(調用效率不高,可是能延時加載):
3.雙重檢測鎖模式(因爲JVM底層模型緣由,偶爾會出問題,不建議使用):
4.靜態內部類式(線程安全,調用效率高,能夠延時加載):
5.枚舉類(線程安全,調用效率高,不能延時加載,能夠自然的防止反射和反序列化調用):
如何選用:
-單例對象 佔用資源少,不須要延時加載,枚舉 好於 餓漢
-單例對象 佔用資源多,須要延時加載,靜態內部類 好於 懶漢式 
 
  • 單例模線程安全嗎?  
  不安全 。
  • 通常如何保證它線程安全 ?  
      double-check 雙重檢查鎖定  。
  • 修飾符 volatile  有什麼做用 ?
        能保證被它修飾的成員變量能夠被多個線程正確的處理。Volatile是輕量級的synchronized,它在多處理器開發中保證了共享變量的「可見性」。可見性的意思是當一個線程修改一個共享變量時,另一個線程能讀到這個修改的值。它在某些狀況下比synchronized的開銷更小
 
十一、 判斷一個字符在一個字符串中出現的次數  ?
  StringUtils.countMatches(str, t);
 
十二、HashMap是否是有序的?
 
   不是有序的.
 
  • 有沒有有順序的Map實現類? 
有TreeMap和LinkedHashMap。
 
  • TreeMap和LinkedHashMap是如何保證它的順序的?
          LinkedHashMap 是根據元素增長或者訪問的前後順序進行排序,而 TreeMap是基於元素的固有順序 (由 Comparator 或者 Comparable 肯定)。
                                 
 
  • 哪一個的有序實現比較好?
      TreeMap TreeMap則實現了 SortedMap 接口。          
  • 你以爲還有沒有比它更好或者更高效的實現方式?
           參照TreeMap的value排序,咱們同樣的也能夠實現HashMap的排序。
 
1三、實現全部的線程一塊兒等待某個事件的發生,當某個事件發生時,全部線程一塊兒開始往下執行的話,有什麼好的辦法嗎?
 
柵欄(Java的併發包中的CyclicBarrier)  CountDownLatch  CyclicBarrier  Semaphore
  • CountDownLatch (N個線程數量count減爲0 主程序或一組程序開始執行)
        CountDownLatch是一個計數器閉鎖,主要的功能就是經過await()方法來阻塞住當前線程,而後等待計數器減小到0了,再喚起這些線程繼續執行。 
        這個類裏主要有兩個方法,一個是向下減計數器的方法:countdown(),若是取得當前的狀態爲0,說明這個鎖已經結束,直接返回false;
        若是沒有結束,而後去設置計數器減1,若是compareAndSetState不成功,則繼續循環執行。 而其中的一直等待計數器歸零的方法是await()。 
  • CyclicBarrier(N個線程,他們之間任何一個沒有完成,全部的線程都必須等待) 
        CyclicBarrier是加計數方式,計數達到構造方法中參數指定的值時釋放全部等待的線程。
 
  • Semaphore(Semaphore 是隻容許必定數量的線程同時執行一段任務。)
            Semaphore,每次semaphore.acquire(),獲取一個資源,每次semaphore.acquire(n),獲取n個資源,
            當達到semaphore 指定資源數量時就不能再訪問線程處於阻塞,必須等其它線程釋放資源,semaphore.relase()每次資源一個資源,
            semaphore.relase(n)每次資源n個資源。
            
你知道它的實現原理嗎?
繼續問,你還知道其它的實現方式嗎?
 
繼續問,你以爲這些方式裏哪一個方式更好?
 
若是讓你來寫的話,你以爲還有比它更好的實現方式嗎?
 
1四、IO包和NIO包 熟悉嗎?
  • NIO模型 其中的selector 職責和實現原理
傳統的socket IO中,須要爲每一個鏈接建立一個線程,當併發的鏈接數量很是巨大時,線程所佔用的棧內存和CPU線程切換的開銷將很是巨大。使用NIO,再也不須要爲每一個線程建立單獨的線程,能夠用一個含有限數量線程的線程池,甚至一個線程來爲任意數量的鏈接服務。因爲線程數量小於鏈接數量,因此每一個線程進行IO操做時就不能阻塞,若是阻塞的話,有些鏈接就得不處處理,NIO提供了這種非阻塞的能力。
一、增長了一個角色,要有一個專門負責收集客人需求的人。NIO裏對應的就是Selector。
二、由阻塞服務方式改成非阻塞服務了,客人吃着的時候服務員不用一直侯在客人旁邊了。傳統的IO操做,好比read(),當沒有數據可讀的時候,線程一直阻塞被佔用,直到數據到來。NIO中沒有數據可讀時,read()會當即返回0,線程不會阻塞。
     NIO中,客戶端建立一個鏈接後,先要將鏈接註冊到Selector,至關於客人進入餐廳後,告訴前臺你要用餐,前臺會告訴你你的桌號是幾號,而後你就可能到那張桌子坐下了,SelectionKey就是桌號。當某一桌須要服務時,前臺就記錄哪一桌須要什麼服務,好比1號桌要點菜,2號桌要結賬,服務員從前臺取一條記錄,根據記錄提供服務,完了再來取下一條。這樣服務的時間就被最有效的利用起來了。
  • NIO的核心是什麼 (Selector)
Selector類是NIO的核心類,Selector可以檢測多個註冊的通道上是否有事件發生,若是有事件發生,便獲取事件而後針對每一個事件進行相應的響應處理。這樣一來,只是用一個單線程就能夠管理多個通道,也就是管理多個鏈接。這樣使得只有在鏈接真正有讀寫事件發生時,纔會調用函數來進行讀寫,就大大地減小了系統開銷,而且沒必要爲每一個鏈接都建立一個線程,不用去維護多個線程,而且避免了多線程之間的上下文切換致使的開銷。
  與Selector有關的一個關鍵類是SelectionKey,一個SelectionKey表示一個到達的事件,這2個類構成了服務端處理業務的關鍵邏輯。
 
1五、虛擬機JVM 組成部分
    
程序計數器
指示當前程序執行到了哪一行,執行JAVA方法時紀錄正在執行的虛擬機字節碼指令地址;執行本地方法時,計數器值爲undefined
虛擬機棧
用於執行JAVA方法。棧幀存儲局部變量表、操做數棧、動態連接、方法返回地址和一些額外的附加信息。程序執行時棧幀入棧;執行完成後棧幀出棧
本地方法棧
用於執行本地方法,其它和虛擬機棧相似
着重說一下虛擬機棧中的局部變量表,裏面存放了三個信息:
  • 各類基本數據類型(boolean、byte、char、short、int、float、long、double)
  • 對象引用(reference)
  • returnAddress地址
這個returnAddress和程序計數器有什麼區別?前者是指示JVM的指令執行到哪一行,後者則是你的代碼執行到哪一行。
私有內存區伴隨着線程的產生而產生,一旦線程停止,私有內存區也會自動消除,所以討論的內存回收主要是針對共享內存區。
JAVA堆
既然GC主要發生在堆內存中,這部分咱們會對堆內存進行比較詳細的描述。
堆內存是由存活和死亡的對象組成的。存活的對象是應用能夠訪問的,不會被垃圾回收。死亡的對象是應用不可訪問尚且尚未被垃圾收集器回收掉的對象。一直到垃圾收集器把這些對象回收掉以前,他們會一直佔據堆內存空間。堆是應用程序在運行期請求操做系統分配給本身的向高地址擴展的數據結構,是不連續的內存區域。用一句話總結堆的做用:程序運行時動態申請某個大小的內存空間。 
 
新生代:剛剛新建的對象在Eden中,經歷一次Minor GC,Eden中的存活對象就會被移動到第一塊survivor space S0,Eden被清空;等Eden區再滿了,就再觸發一次Minor GC,Eden和S0中的存活對象又會被複制送入第二塊survivor space S1。S0和Eden被清空,而後下一輪S0與S1交換角色,如此循環往復。若是對象的複製次數達到16次,該對象就會被送到老年代中。
  • 爲何新生代要分出兩個survivor區?
        設置兩個Survivor區最大的好處就是解決了碎片化
老年代:若是某個對象經歷了幾回垃圾回收以後還存活,就會被存放到老年代中。老年代的空間通常比新生代大。
 
GC名稱
介紹
Minor GC
發生在新生代,頻率高,速度快(大部分對象活不過一次Minor GC)
Major GC
發生在老年代,速度慢
Full GC
清理整個堆空間
  • java中垃圾回收機制?
JAVA 並無給咱們提供明確的代碼來標註一塊內存並將其回收。或許你會說,咱們能夠將相關對象設爲 null 或者用 System.gc()。然而,後者將會嚴重影響代碼的性能,由於通常每一次顯式的調用 system.gc() 都會中止全部響應,去檢查內存中是否有可回收的對象。這會對程序的正常運行形成極大的威脅。另外,調用該方法並不能保證 JVM 當即進行垃圾回收,僅僅是通知 JVM 要進行垃圾回收了,具體回收與否徹底由 JVM 決定。這樣作是費力不討好。
 
  • 垃圾回收算法概述
    一、追蹤回收算法(tracing collector) 
    從根結點開始遍歷對象的應用圖。同時標記遍歷到的對象。遍歷完成後,沒有被標記的對象就是目前未被引用,能夠被回收。
    二、壓縮回收算法(Compacting Collector) 
    把堆中活動的對象集中移動到堆的一端,就會在堆的另外一端流出很大的空閒區域。這種處理簡化了消除碎片的工做,但可能帶來性能的損失。
    三、複製回收算法(Coping Collector) 
把堆均分紅兩個大小相同的區域,只使用其中的一個區域,直到該區域消耗完。此時垃圾回收器終端程序的執行,經過遍歷把全部活動的對象複製到另外一個區域,複製過程當中它們是緊挨着佈置的,這樣也能夠達到消除內存碎片的目的。複製結束後程序會繼續運行,直到該區域被用完。 
可是,這種方法有兩個缺陷:
對於指定大小的堆,須要兩倍大小的內存空間,
須要中斷正在執行的程序,下降了執行效率
    四、按代回收算法(Generational Collector) 
    爲何要按代進行回收?這是由於不一樣對象生命週期不一樣,每次回收都要遍歷全部存活對象,對於整個堆內存進行回收無疑浪費了大量時間,對症下藥能夠提升垃圾回收的效率。主要思路是:把堆分紅若搞個子堆,每一個子堆視爲一代,算法在運行的過程當中優先收集「年幼」的對象,若是某個對象通過屢次回收仍然「存活」,就移動到高一級的堆,減小對其掃描次數。
  •     垃圾回收器有哪些?
    串行回收器(serial collector)
    並行回收器
    CMS回收器
    G1回收器
    
    
    JAVA性能優化
    真正影響JAVA程序性能的,就是碎片化。碎片是JAVA堆內存中的空閒空間,多是TLAB剩餘空間,也多是被釋放掉的具備較長生命週期的小對象佔用的空間。
  1. 減小new對象。每次new對象以後,都要開闢新的內存空間。這些對象不被引用以後,還要回收掉。所以,若是最大限度地合理重用對象,或者使用基本數據類型替代對象,都有助於節省內存;
  2. 多使用局部變量,減小使用靜態變量。局部變量被建立在棧中,存取速度快。靜態變量則是在堆內存;
  3. 避免使用finalize,該方法會給GC增添很大的負擔;
  4. 若是是單線程,儘可能使用非多線程安全的,由於線程安全來自於同步機制,同步機制會下降性能。例如,單線程程序,能使用HashMap,就不要用HashTable。同理,儘可能減小使用synchronized
  5. 用移位符號替代乘除號。eg:a*8應該寫做a<<3
  6. 對於常常反覆使用的對象使用緩存;
  7. 儘可能使用基本類型而不是包裝類型,儘可能使用一維數組而不是二維數組;
  8. 儘可能使用final修飾符,final表示不可修改,訪問效率高
  9. 單線程狀況下(或者是針對於局部變量),字符串儘可能使用StringBuilder,比StringBuffer要快;
  10. String爲何慢?由於String 是不可變的對象, 所以在每次對 String 類型進行改變的時候其實都等同於生成了一個新的 String 對象,而後將指針指向新的 String 對象。若是不能保證線程安全,儘可能使用StringBuffer來鏈接字符串。這裏須要注意的是,StringBuffer的默認緩存容量是16個字符,若是超過16,apend方法調用私有的expandCapacity()方法,來保證足夠的緩存容量。所以,若是能夠預設StringBuffer的容量,避免append再去擴展容量。若是能夠保證線程安全,就是用StringBuilder。
 
 
 
1六、ArrayList遍歷時正確刪除元素?
 
刪除元素請使用Iterator方式,若是併發操做,須要對Iterator對象加鎖。
 
1七、對一個List的進行subList後,原有list進行增、刪、改,再操做subList會怎樣?
 
     子 List 的元素和原 List 中的後一部分是重合的, 而子 List 還在遍歷過程當中時, 向原 List 中新增元素, 這樣給子 List 的遍歷過程形成了干擾甚至困擾, 因而就拋出了併發修改異常將會拋出java.util.ConcurrentModificationException
 
 
1八、web應用安全問題?
    一、跨站腳本攻擊(CSS or XSS, Cross Site Scripting) 
     方案:輸入或輸出時對其進行字符過濾或轉義處理。
  2、SQL注入攻擊(SQL injection)
方案:輸入輸出都是過濾、合法性檢查和長度限制等通用方法。
  3、遠程命令執行(Code execution,我的以爲譯成代碼執行並不確切) 
     方案:嚴格限制運行Web服務的用戶權限。
  4、目錄遍歷(Directory traversal) 
    方案:一、一樣是限制Web應用在服務器上的運行  2、進行嚴格的輸入驗證,控制用戶輸入非法路徑。
  5、文件包含(File inclusion)
    方案:對文件來源進行審查
  6、腳本代碼暴露(Script source code disclosure) 
  7、Http請求頭的額外的回車換行符注入(CRLF injection/HTTP response splitting)
  8、跨幀腳本攻擊(Cross Frame Scripting)
  9、PHP代碼注入(PHP code injection)
  十、XPath injection
  十一、Cookie篡改(Cookie manipulation)
  十二、URL重定向(URL redirection)
  1三、Blind SQL/XPath injection for numeric/String inputs
  1四、Google Hacking
1五、表單、AJAX提交必須執行CSRF安全過濾。
1六、URL外部重定向傳入的目標地址必須執行白名單過濾。
 
1九、簡單介紹下spring的ioc和aop?
  •  控制反轉(Inversion of Control,英文縮寫爲IOC);
    ioc就是典型的工廠模式,經過sessionfactory去注入實例。依賴注入    。
  本身實現用什麼方式?    反射原理  
其實就是經過解析xml文件,經過反射建立出咱們所須要的bean,再將這些bean挨個放到集合中,而後對外提供一個getBean()方法,以便咱們得到這bean。
通俗來說就如同婚姻介紹所,只須要告訴它找個什麼樣的女友,而後婚介就會按照咱們的要求,提供一個mm,若是婚介給咱們的人選不符合要求,咱們就會拋出異常。
 
  •  面向切面編程(Aspect Oriented Programming,英文縮寫爲AOP)
    AOP就是典型的代理模式的體現。 實現攔截器  日誌 統一權限校驗 。
    spring的IoC容器是spring的核心,spring AOP是spring框架的重要組成部分。
    
 實現AOP的技術,主要分爲兩大類:一是採用動態代理技術,利用截取消息的方式,對該消息進行裝飾,以取代原有對象行爲的執行;二是採用靜態織入的方式,引入特定的語法建立「方面」,從而使得編譯器能夠在編譯期間織入有關「方面」的代碼.簡單點解釋,比方說你想在你的biz層全部類中都加上一個打印‘你好’的功能,這時就能夠用aop思想來作.你先寫個類寫個類方法,方法經實現打印‘你好’,而後Ioc這個類 ref=「biz.*」讓每一個類都注入便可實現。
 
20、併發問題 
丟失更新  用戶A把6改爲2  用戶B把2改爲6  則用戶A丟失了更新
髒讀問題  用戶A,B 看到的都是6  用戶B把6改成2  則用戶A讀的值仍然是6
 
2一、樂觀鎖  悲觀鎖
 
悲觀  屏蔽一切違反數據操做完整性
樂觀  只是在提交的時候檢查是否違反數據完整性
 
2二、sql優化
    • 複雜sql避免模糊匹配
    • 索引問題 惟一索引  和普通索引
    • 複雜操做
    • 在能夠使用UNION ALL的語句裏,使用了UNION  
    • 字段長度小於5000用varchar,超過用TEXT,獨立一張表,用主鍵來對應。
    • 在varchar字段上創建索引時,必須指定索引長度,不必對全字段創建索引。
 
2四、介紹下使用的持久層框架? 爲何要選擇這個(有什麼好處)?
MyBatis 是支持普通 SQL查詢,存儲過程和高級映射的優秀持久層框架。MyBatis 消除了幾乎全部的JDBC代碼和參數的手工設置以及結果集的檢索。
MyBatis 使用簡單的 XML或註解用於配置和原始映射,將接口和 Java 的POJOs(Plain Old Java Objects,普通的 Java對象)映射成數據庫中的記錄。
 
2五、mybatis  $和#的區別
  一、效果來看:SELECT * FROM USER WHERE ID = #{id} /${id} ,從效果上來看均可以替換成功,只不過前置默認會把傳入的參數當成字符串處理(它還能夠經過,jdbcType=Number等指明類型),後者只是簡單的替換(不加‘’號)。
  二、概念上:${}是一個動態SQL編譯,#{}是預編譯
  三、功能上:#{}由於是預編譯,因此能夠防止SQL注入,但${}不能。
  四、使用上:#{}經常使用來處理參數傳入,${}用來處理動態SQL構造,如select * from ${} 
  五、原則:儘量的使用#{}
  PS:
  1. #將傳入的數據都當成一個字符串,會對自動傳入的數據加一個雙引號。如:order by #user_id#,若是傳入的值是111,那麼解析成sql時的值爲order by "111", 若是傳入的值是id,則解析成的sql爲order by "id". 
        2. $將傳入的數據直接顯示生成在sql中。如:order by $user_id$,若是傳入的值是111,那麼解析成sql時的值爲order by user_id,  若是傳入的值是id,則解析成的sql爲order by id. 
        3. #方式可以很大程度防止sql注入。 
        4.$方式沒法防止Sql注入。 
        5.$方式通常用於傳入數據庫對象,例如傳入表名.  
        6.通常能用#的就別用$. 
2六、HashMap和Hashtable有什麼區別?
     HashMap和Hashtable都實現了Map接口,所以不少特性很是類似。可是,他們有如下不一樣點:
     HashMap容許鍵和值是null,而Hashtable不容許鍵或者值是null。
     Hashtable是同步的,而HashMap不是。所以,HashMap更適合於單線程環境,而Hashtable適合於多線程環境。
     HashMap提供了可供應用迭代的鍵的集合,所以,HashMap是快速失敗的。另外一方面,Hashtable提供了對鍵的列舉(Enumeration)。
     通常認爲Hashtable是一個遺留的類。
2七、hashCode()和equals()方法的重要性體如今什麼地方?
 
    經過hashCode和equals方法保證元素的惟一性,當重寫equals方法時,必須重寫hashCode方法,由於若是不重寫這兩個方法,就會默認使用Object的方法,通常是不相同的,因此就會致使存儲了重複值,與hashset、hashmap等性質衝突。
 
 
2八、Vector、ArrayList和LinkedList有什麼區別?
 
ArrayList和LinkedList都實現了List接口,他們有如下的不一樣點:
 
ArrayList是基於索引的數據接口,它的底層是數組。它能夠以O(1)時間複雜度對元素進行隨機訪問。與此對應,LinkedList是以元素列表的形式存儲它的數據,每個元素都和它的前一個和後一個元素連接在一塊兒,在這種狀況下,查找某個元素的時間複雜度是O(n)。
 
相對於ArrayList,LinkedList的插入,添加,刪除操做速度更快,由於當元素被添加到集合任意位置的時候,不須要像數組那樣從新計算大小或者是更新索引。
 
LinkedList比ArrayList更佔內存,由於LinkedList爲每個節點存儲了兩個引用,一個指向前一個元素,一個指向下一個元素。
 
也能夠參考ArrayList vs. LinkedList。 
 
 
2九、數據庫事務及隔離級別說一下。
  •     數據庫事務的幾個特性:
    原子性(Atomicity )、一致性( Consistency )、隔離性或獨立性( Isolation)和持久性(Durabilily),簡稱就是ACID。
  •     數據庫事務怎麼保證一致性?
    數據庫進行任何寫入操做的時候都是要先寫日誌的,一樣的道理,咱們在執行事務的時候數據庫首先會記錄下這個事務的redo操做日誌,而後纔開始真正操做數據庫,
    在操做以前,首先會把日誌文件寫入磁盤,那麼當忽然斷電的時候,即便操做沒有完成,在從新啓動數據庫時候,數據庫會根據當前數據的狀況進行undo回滾或者是redo前滾,
    這樣就保證了數據的強一致性。
  •     數據庫隔離級別:
    ① Serializable (串行化):可避免髒讀、不可重複讀、幻讀的發生。
  ② Repeatable read (可重複讀):可避免髒讀、不可重複讀的發生。
  ③ Read committed (讀已提交):可避免髒讀的發生。
  ④ Read uncommitted (讀未提交):最低級別,任何狀況都沒法保證。
 
30、synchronized和lock區別,可重入鎖與非可重入鎖的區別
  •     synchronized和lock區別:
  1. Lock是一個接口,而synchronized是Java中的關鍵字,synchronized是內置的語言實現;
  2. synchronized在發生異常時,會自動釋放線程佔有的鎖,所以不會致使死鎖現象發生;而Lock在發生異常時,若是沒有主動經過unLock()去釋放鎖,則極可能形成死鎖現象,所以使用Lock時須要在finally塊中釋放鎖;
  3. Lock可讓等待鎖的線程響應中斷,而synchronized卻不行,使用synchronized時,等待的線程會一直等待下去,不可以響應中斷;
  4. 經過Lock能夠知道有沒有成功獲取鎖,而synchronized卻沒法辦到。
  5. Lock能夠提升多個線程進行讀操做的效率。
  6. 在性能上來講,若是競爭資源不激烈,二者的性能是差很少的,而當競爭資源很是激烈時(即有大量線程同時競爭),此時Lock的性能要遠遠優於synchronized。因此說,在具體使用時要根據適當狀況選擇。
  •     可重入鎖與非可重入鎖的區別:
     可重入鎖,也叫作遞歸鎖,指的是同一線程 外層函數得到鎖以後 ,內層遞歸函數仍然有獲取該鎖的代碼,但不受影響。
在JAVA環境下 ReentrantLock 和synchronized 都是 可重入鎖。可重入鎖最大的做用是避免死鎖。
 
 
3一、aop代理模式
AOP 全稱 Aspect Oriented Programming,面向切面編程,和 OOP 同樣也是一種編程思想。AOP 出現的緣由是爲了解決 OOP 在處理 侵入性業務上的不足。
代理模式分爲靜態代理和動態代理兩種。
靜態代理:一般用於對原有業務邏輯的擴充。建立一個代理類實現和方法相同的方法,經過讓代理類持有真實對象,而後在原代碼中調用代理類方法,來達到添加咱們須要業務邏輯的目的。
動態代理:動態代理底層是使用反射實現的,是在程序運行期間動態的建立接口的實現。
 
3二、jdk1.8新特性
1. 速度更快 – 紅黑樹 
HashMap中的紅黑樹
HashMap中鏈長度大於8時採起紅黑樹的結構存儲。
紅黑樹,除了添加,效率高於鏈表結構。
 
2. 代碼更少 – Lambda 
    • Lambda表達式的基礎語法:Java8引入了一個新的操做符「->」,該操做符成爲箭頭操做符或者Lambda操做符,箭頭操做符將Lambda表達式拆分紅兩部分
    • 左側:Lambda表達式的參數列表 
    • 右側:Lambda表達式中所需執行的功能,即Lambda體。
3. 強大的Stream API – Stream 
一系列流水線式的中間操做。
流是數據渠道,用於操做數據源(集合、數組等)所生成的元素序列。
注意: 
①Stream本身不會存儲元素。 
②Stream不會改變源對象。相反,會返回持有新結果的新Stream。 
③Stream操做是延遲執行的。這意味着他們會等到須要結果的時候才執行。
 
4. 便於並行 – Parallel 
        在必要的狀況下,將一個大任務進行必要的拆分Fork成若干個小任務,再將小任務的運算結果進行Join彙總。
5. 最大化減小空指針異常 – Optional
         是一個容器類,表明一個值存在或不存在,原來用null 表示一個值不存在,如今Optional 能夠更好的表達這個概念。而且能夠避免空指針異常。
六、ConcurrentHashMap
    • Jdk1.7時隔壁級別CocnurrentLevel(鎖分段機制)默認爲16。
    • JDK1.8採起了CAS算法
    • Jdk1.8沒有永久區,取而代之的是MetaSpace元空間,用的是物理內存。
 
3三、java的4種引用 強軟弱虛
   強引用        
    a、new一個對象,強引用不會被GC回收。
    b、默認的就是強引用
  軟引用(SoftReference)、
    一、內存不足的時候會被回收
    二、會影響對象的回收

 

  弱引用(WeakReference)        
    一、當引用的對象被回收時,reference的引用爲null
    二、它不影響對象的回收

 

  弱引用與軟引用的區別在於:
    只具備弱引用的對象擁有更短暫的生命週期。        
    在垃圾回收器線程掃描它所管轄的內存區域的過程當中,一旦發現了只具備弱引用的對象,無論當前內存空間足夠與否,都會回收它的內存。
  虛引用(PhantomReference)        
    虛引用主要用來跟蹤對象被垃圾回收器回收的活動。
  虛引用與軟引用和弱引用的一個區別在於:
    虛引用必須和引用隊列 (ReferenceQueue)聯合使用。        
    當垃圾回收器準備回收一個對象時,若是發現它還有虛引用,就會在回收對象的內存以前,把這個虛引用加入到與之關聯的引用隊列中
  PS:
    一、在對應引用A設置成null調用gc時並不會立刻回收,如有對A對象的引用則更不會回收了
    二、這些引用會保存着對象設置時的值,因此並非只持有引用。
    三、threadlocal中的entity就是使用了weakreference
 
3四、分佈式服務 解決了哪些問題,本身設計一個分佈式框架 會用到哪些技術?
        須要拆分應用進行服務化,以提升開發效率,調優性能,節省關鍵競爭資源
        當服務愈來愈多時,服務的URL地址信息就會爆炸式增加,配置管理變得很是困難,F5硬件負載均衡器的單點壓力也愈來愈大。 
        當進一步發展,服務間依賴關係變得錯蹤複雜,甚至分不清哪一個應用要在哪一個應用以前啓動,架構師都不能完整的描述應用的架構關係。 
        接着,服務的調用量愈來愈大,服務的容量問題就暴露出來,這個服務須要多少機器支撐?何時該加機器?等等…
            
        用到哪些技術: dubbo(分佈式框架), zookeeper(開源的分佈式協調服務) ,redis(緩存), ssdb, nsq, nginx(負載均衡), Kafka,hessian ,RPC,netty。
            
3五、Zookeeper服務的註冊和發現?
        1. init獲取Zookeeper的服務註冊信息,並緩存在service_repos
        2. get_service_repos方法獲取實例變量service_repos
        3. get_service_endpoint根據init構建好的service_repos,以及lb_strategy提供的負載均衡策略返回某個服務的URL地址
        4. update_service_repos經過Zookeeper的Watcher機制來實時更新本地緩存service_repos
        5. heartbeat_monitor是一個心跳檢測線程,用來進行服務提供者的健康存活檢測,若是出現問題,將該服務提供者從該服務的提供者列表中移除;
        反之,則加入到服務的提供者列表中LoadBalanceStrategy定義了根據服務提供者的信息返回對應的服務Host和IP,即決定由那臺主機+端口來提供服務。
 
3六、主流的分佈式框架?
 dubbo,dubbox, spring-cloudfinaglethrift
 
3七、redis的原理 和存儲結構   持久化和非持久
 
    Redis存儲機制分紅兩種Snapshot和AOF。不管是那種機制,Redis都是將數據存儲在內存中。
    Snapshot工做原理: 是將數據先存儲在內存,而後當數據累計達到某些設定的伐值的時候,就會觸發一次DUMP操做,將變化的數據一次性寫入數據文件(RDB文件)。
    AOF 工做原理: 是將數據也是先存在內存,可是在存儲的時候會使用調用fsync來完成對本次寫操做的日誌記錄,這個日誌揭露文件實際上是一個基於Redis網絡交互協議的文本文件。
    
3八、類加載過程
JVM類加載機制分爲五個部分:加載,驗證,準備,解析,初始化,下面咱們就分別來看一下這五個過程。
 
1. 虛擬機在首次加載Java類時,會對靜態代碼塊、靜態成員變量、靜態方法進行一次初始化(靜態間按順序執行)。
2. 只有在調用new方法時纔會建立類的實例。
3. 類實例建立過程:父子繼承關係,先父類再子類。父類的靜態->子類的靜態->父類的初始化塊->父類的構造方法->子類的初始化塊->子類的構造方法
4. 類實例銷燬時候:首先銷燬子類部分,再銷燬父類部分。
 PS:
  一、加載:類加載器加載類文件
  二、驗證:確保class文件結構沒有被串改
  三、準備:爲類的講臺變量分配內存,將其初始化默認值。
  四、解析:常量池內的符號引用轉爲直接引用,將法中對其餘方法的應用緩存方法區中對內存地址的引用。
 
 
3九、String,StringBuffer,StringBuilder有什麼不一樣?
  一、線程安全方面:stringbuffer是線程安全的,其餘都不是。
  二、速度:stringbuilder是最快的。
  三、內存空間:string每次操做都會建立一個新對象(另string常量池優化),其餘都是可變類(有緩存會自增容量)
40、String和StringBuffer的實現?
4一、Volatile關鍵字做用?除了保證數據可見性,還有其餘什麼使用方式?  
  一、做用主要有兩個:保持內存的可見性,防止指令重排序
  二、保持內存可見性:經過集中原子操做完成工做內存和主內存的交互 
    a、unlock:做用於主內存,解除獨佔狀態。
    b、read:做用主內存,把一個變量的值從主內存傳輸到線程的工做內存。
    c、load:做用於工做內存,把read操做傳過來的變量值放入工做內存的變量副本中。
    d、use:做用工做內存,把工做內存當中的一個變量值傳給執行引擎。
    e、assign:做用工做內存,把一個從執行引擎接收到的值賦值給工做內存的變量。
    f、store:做用於工做內存的變量,把工做內存的一個變量的值傳送到主內存中。
    g、write:做用於主內存的變量,把store操做傳來的變量的值放入主內存的變量中。
    h、lock:做用於主內存,把變量標識爲線程獨佔狀態。
    valatile的特殊規則:
      a、read、load、use動做必須連續出現 :每次讀取前必須先從主內存刷新最新的值。
      b、assign、store、write動做必須連續出現 : 每次寫入後必須當即同步回主內存當中。
  三、防止指令重排序:編譯器在生成字節碼時,會在指令序列中插入內存屏障來禁止特定類型的處理重排序。
    a、valitile寫操做:storestore 寫操做 storeload
    b、volatile讀操做: loadload 讀操做 loadstore
   PS:
    一、內存屏障:解決指令重排序問題,多CPU的情形下能夠強制同步cpu中緩存不一致的狀況。
    二、緩存一致性原則:
    三、鎖、內存屏障與一致性
      a、只有一個cpu是,cup只會從本身的緩存中讀取數據,加入緩存丟失,從主內存讀取數據到緩存中,最終內存數據都是一致的。
      b、多核狀況下,每一個CPU都有本身的緩存,須要讀到最新數據就要解決某CPU更新了緩存端但未寫回內存,其餘cpu看不到的問題。
      c、此時就引入了緩存一致性協議(做用域緩存行):
        modifiled(該cup已經獨佔了該緩存段,並作了修改,其餘核心讀取時,此數據必須刷入主存),
        exclusive(數據已經讀入cacheline,而且只有該CPU擁有它,能夠直接修改數據)
        shared(多個cpu共享某內存的數據,當cpu須要修改數據時,須要提交RFO請求獲取數據的獨佔全,即進入exclusive狀態才能進行修改)
        invalid(share 狀態下,當前cpu贊成了其餘cpu申請寫的時候,變成該狀態)
        即:當前cpu起始狀態(shared),發起rfo,(被接收後當前cpu變成exclusive,其餘接收的cpu變成invalid),當前cpu修改了數據就變成modified(這個狀態要求讀取當前緩存段前,當前數據必須同步到主存)
      d、問題:若是當前cpu忙,沒接收別的cpu的RFO,則那個發起請求的cpu就無事可作了,這就下降了性能。
      e、爲了解決上述問題,添加了兩個相似緩存的東西,Store buffer和invalidate queue。
        這樣當cpu須要寫緩存行時就將寫指令丟入storebuff,去幹別的事,等RFO受到迴應時,該指令才執行。
        cup收到其餘核心的RFO指令後,會當即迴應但相應的失效操做指令(較忙時)會放入到invalidate queue中
      f、這套機制實現了異步,帶了性能提高的同時也帶來了問題,在發去RFO請求的cpu執行寫時(先讀store buffer,再讀緩存)只對當前CPU可見(其餘cpu可能沒有執行本身invalidate queue中的失效指令)。
      g、爲了解決這個問題,引入了讀寫屏障。寫屏障保證寫屏障前在全部store buffer的指令都真正的寫入到緩存,讀屏障保證在讀屏障前全部invalidate queue中全部的無效化指令都執行。這保證了不一樣核心上,緩存的強同步。
      h、在鎖的實現上,通常lock都會加入讀屏障,保證後續代碼能夠讀到別的cpu未回寫的緩存數據( 應該是作緩存失效吧?),而unlock會加入寫屏障,將全部未回寫的緩存回寫( 確保到modified狀態吧)。
    四、內存屏障保證緩存的一致性
  詳參:http://gocode.cc/project/9/article/128
4二、ThreadLocal作什麼的?如何使用?源碼是如何實現的?get()方法?  
  一、併發背景下,經過將相關對象封閉到執行線程中來解決併發問題,是一種以空間換時間的作法。
  二、具體實現就是爲當前執行線程維護一個ThreadLocalMap對象(維護到執行線程中,每一個線程建立一個),其中維護一個entity對象數組(繼承weakReference),其中threadlocal對象爲key,設置的對象爲value,以實現thread中維護多個threadLocal對象
  三、通常在類中使用final static 修飾
  四、get方法使用當前threadlocal對象實例從當前執行線程的threadlocalmap中獲取存放的對象。
  五、線程的threadLocalMap中之索引維護一個entity數組,是由於一個線程能夠持有多個threadlocal
  PS:
    一、threadLocalMap中維護的entity繼承了weakReference,功能就是當threadlocal被回收後就變成null->value
    二、threadLocal在get時會清除key爲null的entity
  
4三、ConcurrentHashMap源碼?JDK1.6,1.7,1.8中分別有什麼不一樣?
  一、負載因子0.75,默認容量16,當大於16*0.75時擴容一倍。
   二、1.6和1.7無太大差異,只是在new hashmap時1.6確實開闢了內存空間,1.7採用的是懶漢式,在put時才構造。
   三、1.7對待併發採用的是基於分片+鏈表數組結構,要通過兩次hash碰撞,分片是用的是可重入鎖;1.8則摒棄了分片直接使用synchronized加到桶中第一個元素上,cas用於交換元素。
   四、1.8對待增加,當鏈表長度大於8時但桶大小小於64時擴充容量,大於64時將桶轉爲紅黑樹操做。
  PS:
    一、node中的value和next都用volatile修飾。
    二、hash碰撞就是兩個對象的key的hashcode同樣,這時候如何獲取他的value。
    三、1.8中的spread方法對hash作了擴展,將高16位和當前hash作異或操做,解決選擇桶的下標時老是與低4位運算,形成的表的長度較小問題,從而減小系統的開銷(hash碰撞狀況)
    四、桶的大小超過64時,使用紅黑樹也是當發生較大碰撞時下降衝突的考慮。
4四、分佈式程序調用鏈
  全鏈路性能監控從總體維度到局部維度展現各項指標,將跨應用的全部調用鏈性能信息集中展示,可方便度量總體和局部性能,而且方便找到故障產生的源頭,生產上可極大縮短故障排除時間。
Google Dapper
  一、背景:隨着微服務的應用,業務調用鏈愈來愈複雜,一個請求可能涉及到幾十個服務的系統服務,涉及到多個團隊的業務系統。當遇到問題須要定位時,也會產生一系列麻煩。
  二、解決方案:經過調用鏈,把一次請求調用過程串聯起來,實現對請求路徑的監控,便於快速定位。
  三、調用鏈顯示內容:各個調用環節的性能分析(如各API使用時間、使用堆棧)、調用個環節依賴關係還原、SQL打印、IP顯示。
  四、通用框架:google的Dapper,淘寶的鷹眼,京東的九頭蛇。
  五、調用鏈原理:
    a、請求生成一個全局TranceId,經過TraceId能夠串聯起整個調用鏈,一個tranceId表明一次請求。
    b、除了TranceId,還須要SpanId記錄調用的父子關係,span是本身生成,透傳子調用成爲parentId
    c、一個沒有parentId的span是調用鏈入口
    e、這個調用過程當中每一個請求都要透傳tranceId和spanId
    f、要查看某次完整的調用鏈只要根據TranceId查出全部調用記錄,而後經過parentId和spanId組織起整個調用父子關係。
  PS:具體參見
    https://blog.csdn.net/Damon__Wang/article/details/81782911
    https://blog.csdn.net/Damon__Wang/article/details/82051631
 
4五、生產環境如何定位內存溢出?CPU使用率太高?Linux命令?
 一、內存溢出的常見狀況分幾種:堆溢出(java heap space),PermGen space(方法區),不能建立本地線程(unable to create new native thread),回收執行了太長時間、超過限制(GC overhead limit exceeded)。初步判斷區域
    二、關鍵還在於分析dump文件。這個能夠提早設置+HeapDumpOnOutOfMemeryError,或jmp
    三、使用Jprofile打開dump文件,這裏能夠看到大對象和具體的引用
 四、固然結合gc的日誌更好(-XX:+PrintGC)
 
4六、Netty 
    Netty 是一個基於NIO的客戶、服務器端編程框架。
   Netty是什麼?
    1)本質:JBoss作的一個Jar包
    2)目的:快速開發高性能、高可靠性的網絡服務器和客戶端程序
    3)優勢:提供異步的、事件驅動的網絡應用程序框架和工具
    4)特色:
      a、併發高
      b、傳輸快:領拷貝,使用直接緩衝區
      c、封裝好:比較簡潔,使用鏈式調用
    通俗的說:一個好使的處理Socket的東東
 
4七、kafka 事務 性能
4八、內存屏障 
     Java內存模型中volatile變量在寫操做以後會插入一個store屏障,在讀操做以前會插入一個load屏障。一個類的final字段會在初始化後插入一個store屏障,來確保final字段在構造函數初始化完成並可被使用時可見。
 PS:
  一、爲了提升性能,處理器設計了多級緩存,cpu的緩存和共享的緩存。cpu把處理結果發到緩存後就能夠作其餘處理了,但這也形成了可見性問題。
  二、緩存操做是分紅緩存行,緩存一致性原則(MESI)經過定義獨佔、共享、修改、失效等緩存行的狀態來協調多個處理器對其的操做。(內存級別)當共享的同一緩存端的數據發生變化時其餘cpu都會獲得通知。
  三、爲了杜絕內存不一致的狀況(如指令重排序,cpu和編譯階段都會),又引入了內存屏障來確保一致性。內存屏障分爲LoadLoad屏障,LoadStore屏障,StoreStore屏障,StoreLoad屏障,都是確保後一個操做前前一個操做必須完成(指令在中間,分割對應的操做指令,肯定先後的一個關係)。(指令級別)
  四、lock前綴指令具有內存屏障功能(load&store)的的CUP指令,執行時鎖住子系統來確保執行順序,甚至跨多個CPU。
  五、JVM中,除了內存屏障,還使用先行發生原則來確保指令的先後關係。如對象鎖釋放必須先於加鎖發生,start先於thread內全部指令執行前發生。
  六、cas操做能夠理解爲是lock指令(鎖着內存)+系統cas指令來實現的。
  七、
 
4九、redis面試題

  一、Redis有哪些數據結構?程序員

       a、基本類型:字符串,數值         
    b、集合:字典Hash、列表List、集合Set、有序集合SortedSet,鏈表      
    c、其餘:若是你是Redis中高級用戶,還須要加上下面幾種數據結構HyperLogLog、Geo、   Pub/Sub。Redis Module,像BloomFilter,RedisSearch,Redis-ML。

  二、使用過Redis分佈式鎖麼,它是什麼回事?github

      a、先拿setnx來爭搶鎖,搶到以後,再用expire給鎖加一個過時時間防止鎖忘記了釋放。

  三、若是在setnx以後執行expire以前進程意外crash或者要重啓維護了,那會怎麼樣?web

      a、這個鎖就永遠得不到釋放了。
   b、set指令有很是複雜的參數,這個應該是能夠同時把setnx和expire合成一條指令來用的! 如redis的lua腳本

  四、假如Redis裏面有1億個key,其中有10w個key是以某個固定的已知的前綴開頭的,若是將它們所有找出來?

      a、使用keys指令能夠掃出指定模式的key列表。

       五、若是這個redis正在給線上的業務提供服務,那使用keys指令會有什麼問題?

         a、redis關鍵的一個特性:redis的單線程的。keys指令會致使線程阻塞一段時間,線上服務會停頓,直到指令執行完畢,服務才能恢復。         
    b、這個時候能夠使用scan指令,scan指令能夠無阻塞的提取出指定模式的key列表,可是會有必定的重複機率,         
    c、在客戶端作一次去重就能夠了,可是總體所花費的時間會比直接用keys指令長。

  六、使用過Redis作異步隊列麼,你是怎麼用的?

      a、通常使用list結構做爲隊列,rpush生產消息,lpop消費消息。當lpop沒有消息的時候,要適當sleep一會再重試。

  七、可不能夠不用sleep呢?

      a、list還有個指令叫blpop,在沒有消息的時候,它會阻塞住直到消息到來。

  八、能不能生產一次消費屢次呢?

      a、使用pub/sub主題訂閱者模式,能夠實現1:N的消息隊列。publish/subscribe

     九、pub/sub有什麼缺點?

      a、在消費者下線的狀況下,生產的消息會丟失,得使用專業的消息隊列如rabbitmq等。(消息是即時的,不作存儲)

  十、redis如何實現延時隊列?

      a、使用sortedset和string,數據做爲拿時間戳做爲score,消息內容做爲key調用zadd來生產消息,消費者用zrangebyscore指令獲取N秒以前的數據輪詢進行處理。 
   b、string中uuid做爲key,data做爲value存放數據。
   c、sortedset中,string中的uuid做爲key,時間戳做爲value存儲。獲取的時候使用zrangebyscore排序回去最先的數據。
   d、應對的場景就是:併發對數據庫更新時,鎖表會致使效率低,使用這種延遲操做能夠解決這種效率低的問題。

  十一、若是有大量的key須要設置同一時間過時,通常須要注意什麼?

      a、若是大量的key過時時間設置的過於集中,到過時的那個時間點,redis可能會出現短暫的卡頓現象。
   b、通常須要在時間上加一個隨機值,使得過時時間分散一些。(過時時間=固定時間+隨機值)

  十二、Redis如何作持久化的?

      a、bgsave作鏡像全量持久化,aof作增量持久化。     
   b、由於bgsave會耗費較長時間,不夠實時,在停機的時候會致使大量丟失數據,因此須要aof來配合使用。    
   c、 在redis實例重啓時,會使用bgsave持久化文件從新構建內存,再使用aof重放近期的操做指令來實現完整恢復重啓以前的狀態。

   1三、若是忽然機器掉電會怎樣?

      a、取決於aof日誌sync屬性的配置,若是不要求性能,在每條寫指令時都sync一下磁盤,就不會丟失數據。     
   b、可是在高性能的要求下每次都sync是不現實的,通常都使用定時sync,好比1s1次,這個時候最多就會丟失1s的數據。

  1四、bgsave的原理是什麼?

      a、fork和cow。fork是指redis經過建立子進程來進行bgsave操做,cow指的是copy on write,     
   b、子進程建立後,父子進程共享數據段,父進程繼續提供讀寫服務,寫髒的頁面數據會逐漸和子進程分離開來。

  1五、Pipeline有什麼好處,爲何要用pipeline?

      a、能夠將屢次IO往返的時間縮減爲一次,前提是pipeline執行的指令之間沒有因果相關性。     
   b、使用redis-benchmark進行壓測的時候能夠發現影響redis的QPS峯值的一個重要因素是pipeline批次指令的數目。

  1六、Redis的同步機制瞭解麼?

      a、Redis能夠使用主從同步,從從同步。     
   b、第一次同步時,主節點作一次bgsave,並同時將後續修改操做記錄到內存buffer,待完成後將rdb文件全量同步到複製節點,     複製節點接受完成後將rdb鏡像加載到內存。     
   c、加載完成後,再通知主節點將期間修改的操做記錄同步到複製節點進行重放就完成了同步過程

  1七、是否使用過Redis集羣,集羣的原理是什麼?

      a、Redis Sentinal着眼於高可用,在master宕機時會自動將slave提高爲master,繼續提供服務。    
   b、 Redis Cluster着眼於擴展性,在單個redis內存不足時,使用Cluster進行分片存儲。
    
50、動態代理有幾種,Jdk與Cglib區別
   一、實現方式:
    a、jdk經過反射機制生成一個實現代理接口(參數中interfaces裏全部接口且繼承了Proxy的代理類)的匿名類,在調用具體方法前調用invokeHandler的invoke處理
      a.1,Proxy.newProxyInstance(classloader,target.getclass.getInterfaces(),target(implemants InvocationHandler)
      a.二、生成一個實現了參數中interfaces裏全部接口且繼承了Proxy的代理類的字節碼,而後用參數中的classloader加載這個代理類。
      a.三、使用代理類父類的構造函數Proxy(invocationHandler)來建立一個代理類實例,將咱們自定義的InvocationHandler的子類傳入。
      a.四、返回這個代理類的實例。
    b、cglib利用ASM開源包,直接修改代理類class的字節碼生成子類來重寫其方法。
   二、目標類的限制:
    a、jdk只能正對實現了接口的類
    b、cglib針對有誤實現接口的都行
    c、cglib不能重寫final類或方法
   三、性能:jdk是愈來愈快
  ps:
    一、Java 字節碼操控框架。它能被用來動態生成類或者加強既有類的功能
    二、spring中的應用:<aop:aspectj-autoproxy proxy-target-class="true"/>:cglib
5一、數據庫三大範式
    一、字段不能夠再分:也就是說一個列名下的值只能是一種類型,如號碼中既有手機號、座機號等都不行
  保證字段的原子性,也是關係型數據庫的標準(面向對象)
  二、有主鍵,非主鍵字段依賴主鍵(有一個主題):一個主鍵表明一條記錄,只能有一個主題;也就是說一條記錄中不能即存在學生的記錄信息也存在課程的主題信息
  惟一性
  三、非主鍵字段不能相互依賴(都是平級的):每列都與主鍵有直接關係,不存在傳遞依賴。
5二、左鏈接和右鏈接說一下,內鏈接呢
  一、左鏈接:使用left join on,匹配時匹配表中沒能匹配上的也顯示,驅動表中只顯示匹配的上的
  二、右鏈接:使用right join on,效果和左鏈接相反
  三、內連接:使用(inner) join on ,匹配表和驅動表都只顯示匹配的上的。
  四、優化:都須要優化驅動表
  五、性能:左右關聯比內關聯要好一點。
5三、數據庫索引有幾種
5四、數據庫引擎你認識幾種,innodb 和myisam 區別,你的項目用到哪一個引擎
 

  PS:

    一、指數據庫事務正確執行的四個基本要素:原子性(Atomicity)、一致性(Consistency)、隔離性(Isolation)、持久性(Durability)    
    二、數據庫引擎是用於存儲、處理和保護數據的核心服務。利用數據庫引擎可控制訪問權限並快速處理事務,從而知足企業內大多數須要處理大量數據的應用程序的要求。
    三、查詢數據庫支持的引擎:show engines
    四、查詢數據庫使用的引擎:show variables like '%storage_engine%';
5五、若hashcode方法永遠返回1會產生什麼結果
  一、首先、編程時就堅持修改了equals就要修改hashchode要求
  二、hashcode在對象的對比和一些集合類中會被用到,如hashmap
  三、hashcode的存在也是一種優化程序的體現,如hashmap中的桶,若是全部的hashcode相同,那hashcode會構成一個線性表,致使性能降級
  
  
  PS:爲何選擇31作乘數     
    一、不大不小:爲了不hash重複須要選擇一個大一點的質數作乘數,不然就會致使hash值重複的較多,100以上的數乘下來容易超過int的範圍。
       二、31,33,37,39,41中31能夠被jvm優化,做爲位移計算,這種很高效。
 
5六、Error與RuntimeException的區別
  兩者的不一樣之處:
    Exception:
      1.能夠是可被控制(checked) 或不可控制的(unchecked)
      2.表示一個由程序員致使的錯誤 
      3.應該在應用程序級被處理
    Error:
      1.老是不可控制的(unchecked)
      2.常常用來用於表示系統錯誤或低層資源的錯誤
      3.如何可能的話,應該在系統級被捕捉
 
5七、引用計數法與GC Root可達性分析法區別
  一、引用計數:相似給對象添加一個計數器,當對象被引用的時候就在本身的計數器上加一,當某個引用的對象被回收後。引用爲零的對象就會被回收
    優勢:簡單、高效
    缺點:相互引用不能被識別(a.instance=c,b.instance=a,這兩項都再也不被使用,但卻沒法回收)
  二、可達性分析:從根開始遍歷他的引用,當某個對象到根不可達時則對象該引用能夠回收
    根節點:方法區的常量、靜態變量
        虛擬機棧區的變量列表的引用
        本地方法棧中的引用
        本地方法棧中引用的對象
  PS:
    一、五大分區:
         
      a、程序計數器:記錄當前執行程序的位置,改變計數器的值來肯定執行下一條指令,如循環、分支、方法跳轉    
      b、虛擬機棧:每一個線程都會建立一個虛擬機棧,經過壓棧出棧的方式執行方法調用。分局部變量表、操做數棧、動態連接、方法出口等。
      c、本地方法棧:native方法
      d、堆:存放對象實例
      e、方法區:用於存放已被虛擬機加載的類信息,常量,靜態變量等數據。
      f、直接內存:並非虛擬機運行時數據區的一部分,也不是虛擬機規範中定義的內存區域。

    二、各區域的使用

      

      

5八、雙親委派機制說一下
  一、某個特定的類加載器在接到加載類的請求時,首先將加載任務委託給父類加載器,依次遞歸,若是父類加載器能夠完成類加載任務,就成功返回;只有父類加載器沒法完成此加載任務時,才本身去加載。
  二、全盤責任委託:一個類若是被某個類加載器加載,那麼除非指定別的加載器,不然這個類關聯的類也有這個類加載器加載。
  三、由下向上詢問是否加載,由上向下嘗試加載。
  四、線程上下文加載器:針對java的spi狀況,也就是引導類加載的類型須要使用二方包的狀況,如jdk
5九、算法題:找出一個數組中第100個小的數字(堆思想解決)  
60、看你項目用到策略模式和工廠模式,說一下區別
  一、比喻:去必勝客吃披薩,工廠模式關注的是最終能吃到披薩,策略模式關注在關注的是披薩是如何作的。
    a、用途不一樣
      工廠是建立型模型,他的做用是建立對象
      策略是行爲型模型,他的做用是讓一個對象在許多行爲中選擇一種行爲。
    b、關注點不一樣
      一個關注對象的建立
      一個關注行爲的封裝
    c、解決不一樣的問題
      工廠模式,它接受指令,建立出符合要求的實例。它主要解決的是資源的統一分配,將對象的建立獨立出來,讓對象的建立和具體的使用客戶無關。
      策略模式,它爲了解決策略的切換與擴展,讓策略模式的變化獨立於使用策略模式的用戶。
    d、工廠至關於黑盒子,策略至關於白盒子
  PS:
    一、 設計模式有三種類型:建立型(解決:對象的建立和具體的使用解耦),行爲型(描述了對象和類的模式,以及它們之間的通訊模式)
  ,組合型(解決怎樣組裝現有的類,設計他們的交互方式,從而達到實現必定的功能的目的)。
    二、項目中:緩存的使用,定義一個公用的緩存操做頁面,在緩存工廠中經過配置緩存類別獲取具體的緩存實現,redis,tair
    三、簡述:
    

    

    

 四、工廠模式有三種:簡單工廠,工廠方法,抽象工廠。
    a、簡單工廠(靜態工廠):將類的實例化轉交一個工廠,具體的行爲由子類決定(即如何構造該實例);解決的問題:怎麼構造一個對象有工廠內部決定,如是否設置某個屬性等,用於隱藏實現細節。如計算器,獲取單例等
    b、工廠方法:定義一個用於建立對象的接口,讓子類決定實例化哪一個類。工廠方法使一個類的實例化延遲到其子類。不一樣的對象使用不一樣的工廠
    c、抽象工廠:提供一個建立一系列相關或相互依賴對象的接口,而無需指定他們具體的類。解決的問題:切換數據庫
  五、策略模式:它定義了算法家族,分別封裝起來,讓他們之間能夠相互替換,此模式讓算法的變化,不會影響到使用算法的客戶

 

 

6一、模板方法模式
   定義:一個模板結構,將具體內容延遲到子類去實現。
   解決的問題:
    一、將複用性高的代碼抽取到抽象父類中,具體的操做在繼承的子類中定義。
    二、父類調用子類操做,子類擴展不一樣的行爲,這樣即實現了控制反轉也符合開閉原則。
   使用場景
    試卷,apache velocity
6二、開閉原則懂嗎,說一下
一、設計模式中提到的一個概念。
二、目的指導咱們如何創建一個穩定的、靈活的系統。
三、開閉原則的定義是:軟件中的對象(類、模塊、函數等)應該對於擴展是開放的,可是,對於修改是封閉的。(能夠新增、儘量少的修改)
 
6三、NIO說一下你的理解
  一、NIO也就是非阻塞IO,相對於BIO而言,固然也有AIO
  二、打個比喻水工接水的比方:BIO就是多個多個水籠統,每一個水龍頭都有一個接水工,每一個接水工只有接到水才纔會作後續處理;NIO是爲每一個水龍頭添加了一個水缸且只有一個水工負責查看接到水剛,當某個水缸接滿水,水工就先處理這個水缸的後續處理。
  三、這個水缸就緩衝區buffer,水工就是選擇器Select,水工就看查看某個水龍頭時就等於鏈接了通道channel。
  四、具體的實現時,全部的通道都註冊到選擇器中,選擇器輪循查看通道中的數據是否準備就緒,而讀出和寫入都是直接到了緩衝區。
  五、這個地方提供了一個新的概念:直接緩衝區,繞過內核地址空間(系統空間),直接將數據的物理地址映射到用戶地址空間(JVM)。
  ps:以前的數據拷貝都是先寫到物理內存,而後再拷貝到jvm中。
6四、AtomicInteger底層原理
  一、AtomicInteger是一個提供原子操做的Integer類,經過線程安全的方式操做加減。
  二、其中存儲的value使用了volatile修飾,操做時使用cas無鎖算法。
  ps:
    一、具體實現就是使用Unsafe。
    二、它有以下功能:內存管理(分配、釋放內存),很是規的對象實例化(無需調用構造器),操做類、對象、變量(指針偏移獲取),數組操做(指針偏移),多線程同步(對象鎖機制,cas操做),線程掛起與恢復,內存屏障(loadFence、storeFence、fullFence)
    
6五、CAS機制是什麼?有什麼缺點,會出現什麼問題?
CAS是英文單詞Compare And Swap的縮寫,翻譯過來就是比較並替換。
CAS機制當中使用了3個基本操做數:內存地址V,舊的預期值A,要修改的新值B,若預期值A和內存值V相同就把內存值修改爲新值B
CAS的缺點:
1.CPU開銷較大
在併發量比較高的狀況下,若是許多線程反覆嘗試更新某一個變量,卻又一直更新不成功,循環往復,會給CPU帶來很大的壓力。
2.不能保證代碼塊的原子性
CAS機制所保證的只是一個變量的原子性操做,而不能保證整個代碼塊的原子性。好比須要保證3個變量共同進行原子性的更新,就不得不使用Synchronized了。
3.ABA問題
這是CAS機制最大的問題所在。

  PS:

  什麼是ABA問題?

 引用原書的話:若是在算法中的節點能夠被循環使用,那麼在使用「比較並交換」指令就可能出現這種問題,在CAS操做中將判斷「V的值是否仍然爲A?」,而且若是是的話就繼續執行更新操做,在某些算法中,若是V的值首先由A變爲B,再由B變爲A,那麼CAS將會操做成功。
      • 怎麼避免ABA問題?
Java中提供了 java.util.concurrent.atomic中AtomicStampedReference和AtomicMarkableReference來解決ABA問題。 
  • 底層實現?
多CPU的狀況下的cas操做是CPU提供的支持。
一、這和volatile的底層實現是相同的
二、底層:這個讀取、對比以及設置的操做私用lock前綴保證惟一性。
6六、本地緩存過時策略怎麼設置,一致性怎麼保證?
  1、一致性(以下是主動的狀況)
    一、當數據時效性要求比較高時,須要保證緩存與數據庫保存一致,並且須要保證緩存節點和副本中的數據也要保存一致,不能出現差別現象。
    二、這就比較依賴緩存過時和更新策略,通常會在數據發生更改時,主動更新緩存中的數據或者移除對應的緩存。
    三、通常的緩存使用
      

 

    方案一(先更新緩存,再更新數據庫):      
      一、是不可用的      
      二、首先庫存是以數據庫爲準的,若是緩存更新完成但數據庫未更新完成且庫存少於緩存則會形成負庫存。
      三、若緩存更新成功,數據庫更新失敗則緩存一直都是髒數據。
    方案二(新更新數據庫,再更新緩存)
      一、不可取
      二、若是兩個線程併發執行,會存在A更新數據庫,B更新數據庫,B更新緩存,A更新緩存(網絡緣由),則數據庫中的就是髒數據。
      三、針對那種依賴前值計算後更新的場景,無疑是浪費性能。
    方案三(先刪除緩存,再更新數據庫:更新數據庫失敗對業務也沒什麼影響)
      一、待優化
      二、存在A刪除了緩存,B發現緩存不存在從數據庫查詢到舊值寫到了緩存,A將新值寫入到數據庫:此時緩存和數據庫不一致。
      三、能夠採用延時雙刪策略:先淘汰緩存,再寫入數據庫,休眠1秒,再次淘汰緩存。此時能夠確保上述B的寫入被刪除。(休眠是爲了確保讀請求結束,寫請求能夠刪除對請求形成的髒數據)
      四、第二次刪除能夠採用新線程來作以免下降吞吐。
      五、第二次刪除失敗了的解決方案詳見方案四
    方案四(先更新數據庫,再刪除緩存)
      一、待優化
      二、緩存恰好失效,A查詢數據庫獲得舊值,b將新值寫入數據庫並刪除緩存,A將舊值寫入緩存(若b的寫入數據庫操做要足夠端以致於B刪除緩存早於A寫入緩存發生:機率低)
      三、2的解決方案:異步延時刪除, 緩存設置有效時間
      四、針對緩存更新失敗的解決方案:
        

      刪除失敗後,將刪除key的消息發送到消息隊列,重試刪除直到成功(這對業務代碼有侵入)

    

        使用mysql的中間件如Canal,單啓一個獨立的程序去處理。

  2、過時策略
    一、緩存過時策略大體分兩種:能夠經過過時時間來控制內容過時的狀況和沒法經過過時時間來控制內容過時的狀況。
    二、能夠經過過時時間來控制內容過時的狀況
         a、設置絕對過時時間(秒殺商品)。
         三、沒法經過過時時間來控制過時的狀況
        a、設置滑動過時(針對時效性不強的):在讀取緩存的時候將該緩存項的過時時間在當前時間的基礎上延後指定長度的事件。(如文章有評論就延長過時時間的狀況)
  
  ps:
    一、緩存滿了,從緩存中移除數據的策略
      a、新進先出算法
      b、最久未使用算法
      c、最少使用算法
    二、緩存併發問題
      a、緩存過時後將嘗試從後端數據庫獲取數據,當數據獲取到更新完成這段會有多個線程到數據庫後去數據,對數據庫形成極大的衝擊,甚至致使血崩。
      b、此時就要加鎖,到後臺數據庫請求數據要先嚐試獲取鎖,未獲取鎖的線程只能等待。
      c、針對緩存過時也要選擇一個範圍內隨機過時,不能所有集中到某個時間段。
    三、緩存穿透(不存在的key,緩存不起做用)
      a、查詢一個不存在的數據,因爲緩存是不被命中被動寫的(若是沒有就查數據庫),而且出於容錯考慮,若是存儲層查不到數據就不寫入緩存,這將致使每次都要查詢存儲層查詢,失去了緩存的意義。在流量大的時候,可能db就掛掉了。
      b、緩存空對象,過時時間段,不超過五分鐘。
      c、單獨過濾處理:將對應數據爲空的key進行統一存放
       d、布隆過濾器:將全部可能存在的數據哈希到一個足夠大的bitmap中,一個必定不存在的數據會被 這個bitmap攔截掉
    四、緩存顛簸:緩存節點故障致使,使用hash算法解決
    五、緩存雪崩
      a、緩存採用了相同的過時時間,致使緩存再同一時刻同時失效,DB瞬間壓力過大崩潰。
      b、在緩存失效的基礎上添加1-5分鐘的隨機值
      c、從應用架構角度,咱們能夠經過限流、降級、熔斷等手段來下降影響,也能夠經過多級緩存來避免這種災難
    六、緩存擊穿(一個存在的key,在緩存過時的一刻,同時有大量的請求)
      a、使用互斥鎖:使用setnx設置值,成功消息返回的纔去查詢數據庫(成功後設置有效期)
    七、緩存無底洞現象
    八、其餘
      a、目前主流的數據庫、緩存、Nosql、搜索中間件等技術棧中,都支持「分片」技術,來知足「高性能、高併發、高可用、可擴展」等要求
      b、命中:能夠直接經過緩存獲取到須要的數據。
 
  3、cache的使用
  4、掌醫的實現
  5、商品秒殺邏輯
相關文章
相關標籤/搜索