java中的關鍵詞彙含義和細碎知識點

JAVA關鍵詞彙

一: java

https://blog.csdn.net/sinat_35512245/article/details/59056120
記錄java項目中的關鍵詞彙

1.1 HandleInterceptor攔截器

SpringMVC的處理器攔截器,類似於Servlet開發中的過濾器Filter,用於對請求進行攔截和處理。

  • preHandle:在業務處理器處理請求之前被調用。預處理,可以進行編碼、安全控制、權限校驗等處理;
  • postHandle:在業務處理器處理請求執行完成後,生成視圖之前執行。後處理(調用了Service並返回
  • ModelAndView,但未進行頁面渲染),有機會修改ModelAndView (這個博主就基本不怎麼用了);
    afterCompletion:在DispatcherServlet完全處理完請求後被調用,可用於清理資源等。返回處理(已經渲染

單個實現類的執行順序
preHandler -> Controller -> postHandler -> model渲染-> afterCompletion

1.2 instanceof

用來在運行時指出對象是否是特定類的一個實例,返回布爾類型

if (anObject instanceof String){

}else{

}

1.3 分佈式跟蹤技術

1.3.1 Dapper

  • Dapper:大規模分佈式系統的跟蹤系統,幫助理解系統行爲,用於分析性能問題,

1.3.2 Sleuth框架

用於跟蹤服務的調用過程
Sleuth借鑑了Google Dapper的設計,先了解兩個概念

  • Trace 表示整個跟蹤過程,從用戶發起請求到最終的響應。一次跟蹤包括多個跨度,這些跨度以樹狀結構進行保存。
  • Span:跨度,表示一次調用的過程,一次跟蹤包含多次調用過程。假設用戶向A服務發起請求,A服務又要調用B服務,那麼此時將會產生兩個跨度。用戶調用A服務、A服務調用B服務。

1.3.3 RPC

  • RPC(Remote Procedure Call Protocol)–遠程過程調用協議,它是一種通過網絡從遠程計算機程序上請求服務,而不需要了解底層網絡技術的協議
  • RPC採用客戶機/服務器模式。請求程序就是一個客戶機,而服務提供程序就是一個服務器。

1.3.4 Zipkin

  • Zipkin是一個分佈式跟蹤系統,主要用於收集、管理微服務產生的數據。Zipkin的設計基於Google Dapper,在實際用時,我們需要讓各個微服務Zipkin服務器報告過程數據。
  • 對於Spring Cloud來說,已經提供了幾個模塊來實現數據報告功能,僅需要加入依賴,以及簡單配置,即可實現向Zipkin“寫入”數據

1.4 Thymeleaf

  • Thymeleaf是一個流行的模板引擎,
  • 頁面模板技術
  • Thymeleaf的主要目標在於提供一種可被瀏覽器正確顯示的、格式良好的模板創建方式,因此也可以用作靜態建模。你可以使用它創建經過驗證的XML與HTML模板

傳統的Spring WEB技術,使用JSP頁面技術,spring boot 已不推薦,spring boot 支持以下頁面模板語言

  • Thymeleaf
  • FreeMarker
  • Velocity
  • Groovy
  • JSP

1.4 安全冪等

get:安全冪等
post:不安全不冪等:當插入一條數據時,遇到網絡不佳,可能會多插入,所以不冪等
update:多次更新,扔一個條數據,所以冪等
冪等:對數據庫操作成功之後會不會產生影響,如果有影響則是不冪等
Post

1.5 snowflake (雪花)算法

  • 用於生成不同的ID,總共64位。

1.6 JPA

類似與mybatis

1.7 RESTful API

1.7.1 介紹

  • RESTful API就是一套協議來規範多種形式的前端和同一個後臺的交互方式。
  • RESTful API由後臺也就是SERVER來提供前端來調用。前端調用API向後臺發起HTTP請求,後臺響應請求將處理結果反饋給前端。也就是說RESTful 是典型的基於HTTP的協議

1.7.2 設計原則和規範

  • 資源:資源就是網絡上的一個實體,一段文本,一張圖片或者一首歌曲。資源總是要通過一種載體來反應它的內容。文本可以用TXT,也可以用HTML或者XML、圖片可以用JPG格式或者PNG格式,JSON是現在最常用的資源表現形式。
  • 統一接口:RESTful風格的數據元操CRUD(create,read,update,delete)分別對應HTTP方法:GET用來獲取資源,POST用來新建資源(也可以用於更新資源),PUT用來更新資源,DELETE用來刪除資源,這樣就統一了數據操作的接口。
  • URL:可以用一個URI(統一資源定位符)指向資源,即每個URI都對應一個特定的資源。要獲取這個資源訪問它的URI就可以,因此URI就成了每一個資源的地址或識別符。一般的,每個資源至少有一個URI與之對應,最典型的URI就是URL。
  • 無狀態:所謂無狀態即所有的資源都可以URI定位,而且這個定位與其他資源無關,也不會因爲其他資源的變化而變化。

二:註解

2.1 @Component

泛指組件,當組件不好歸類的時候,我們可以使用這個註解進行標記

2.2 @CrossOrigin

在微服務中,在controller層加上該註解,允許跨域請求

三:理論

3.1 CAP理論

CAP理論:一個分佈式系統最多隻能同時滿足一致性(Consistency)、可用性(Availability)和分區容錯性(Partition tolerance)這三項中的兩項。

  • 一致性:即更新操作成功並返回客戶端完成後,所有節點在同一時間的數據完全一致,所以,一致性,說的就是數據一致性。
  • 可用性:服務一直可用,而且是正常響應時間。對於一個可用性的分佈式系統,每一個非故障的節點必須對每一個請求作出響應。所以,一般我們在衡量一個系統的可用性的時候,都是通過停機時間來計算的。
  • 分區容錯性:分佈式系統在遇到某節點或網絡分區故障的時候,仍然能夠對外提供滿足一致性和可用性的服務。簡單點說,就是在網絡中斷,消息丟失的情況下,系統如果還能正常工作,就是有比較好的分區容錯性。
    在這裏插入圖片描述

假設在N1和N2之間網絡斷開的時候,有用戶向N1發送數據更新請求,那N1中的數據V0將被更新爲V1,由於網絡是斷開的,所以分佈式系統同步操作M,所以N2中的數據依舊是V0;這個時候,有用戶向N2發送數據讀取請求,由於數據還沒有進行同步,應用程序沒辦法立即給用戶返回最新的數據V1,怎麼辦呢?
有二種選擇

  • 第一,犧牲數據一致性,保證可用性。響應舊的數據V0給用戶;
  • 第二,犧牲可用性,保證數據一致性。阻塞等待,直到網絡連接恢復,數據更新操作M完成之後,再給用戶響應最新的數據V1。

這個過程,證明了要滿足分區容錯性的分佈式系統,只能在一致性和可用性兩者中,選擇其中一個。

四:安全認證(session、cookie、token)

cookie:數據只會保存在瀏覽器中,當需要開發安卓IOS時,使用cookie比較麻煩,需要更改

4.1 session

會話標識:客戶端和服務器中都需要保存,
客戶端(瀏覽器)如何保存這個“身份標識”,一般默認採用 Cookie 的方式,這個會話標識(Session id)會存在客戶端的Cookie中。

4.2 cookie

數據保存在瀏覽器中,
session的id一般存於cookie中。

  • session和cookie一般配合使用,cookie值保存在瀏覽器中,而session保存在服務端。但是當用戶量太大的時候,會造成存儲多,需要的服務多。

4.3 token

在這裏插入圖片描述
在這裏插入圖片描述

4.3 登錄有狀態、無狀態區別

  • 有狀態:服務器端保存記錄(cookie、session)
  • 無狀態:服務器端不用保存記錄(JWT、token)

4.4 RBAC

RBAC基於角色的訪問控制(Role-Based Access Control)是按角色進行授權,比如:主體的角色爲總經理可以查
詢企業運營報表,查詢員工工資信息等,訪問控制流程如下:

五:細碎知識點

5.1 多態

  • 必須滿足的條件:
    繼承
    重寫
    父類引用指向子類對象
    在這裏插入圖片描述

5.2 淺拷貝和深拷貝的區別?

  • 淺拷貝可以使用列表自帶的copy()函數(如list.copy()),或者使用copy模塊的copy()函數。
  • 深拷貝只能使用copy模塊的deepcopy()

區別

  • 淺拷貝僅僅複製所考慮的對象,而不復制它所引用的對象,當對象中含有對象時,則複製子對象的地址
  • 深拷貝把要複製的對象所引用的對象都複製了一遍。當對象中含有對象時,則將子對象也複製。改變子對象後,複製出來的對象裏面的屬性不會發生變化。而淺拷貝則會變化

5.3 反射中,Class.forName 和 ClassLoader 區別。

  • 在Java中,類裝載器把一個類裝入Java虛擬機中,要經過三個步驟來完成:裝載、鏈接和初始化,其中鏈接又可以分成校驗、準備和解析三 步,除了解析外,其它步驟是嚴格按照順序完成的,各個步驟的主要工作如下:
      裝載:查找和導入類或接口的二進制數據;
      鏈接:執行下面的校驗、準備和解析步驟,其中解析步驟是可以選擇的;
      校驗:檢查導入類或接口的二進制數據的正確性;
      準備:給類的靜態變量分配並初始化存儲空間;
      解析:將符號引用轉成直接引用;
      初始化:激活類的靜態變量的初始化Java代碼和靜態Java代碼塊。
  • Class.forName(className)方法,其實調用的方法是Class.forName(className,true,classloader);注意看第2個boolean參數,它表示的意思,在loadClass後必須初始化。比較下我們前面準備jvm加載類的知識,我們可以清晰的看到在執行過此方法後,目標對象的 static塊代碼已經被執行,static參數也已經被初始化。
  • 再看ClassLoader.loadClass(className)方法,其實他調用的方法是ClassLoader.loadClass(className,false);還是注意看第2個 boolean參數,該參數表示目標對象被裝載後不進行鏈接,**這就意味這不會去執行該類靜態塊中間的內容。**因此2者的區別就顯而易見了。

5.4 .如何在父類中爲子類自動完成所有的 hashcode 和 equals 實現?這麼做有何優劣。

hashCode()並不是完全可靠,有時候不同的對象他們生成的hashcode也會一樣(生成hash值得公式可能存在的問題),所以hashCode()只能說是大部分時候可靠,並不是絕對可靠

  • equal()相等的兩個對象他們的hashCode()肯定相等,也就是用equal()對比是絕對可靠的。
  • hashCode()相等的兩個對象他們的equal()不一定相等,也就是hashCode()不是絕對可靠的。

5.5描述動態代理的幾種實現方式,分別說出相應的優缺點。

jdk、cglib

  • jdk動態代理是由java內部的反射機制來實現的,
  • cglib動態代理底層則是藉助asm來實現的。
    jdk動態代理的應用前提,必須是目標類基於統一的接口。如果沒有上述前提,jdk動態代理不能應用。由此可以看出,jdk動態代理有一定的侷限性,cglib這種第三方類庫實現的動態代理應用更加廣泛,且在效率上更有優勢。

5.6 數組與鏈表的優缺點和區別

  • 數組 
    **是將元素在內存中連續存放,由於每個元素佔用內存相同,可以通過下標迅速訪問數組中任何元素。**但是如果要在數組中增加一個元素,需要移動大量元素,在內存中空出一個元素的空間,然後將要增加的元素放在其中。同樣的道理,如果想刪除一個元素,同樣需要移動大量元素去填掉被移動的元素。
    如果應用需要快速訪問數據,很少插入和刪除元素,就應該用數組。
  • 鏈表

元素在內存中不是順序存儲的,而是通過存在元素中的指針聯繫到一起,每個結點包括兩個部分:一個是存儲 數據元素 的數據域,另一個是存儲下一個結點地址的 指針。
  如果要訪問鏈表中一個元素,需要從第一個元素開始,一直找到需要的元素位置。但是增加和刪除一個元素對於鏈表數據結構就非常簡單了,只要修改元素中的指針就可以了。
  如果應用需要經常插入和刪除元素你就需要用鏈表。
區別

  • 數組必須事先定義固定的長度(元素個數),不能適應數據動態地增減的情況。當數據增加時,可能超出原先定義的元素個數;當數據減少時,造成內存浪費。
  • 鏈表動態地進行存儲分配,可以適應數據動態地增減的情況,且可以方便地插入、刪除數據項。(數組中插入、刪除數據項時,需要移動其它數據項)
  • 數組可以隨機訪問,時間複雜度爲O(1),而鏈表不支持隨機訪問,平均需要O(n);

5.7 error 和 exception 的區別,CheckedException,RuntimeException 的區別

在這裏插入圖片描述
從圖中可以看出所有異常類型都是內置類Throwable的子類,

  • Error:不能被程序捕獲,或程序無法處理的錯誤

  • Exception,它表示用戶程序可能捕捉的異常情況或者說是程序可以處理的異常。

  • Error:Error類對象由 Java 虛擬機生成並拋出,大多數錯誤與代碼編寫者所執行的操作無關

  • Exception分支中有一個重要的子類RuntimeException(運行時異常),該類型的異常自動爲你所編寫的程序定義ArrayIndexOutOfBoundsException(數組下標越界)、NullPointerException(空指針異常)、ArithmeticException(算術異常)、MissingResourceException(丟失資源)、ClassNotFoundException(找不到類)等異常,這些異常是不檢查異常,程序中可以選擇捕獲處理,也可以不處理。

  • 在這裏插入圖片描述
    檢查性異常:最具代表的檢查性異常是用戶錯誤或問題引起的異常,這是程序員無法預見的。例如要打開一個不存在文件時,一個異常就發生了,這些異常在編譯時不能被簡單地忽略。
    運行時異常: 運行時異常是可能被程序員避免的異常。與檢查性異常相反,運行時異常可以在編譯時被忽略。
    錯誤: 錯誤不是異常,而是脫離程序員控制的問題。錯誤在代碼中通常被忽略。例如,當棧溢出時,一個錯誤就發生了,它們在編譯也檢查不到的。

5.8 JVM

JVM是Java Virtual Machine(Java虛擬機)的縮寫,JVM是一種用於計算設備的規範,它是一個虛構出來的計算機。
jvm中包括:字節碼指令集、一組寄存器、一個棧、一個垃圾回收堆和一個存儲方法域

  • JRE(JavaRuntimeEnvironment,Java運行環境),也就是Java平臺。所有的Java 程序都要在JRE下才能運行
  • JDK(Java Development Kit)是程序開發者用來來編譯、調試java程序用的開發工具包。JDK的工具也是Java程序,也需要JRE才能運行
  • JVM(JavaVirtualMachine,Java虛擬機)是JRE的一部分。它是一個虛構出來的計算機,是通過在實際的計算機上仿真模擬各種計算機功能來實現的

程序計數器
程序計數器是線程私有的區域,每個線程當然得有個計數器記錄當前執行到那個指令。佔用的內存空間小,可以把它看成是當前線程所執行的字節碼的行號指示器
(記錄每一個線程當前執行到那個指令)
程序計數器還有幾個特點:

  • 如果線程正在執行的是Java 方法,則這個計數器記錄的是正在執行的虛擬機字節碼指令地址。
  • 如果正在執行的是Native 方法(本地方法),則這個計數器值爲空(Undefined)。
    例如:(System.currentTimeMillis())他是通過C來實現,直接通過系統就能直接調用了不需要去編譯成需要執行的字節碼指令的 話,那麼就相當於不過程序計數器,它沒有記錄的話,那他的計數器的值就肯定爲空了。
  • 此內存區域是唯一一個在Java虛擬機規範中沒有規定任何OutOfMemoryError情況的區域。

JAVA虛擬機棧(VM Stack)

  • 這個虛擬機棧描述的是JAVA方法執行的內存模型,用於存局部變量,操作數棧,方法出口等信息的,在這裏最需要注意的就是他存放的是什麼數據.局部變量裏面放的就是那些我們所知道的基本的數據類型,對象引用的話那就是一個地址。
  • 它裏面存放的是一個函數的上下文,具體存放的是執行的函數的一些數據
  • 在虛擬機規範裏面還說,他的2個異常狀況:
    • 一個是StackOverflowError異常,棧內存溢出,這肯定很容易理解,就是棧的內存不夠,你的請求線程太大。(固定長度的棧)
      一種是StackOverflowError,當前線程請求的棧深度大於虛擬機所允許的深度時,會拋出這個異常。製造這種異常很簡單:將一個函數反覆遞歸自己,最終會出現棧溢出錯誤(StackOverflowError)。
    • 如果說在動態擴展的過程中,申請的長度還是不夠,那麼會拋出另外一個異常OutOfMemoryError異常
      當虛擬機棧可以動態擴展時(當前大部分虛擬機都可以),如果無法申請足夠多的內存就會拋出OutOfMemoryError,如何製作虛擬機棧OOM呢,參考一下代碼:

JAVA堆(heap)

  • Heap是在JAVA虛擬機中內存佔用***的一個地方,也是所有線程共享的一個內存區域,堆內存中主要就是用於存放對象實例的。

方法區

  • 方法區存放的是類信息、常量、靜態變量等。方法區是各個線程共享區域,很容易理解,我們在寫Java代碼時,每個線程度可以訪問同一個類的靜態變量對象。由於使用反射機制的原因,虛擬機很難推測那個類信息不再使用,因此這塊區域的回收很難。另外,對這塊區域主要是針對常量池回收,

垃圾回收機制

  • 什麼樣的對象纔是垃圾?
  • 這個問題其實很簡單,對於Java對象來講,如果說這個對象沒有被其他對象所引用該對象就是無用的,此對象就被稱爲垃圾,其佔用的內存也就要被銷燬。那麼自然而然的就引出了我們的第二個問題,判斷對象爲垃圾的算法都有哪些?

標記垃圾的算法

  • Java中標記垃圾的算法主要有兩種, 引用計數法和可達性分析算法。我們首先來介紹引用計數法。

引用計數法

  • 引用計數法就是給對象中添加一個引用計數器,每當有一個地方引用它,計數器就加 1;當引用失效,計數器就減 1;任何時候計數器爲 0 的對象就是不可能再被使用的,可以當做垃圾收集。這種方法實現起來很簡單而且優缺點都很明顯。
    • 優點 執行效率高,程序執行受影響較小
    • 缺點 無法檢測出循環引用的情況,導致內存泄露

可達性分析算法

  • 這個算法的基本思想就是通過一系列的稱爲 “GC Roots” 的對象作爲起點,從這些節點開始向下搜索,節點所走過的路徑稱爲引用鏈,當一個對象到 GC Roots 沒有任何引用鏈相連的話,則證明此對象是不可用的。
  • 那麼什麼對象可以作爲GCRoot?
    • 虛擬機棧中的引用對象
    • 方法區中的常量引用對象
    • 方法區中的類靜態屬性引用對象
    • 本地方法棧中的引用對象
    • 活躍線程中的引用對象

GC Roots都有哪些:

  1. 虛擬機棧中的引用的對象
  2. 方法區中靜態屬性引用的對象,常量引用的對象
  3. 本地方法棧中JNI(即一般說的Native方法)引用的對象。
    如何將垃圾回收?
    在Java中存在着四種垃圾回收算法,標記清除算法、複製算法、標記整理算法以及分代回收算法。
  • 標記清除算法
    • 該算法分爲“標記”和“清除”兩個階段:標記階段的任務是標記出所有需要被回收的對象,清除階段就是回收被標記的對象所佔用的空間。它是最基礎的收集算法,效率也很高,
  • 複製算法
    • 爲了解決效率問題,我們開發出了複製算法**。它可以將內存分爲大小相同的兩塊**,每次使用其中的一塊。當第一塊的內存使用完後,就將還存活的對象複製到另一塊去,然後再把使用的空間一次清理掉。這樣就使每次的內存回收都是對內存區間的一半進行回收。
    • 簡單來說就是該對象分爲對象面以及空閒面,對象在對象面上創建,對象面上存活的對象會被複制到空閒面,接下來就可以清除對象面的內存。
    • 這種算法的優缺點也比較明顯
      • 優點:解決碎片化問題,順序分配內存簡單高效
      • 缺點:只適用於存活率低的場景,如果極端情況下如果對象面上的對象全部存活,就要浪費一半的存儲空間。
  • 標記整理算法
    • 爲了解決複製算法的缺陷,充分利用內存空間,提出了標記整理算法。該算法標記階段和標記清除一樣,但是在完成標記之後,它不是直接清理可回收對象,而是將存活對象都向一端移動,然後清理掉端邊界以外的內存。
    • 在這裏插入圖片描述
  • 分代收集算法
    • 當前虛擬機的垃圾收集都採用分代收集算法,這種算法就是根據具體的情況選擇具體的垃圾回收算法。一般將 java 堆分爲新生代和老年代,這樣我們就可以根據各個年代的特點選擇合適的垃圾收集算法。
    • 比如在新生代中,每次收集都會有大量對象死去,所以可以選擇複製算法,只需要付出少量對象的複製成本就可以完成每次垃圾收集。而老年代的對象存活機率是比較高的,而且沒有額外的空間對它進行分配擔保,所以我們必須選擇“標記-清除”或“標記-整理”算法進行垃圾收集。

5.8.2 jvm 和 類的關係

當調用 java命令運行一個java程序時,必會啓動一個jvm即java虛擬機。(5.6.處有聯繫!!)
該java程序的所有線程,變量都處於jvm中,都使用該jvm的內存區
jvm終止的情況:
1.程序自然運行結束
2.遇到System.exit();Runtime.getRuntime.exit();
3.遇到未捕獲異常或錯誤時
4.程序所在的平臺強制結束了JVM進程
jvm終止,jvm內存中的數據全部丟失。

5.8.3 類的加載

類的加載 又稱爲 類的初始化,實際上可細分爲 類的 加載、連接、初始化。下面將講述着三個階段的過程!
類的加載 指.class文件讀入內存,併爲之創建一個 java.lang.Class對象
類加載,是通過類加載器來完成的,類加載器通常由JVM提供,通常稱爲系統類加載器(也可以是自己寫的加載器,只要繼承ClassLoader基類)。
類加載無須等到“首次使用該類”時加載,jvm允許預加載某些類。。。。
加載來源:
1.本地.class文件
2.jar包的.class文件
3.網絡.class文件
4.把一個java源文件動態編譯,加載

5.8.4 類的連接

負責把類的二進制數據合併到JRE(java運行環境)中
1.驗證 檢測被加載的類是否有正確的內部結構,並和其他類協調一致
2.準備 負責爲類的類變量(非對象變量)分配內存,並設置默認初始值
3.解析 將類的二進制數據中的符號引用替換成直接引用。。

5.8.5 類初始化

主要對類變量(而非對象變量)的初始化
聲明類變量的初始值 = 靜態初始化塊 他們是相同的,等效的。都會被當成類的初始化語句,JVM會按照這些語句在程序中的順序依次執行他們
JVM初始化一個類包含如下幾個步驟:
1.假設類還沒有被加載和連接,那麼先加載和連接該類
2.假設該類的父類還沒被初始化,那麼先初始化父類 ----jvm總是最先初始化java.lang.Object類
3.假設類中有初始化語句,則一次執行這些初始化語句
當程序主動使用任何一個類時,系統會保證該類以及所有父類(直接父類和間接父類)都會被初始化

5.8.6 類初始化的時機:

1.創建類的實例。new,反射,反序列化
2.使用某類的類方法–靜態方法
3.訪問某類的類變量,或賦值類變量
4.反射創建某類或接口的Class對象。Class.forName(“Hello”);—注意:loadClass調用ClassLoader.loadClass(name,false)方法,沒有link,自然沒有initialize
5.初始化某類的子類
6.直接使用java.exe來運行某個主類。即cmd java 程序會先初始化該類。
特殊情形:final 類型的類變量,如果在編譯時(轉成.class文件)就可以確定,那麼這個類變量就相當於“宏變量”,編譯時,直接替換成值。
所以,即使使用這個類變量,程序也不會導致該類的初始化!!----相當於直接使用 常量

  • 使用ClassLoader類的 loadClass方法來加載類時,只是加載該類,而不會執行該類的初始化!!使用Class的forName()靜態方法,纔會導致強制初始化該類。

5.8.7 類加載器

類加載器 負責加載所有的類,爲被加載如內存中的類生成一個java.lang.Class實例。一旦類被載入內存,同一個類就不會再加載第二次
如何判斷是同一個類:
java中 一個類用其 全限定類名標示–包名+類名
jvm中 一個類用其 全限定類名+加載器標示—包名+類名+加載器名
加載器層次結構:
JVM啓動時,姓曾的三個類加載器組成的機構
1.Bootstrap ClassLoader 根類 ------引導類加載器,加載java核心類。非java.lang.ClassLoader子類,而是JVM自身實現
2.Extension ClassLoader 擴展類-----加載JRE的擴展目錄中的JAR包的類(%JAVA_HOME%/jre/lib/ext或java.ext.dirs系統屬性指定的目錄)
3.System ClassLoader 系統類-----加載cmd java -cp **,環境變量指定的jar包和類路徑。ClassLoader.getSystemClassLoader獲得 系統類加載器。
4.用戶類加載器。。。

5.8.7.1 類加載器加載Class大致要經過9個步驟:

1.檢測此Class 是否被載入過(即在緩存區中是否由此 Class),有,則進入第8步,否則執行第2步。
2.如果父類加載器不存在(要麼parent 一定是根類加載器,要麼本身就是根類加載器),則跳到第4步;如果父類加載器存在,則執行第3步。
3.請求使用父類加載器去載入目標類,如果成功則跳到第8步,否則執行第5步
4.請求使用 根類加載器 載入目標類,成功則跳到第8步,否則跳到第7步
5.當前類加載器 嘗試尋找 Class文件(從與此ClassLoader相關的類路徑中尋找),如果找到則執行第6步,否則跳到第7步。
6.從文件中載入Class,成功後跳到第8步。
7.拋出ClassNotFoundException異常。
8.返回對應的 java.lang.Class對象。
其中 第5,6步允許重寫ClassLoader的findClass()方法來實現自己的載入策略,甚至重寫loadClass()方法來實現自己 的載入過程

5.9 自定義一個java.lang.String類,這個類是否可以被類加載器加載?爲什麼

1.、雙親委派模型

  • 類加載器可分爲兩類:一是啓動類加載器(Bootstrap ClassLoader),是C++實現的,是JVM的一部分;另一種是其它的類加載器,是Java實現的,獨立於JVM,全部都繼承自抽象類java.lang.ClassLoader。
  • jdk自帶了三種類加載器,分別是啓動類加載器(Bootstrap ClassLoader),擴展類加載器(Extension ClassLoader),應用程序類加載器(Application ClassLoader)。
  • 後兩種加載器是繼承自抽象類java.lang.ClassLoader。
    一般是: 自定義類加載器 >> 應用程序類加載器 >> 擴展類加載器 >> 啓動類加載器
    上面的層次關係被稱爲雙親委派模型(Parents Delegation Model)。

當一個類加載器收到類加載的請求,它將這個加載請求委派給父類加載器進行加載,每一層加載器都是如此,最終,所有的請求都會傳送到啓動類加載器中。只有當父類加載器自己無法完成加載請求時,子類加載器纔會嘗試自己加載。

思考:假如我們自己寫了一個java.lang.String的類,我們是否可以替換調JDK本身的類?

  • 答案是否定的。我們不能實現。爲什麼呢?我看很多網上解釋是說雙親委託機制解決這個問題,其實不是非常的準確。因爲雙親委託機制是可以打破的,你完全可以自己寫一個classLoader來加載自己寫的java.lang.String類,但是你會發現也不會加載成功,具體就是因爲針對java.*開頭的類,jvm的實現中已經保證了必須由bootstrp來加載。
  • 因加載某個類時,優先使用父類加載器加載需要使用的類。如果我們自定義了java.lang.String這個類, 加載該自定義的String類,該自定義String類使用的加載器是AppClassLoader,根據優先使用父類加載器原理, AppClassLoader加載器的父類爲ExtClassLoader,所以這時加載String使用的類加載器是ExtClassLoader, 但是類加載器ExtClassLoader在jre/lib/ext目錄下沒有找到String.class類。然後使用ExtClassLoader父類的加載器BootStrap, 父類加載器BootStrap在JRE/lib目錄的rt.jar找到了String.class,將其加載到內存中。這就是類加載器的委託機制。

5.10 jdk1.5 中,引入了泛型,泛型的存在是用來解決什麼問題。

泛型的本質是參數化類型,也就是說所操作的數據類型被指定爲一個參數,泛型的好處是在編譯的時候檢查類型安全,並且所有的強制轉換都是自動和隱式的,以提高代碼的重用率

5.11 string 、stringbuffer、stringbuilder區別

  • String是Java中基礎且重要的類,並且String也是Immutable類的典型實現,被聲明爲final class,除了hash這個屬性其它屬性都聲明爲final,因爲它的不可變性,所以例如拼接字符串時候會產生很多無用的中間對象,如果頻繁的進行這樣的操作對性能有所影響。
  • StringBuffer就是爲了解決大量拼接字符串時產生很多中間對象問題而提供的一個類,提供append和add方法,可以將字符串添加到已有序列的末尾或指定位置,它的本質是一個線程安全的可修改的字符序列,把所有修改數據的方法都加上synchronized。但是保證了線程安全是需要性能的代價的。
  • StringBuilder是JDK1.5發佈的,它和StringBuffer本質上沒什麼區別,就是去掉了保證線程安全的那部分,減少了開銷。

應用場景

  • 在字符串不經常發生變化的業務場景優先使用String(代碼更清晰簡潔)。如常量的聲明,少量的字符串操作(拼接,刪除等)。
  • 在單線程情況下,如有大量的字符串操作情況,應該使用StringBuilder來操作字符串。不能使用String"+"來拼接而是使用,避免產生大量無用的中間對象,耗費空間且執行效率低下(新建對象、回收對象花費大量時間)。如JSON的封裝等。
  • 在多線程情況下,如有大量的字符串操作情況,應該使用StringBuffer。如HTTP參數解析和封裝等。

5.12 volatile和synchronized的區別

爲了解決多個線程同時操作一個變量時,會導致變量的結果不一致的問題;
volatile作用在變量上時,當在一個線程中改變該變量的值,會同步至其他線程中的變量。
1.volatile本質是在告訴jvm當前變量在寄存器(工作內存)中的值是不確定的,需要從主存中讀取;synchronized則是鎖定當前變量,只有當前線程可以訪問該變量,其他線程被阻塞住;
2.volatile僅能使用在變量級別;synchronized則可以使用在變量、方法、和類級別的;
3.volatile僅能實現變量的修改可見性,不能保證原子性;而synchronized則可以保證變量的修改可見性和原子性;
4.volatile不會造成線程的阻塞;synchronized可能會造成線程的阻塞;
5.volatile標記的變量不會被編譯器優化;synchronized標記的變量可以被編譯器優化。

5.13單點登錄的原理,你們jwt中包含哪些信息?

JWT由三部分組成,分別是頭信息、有效載荷、簽名,中間以(.)分隔,如下格式:

  • 頭信息: 由兩部分組成,令牌類型(即:JWT)、散列算法(HMAC、RSASSA、RSASSA-PSS等),例如:

{
“alg”: “HS256”,
“typ”: “JWT”
}

  • 有效載荷
    JWT的第二部分是payload,其中包含claims。claims是關於實體(常用的是用戶信息)和其他數據的聲明,claims有三種類型: registered, public, and private claims。
  • JWT的第三部分是一個簽證信息,包含三個部分:

header(base64加密後的)
payload(base64加密後的)
secret
這個部分需要base64加密後的header和base64加密後的payload使用.連接組成的字符串,然後通過header中聲明的加密方式進行加鹽secret組合加密,然後就構成了jwt的第三部分

5.14 紅黑樹的實現原理和應用場景

  • 定義
    每個結點或者爲黑色或者爲紅色
    根結點爲黑色
    每個葉結點(實際上就是NULL指針)都是黑色的
    如果一個結點是紅色的,那麼它的兩個子節點都是黑色的(也就是說,不能有兩個相鄰的紅色結點)
    對於每個結點,從該結點到其所有子孫葉結點的路徑中所包含的黑色結點數量必須相同

特點:一個根節點的左右個子樹的高度差不超過1

紅黑樹是自平衡的二叉搜索樹,是計算機科學中的一種數據結構。
它不是完全平衡的二叉樹,但能保證搜索操作在O(log n)的時間複雜度內完成(n是樹中節點總數)。
插入、刪除以及旋轉、染色操作都是O(log n)的時間複雜度

5.15 HashMap和Hashtable的區別

1、線程安全
兩者最主要的區別在於Hashtable是線程安全,而HashMap則非線程安全。
Hashtable的實現方法裏面都添加了synchronized關鍵字來確保線程同步,因此相對而言HashMap性能會高一些,我們平時使用時若無特殊需求建議使用HashMap,在多線程環境下若使用HashMap需要使用Collections.synchronizedMap()方法來獲取一個線程安全的集合。
2:針對null的不同
HashMap可以使用null作爲key,而Hashtable則不允許null作爲key
Hashtable既不支持Null key也不支持Null value。
HashMap中,null可以作爲鍵,這樣的鍵只有一個;可以有一個或多個鍵所對應的值爲null
3:繼承結構
HashMap是對Map接口的實現,HashTable實現了Map接口和Dictionary抽象類。
4:兩者計算hash的方法不同
Hashtable直接使用key對象的hashCode
HashMap爲了得到元素的位置,首先需要根據元素的 Key計算出一個hash值,然後再用這個hash值來計算得到最終的位置。

5.16 線程池有哪些類型

線程池就是創建若干個可執行的線程放入一個池(容器)中,有任務需要處理時,會提交到線程池中的任務隊列,處理完之後線程並不會被銷燬,而是仍然在線程池中等待下一個任務。

  • newCachedThreadPool
    是一種線程數量不定的線程池,並且其最大線程數爲Integer.MAX_VALUE
    (1)線程池中數量沒有固定,可達到最大值(Interger. MAX_VALUE)
    (2)線程池中的線程可進行緩存重複利用和回收(回收默認時間爲1分鐘)
    (3)當線程池中,沒有可用線程,會重新創建一個線程
public class PoolExecutorTest {
 
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		ExecutorService mCachelThreadPool = Executors.newCachedThreadPool();
		for(int i = 0;i < 7;i++ ) {
			final int index = i;
			try {
				Thread.sleep(2000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			mCachelThreadPool.execute(new Runnable() {
				@Override
				public void run() {
					System.out.println("第" +index +"個線程" +Thread.currentThread().getName()); 
				}
			});
		}	
	}
}

在這裏插入圖片描述
從結果可以看到,執行第二個任務的時候第一個任務已經完成,會複用執行第一個任務的線程,不用每次新建線程

  • newFixedThreadPool 創建一個指定工作線程數量的線程池,每當提交一個任務就創建一個工作線程

特徵:
(1)線程池中的線程處於一定的量,可以很好的控制線程的併發量
(2)線程可以重複被使用,在顯示關閉之前,都將一直存在
(3)超出一定量的線程被提交時候需在隊列中等待

public class PoolExecutorTest {
	public static void main(String[] args) throws InterruptedException {
		// TODO Auto-generated method stub
		//設置最大線程數5個
		ExecutorService mFixedThreadPool = Executors.newFixedThreadPool(5);
		for(int i = 0;i < 7;i++ ) {
			final int index = i;
			mFixedThreadPool.execute(new Runnable() {
				@Override
				public void run() {
					System.out.println("時間是:"+System.currentTimeMillis()+"第" +index +"個線程" +Thread.currentThread().getName()); 
					try {
						Thread.sleep(2000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					} }	});}}}

在這裏插入圖片描述
由於設置最大線程是5,所以當執行完這5個線程後,等待兩秒後,在執行後面2個線程。

  • newScheduledThreadPool 創建一個線程池,它的核心線程數量是固定的

特徵:
(1)線程池中具有指定數量的線程,即便是空線程也將保留
(2)可定時或者延遲執行線程活動
延遲執行:

public class PoolExecutorTest {
	public static void main(String[] args) throws InterruptedException {
		// TODO Auto-generated method stub
		//設置池中核心數量是2
        ScheduledExecutorService mScheduledThreadPool = Executors.newScheduledThreadPool(2);  
        System.out.println("現在的時間:"+System.currentTimeMillis());
        mScheduledThreadPool.schedule(new Runnable() {
			@Override
			public void run() {
				// TODO Auto-generated method stub
				System.out.println("現在的時間:"+System.currentTimeMillis());		
			}
		}, 4, TimeUnit.SECONDS);//這裏設置延遲4秒執行
	} }

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

  • newSingleThreadExecutor這類線程池內部只有一個核心線程
    (1)線程池中最多執行1個線程,之後提交的線程活動將會排在隊列中以此執行
    在這裏插入圖片描述
    5、newSingleThreadScheduledExecutor
    作用: 創建一個單線程執行程序,它可安排在給定延遲後運行命令或者定期地執行。

特徵:
(1)線程池中最多執行1個線程,之後提交的線程活動將會排在隊列中以此執行
(2)可定時或者延遲執行線程活動

5.17 Redis三種模式對比

  • 爲什麼使用redis
    1,解決應用服務器的cpu和內存壓力
    2,減少io的讀操作,減輕io的壓力
    3,關係型數據庫擴展性不強,難以改變表的結構
  • 使用場景
    1,數據高併發讀寫
    2,海量數據讀寫
    3,對不規則數據也就是擴展性要求高的數據
  • 主從模式
    在這裏插入圖片描述
    實現主從複製(Master-Slave Replication)的工作原理:Slave從節點服務啓動並連接到Master之後,它將主動發送一個SYNC命令。Master服務主節點收到同步命令後將啓動後臺存盤進程,同時收集所有接收到的用於修改數據集的命令,在後臺進程執行完畢後,Master將傳送整個數據庫文件到Slave,以完成一次完全同步。而Slave從節點服務在接收到數據庫文件數據之後將其存盤並加載到內存中。此後,Master主節點繼續將所有已經收集到的修改命令,和新的修改命令依次傳送給Slaves,Slave將在本次執行這些數據修改命令,從而達到最終的數據同步。
    如果Master和Slave之間的鏈接出現斷連現象,Slave可以自動重連Master,但是在連接成功之後,一次完全同步將被自動執行。
    優點:
    1:同一個Master可以同步多個Slaves。
    2:**Slave同樣可以接受其它Slaves的連接和同步請求,這樣可以有效的分載Master的同步壓力。**因此我們可以將Redis的Replication架構視爲圖結構。
    3:Master Server是以非阻塞的方式爲Slaves提供服務。所以在Master-Slave同步期間,客戶端仍然可以提交查詢或修改請求。
    4:Slave Server同樣是以非阻塞的方式完成數據同步。在同步期間,如果有客戶端提交查詢請求,Redis則返回同步之前的數據
    4:爲了分載Master的讀操作壓力,Slave服務器可以爲客戶端提供只讀操作的服務,寫服務仍然必須由Master來完成。即便如此,系統的伸縮性還是得到了很大的提高。
    5:Master可以將數據保存操作交給Slaves完成,從而避免了在Master中要有獨立的進程來完成此操作。
    6:支持主從複製,主機會自動將數據同步到從機,可以進行讀寫分離。
    缺點
    1:Redis不具備自動容錯和恢復功能,
    2:主機宕機,宕機前有部分數據未能及時同步到從機,切換IP後還會引入數據不一致的問題,降低了系統的可用性。
    3:Redis的主從複製採用全量複製,複製過程中主機會fork出一個子進程對內存做一份快照,並將子進程的內存快照保存爲文件發送給從機,這一過程需要確保主機有足夠多的空餘內存。若快照文件較大,對集羣的服務能力會產生較大的影響,**而且複製過程是在從機新加入集羣或者從機和主機網絡斷開重連時都會進行,也就是網絡波動都會造成主機和從機間的一次全量的數據複製,**這對實際的系統運營造成了不小的麻煩。
    4:**Redis較難支持在線擴容,在集羣容量達到上限時在線擴容會變得很複雜。**爲避免這一問題,運維人員在系統上線時必須確保有足夠的空間,這對資源造成了很大的浪費。
  • Sentinel(哨兵模式)
    Sentinel(哨兵)是Redis的高可用性解決方案:由一個或多個Sentinel實例組成的Sentinel系統可以監視任意多個主服務器,以及這些主服務器屬下的所有從服務器,並在被監視的主服務器進入下線狀態時,自動將下線主服務器屬下的某個從服務器升級爲新的主服務器
    在這裏插入圖片描述
    優點
    1、Master 狀態監測
    2、如果Master 異常,則會進行Master-slave 轉換,將其中一個Slave作爲Master,將之前的Master作爲Slave
    3、Master-Slave切換後,master_redis.conf、slave_redis.conf和sentinel.conf的內容都會發生改變,即master_redis.conf中會多一行slaveof的配置,sentinel.conf的監控目標會隨之調換
    缺點:
    1、如果是從節點下線了,sentinel是不會對其進行故障轉移的,連接從節點的客戶端也無法獲取到新的可用從節點
    2、無法實現動態擴容
    主從模式和Sentinel模式這兩個模式都有一個問題,不能水平擴容
  • Cluster模式
    Redis 的哨兵模式基本已經可以實現高可用,讀寫分離 ,但是在這種模式下每臺 Redis 服務器都存儲相同的數據,很浪費內存,所以在redis3.0上加入了 Cluster 集羣模式,實現了 Redis 的分佈式存儲,也就是說每臺 Redis 節點上存儲不同的內容。
    在這裏插入圖片描述
    在 Redis 的每一個節點上,都有這麼兩個東西,一個是插槽(slot),它的的取值範圍是:0-16383。還有一個就是cluster,可以理解爲是一個集羣管理的插件。當我們的存取的 Key到達的時候,Redis 會根據 crc16的算法得出一個結果,然後把結果對 16384 求餘數,這樣每個 key 都會對應一個編號在 0-16383 之間的哈希槽,通過這個值,去找到對應的插槽所對應的節點,然後直接自動跳轉到這個對應的節點上進行存取操作。
      爲了保證高可用,redis-cluster集羣引入了主從模式,一個主節點對應一個或者多個從節點,當主節點宕機的時候,就會啓用從節點。當其它主節點ping一個主節點A時,如果半數以上的主節點與A通信超時,那麼認爲主節點A宕機了。如果主節點A和它的從節點A1都宕機了,那麼該集羣就無法再提供服務了。
      在這裏插入圖片描述
    什麼時候整個集羣不可用(cluster_state:fail)?
     a:如果集羣任意master掛掉,且當前master沒有slave.集羣進入fail狀態,也可以理解成集羣的slot映射[0-16383]不完整時進入fail狀態. ps : redis-3.0.0.rc1加入cluster-require-full-coverage參數,默認關閉,打開集羣兼容部分失敗.
     b:如果集羣超過半數以上master掛掉,無論是否有slave,集羣進入fail狀態.

5.18 項目中的搜索功能是怎麼實現的?

ES +ik分詞

5.19 引入第三方登錄時,怎麼使得你自己的token和第三方的token關聯起來?

方案一:第三方登錄是通過qq、微信、微博等等,第三方系統的賬戶來登錄其他的系統。其他系統會在用戶登錄的時候調用第三方的登錄窗口,用戶在第三方登錄後,第三方系統會返回給我們一個第三方的用戶uid,token,這個像相當於用戶名和密碼了。
我們可以將這個兩個值存在數據庫中當做第三方登錄的用戶名和密碼。下次用戶在通過第三方登錄的時候,我們發現這個uid我們的數據庫以及有了,就知道他是哪個用戶了。
到這裏,事情本應該解決了,但實際上問題纔剛剛開始
方案二:
用戶選擇第三方登錄》第三方登錄成功》系統後臺判斷是否爲第一次登錄,如果是,就創建一個用戶。》創建用戶並登錄成功

5.20 Thread 類中的start() 和 run() 方法有什麼區別?

start()方法被用來啓動新創建的線程,而且start()內部 調用了run()方法,這和直接調用run()方法的效果不一樣。
當你調用run()方法的時候,只會是在原來的線程中調用,沒有新的線程啓 動,
start()方法纔會啓動新線程。

5.21 Java中Runnable和Callable有什麼不同?

Runnable和Callable都代表那些要在不同的線程中執行的任務。Runnable從JDK1.0開始就有了,Callable是在 JDK1.5增加的。

  • 它們的主要區別是Callable的 call() 方法可以返回值和拋出異常,
  • 而Runnable的run()方法沒有這些功能。
  • Callable可以返回裝載有計算結果的Future對象。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章