首頁>技術>

垃圾回收演算法標記演算法:引用計數法可達性分析法(注意GC root的型別,虛擬機器棧和本地方法棧引用的物件、靜態物件、位元組碼物件)回收演算法(複製演算法、標記清除、標記整理)新生代:物件存活率低,採用複製演算法,堆中分為3個區域,Eden、from、to,每次分配物件都在Eden,第一次gc時,把存活物件複製到from,第二次gc把Eden和from的物件複製到to,第三次又把Eden和to的物件複製到from,依次往復。達到一定閾值時,把物件移入老年代。老年代:物件存活率高,標記整理法java類載入機制雙親委託機制:如果一個類載入器收到了類載入請求,它並不會自己先去載入,而是把這個請求委託給父類的載入器去執行,如果父類載入器還存在其父類載入器,則進一步向上委託,依次遞迴,請求最終將到達頂層的啟動類載入器,如果父類載入器可以完成類載入任務,就成功返回,倘若父類載入器無法完成此載入任務,子載入器才會嘗試自己去載入Java的類載入器:根載入器(載入java核心類)、擴充套件類載入器(載入jre擴充套件目錄)、應用類載入器(載入classPath指定目錄的類,自定義類載入器一般繼承此載入器)Android的類載入器:根載入器、BaseDexClassLoader、PathClassLoader(載入安裝到系統的APK)、DexClassLoader(載入指定目錄的dex和apk)java匿名內部類

匿名內部類就是沒有名字的內部類,(其實是有名字的,虛擬機器定位這個類,編譯之後會使用 外部類名$1這樣的名字,數字按順序生成)。

匿名內部類的構造方法由編譯器生成,引數列表包括:

外部類的引用(定義在非靜態域)捕獲的外部變數(方法體中使用的外部final物件)父類的構造引數如果父類也是一個非靜態內部類則還有父類的外部類引用。

注意

不能繼承父類或者實現介面(kotlin中是可以的)不能定義靜態變數和方法會持有外部類的引用,可能會造成記憶體洩露。

拓展:lambda表示式可以替代部分匿名內部類,父類必須是介面,且只有一個方法。

java泛型擦除

使用泛型可以宣告集合儲存的元素型別,取出元素時避免強轉的操作。在java中,編譯完成後泛型引數會被擦除,例如List<String>和List<Integer>編譯完成後都是List型別。

java泛型為什麼會被擦除:執行時記憶體壓力小,不同泛型的List都編譯成同一個型別。泛型不擦除的語言如c#,在方法區就會真實存在各種不同的List型別,壓力就會相對較大。相容性的問題,1.5之前是沒有泛型的,java當時的使用者量很大,為了向下相容。存在的問題:基本型別無法用於泛型,只能用裝箱型別,例如List,裝箱操作有額外的開銷。泛型引數不能用於方法過載,因為編譯完成後泛型被擦除,引數都是一樣的。泛型型別不能當做真實的型別來使用,例如方法引數中有一個泛型T,方法中不能直接new T(),因為編譯之後就是Object,不知道真實的型別。靜態方法無法引用類的泛型,因為類的泛型在例項化的時候才知道。型別強轉的額外開銷。

泛型在特定場景可以透過反射獲取,例如父類有一個泛型引數已經被確定,子類繼承之後可以獲取。例如gson中,解析帶泛型的List,要傳入一個TypeToken,實際上是new了一個子類,透過反射獲取泛型型別。

如何寫出執行緒安全的程式?

執行緒安全的本質,可變資源線上程間共享的問題。關鍵:可變資源執行緒共享

執行緒安全三要素:原子性可見性有序性

所以要保證執行緒安全:

共享不可變資源,final關鍵字的使用。使用純函式(不訪問外部資源),使用ThreadLocal,不共享資源。使用volatile關鍵字保證共享資源的可見性,並禁止指令重排序。操作原子性(加鎖保證操作的互斥性,原子類AtomicXXX的使用,CAS指令如Unsafe.compareAndSwap)Synchronized原理

底層透過一個監視器monitor實現,monitor物件包含一個count計數字段和owner欄位指向獲取鎖的執行緒,當執行緒獲取monitor後,count+1,owner指向執行緒,監視器處於鎖定狀態,其他執行緒不能獲取monitor會進入阻塞狀態,當前執行緒釋放monitor後,其他執行緒可以繼續競爭該鎖。

Java1.6之後對Synchronized進行了一些最佳化:鎖自旋:執行緒的阻塞和喚醒需要 CPU 從使用者態轉為核心態,例如在Synchronized程式碼塊中呼叫wait方法阻塞執行緒,wait會釋放鎖,所謂自旋,就是讓該執行緒執行一段無意義的迴圈指令來等待一段時間,不會被立即掛起,看當前持有鎖的執行緒是否會很快釋放鎖。缺點是需要佔用 CPU,鎖競爭的時間比較長時不實用)偏斜鎖:如果一個執行緒獲得了一個偏向鎖,如果在接下來的一段時間中沒有其他執行緒來競爭鎖,那麼持有偏向鎖的執行緒再次進入或者退出同一個同步程式碼塊,不需要再次進行搶佔鎖和釋放鎖的操作,一旦出現鎖競爭,偏向鎖會被撤銷,並膨脹成輕量級鎖輕量級鎖:對於一塊同步程式碼,雖然有多個不同執行緒會去執行,但是這些執行緒是在不同的時間段交替請求這把鎖物件,也就是不存在鎖競爭的情況。在這種情況下,鎖會保持在輕量級鎖的狀態,從而避免重量級鎖的阻塞和喚醒操作

Synchronized可以修飾靜態方法(鎖物件為位元組碼物件)、例項方法(鎖為例項物件)和程式碼塊,無論是否發生異常虛擬機器都會正常釋放鎖 ReentrantLock發生異常時不能釋放鎖,所以一般需要在finaly程式碼塊中釋放鎖,它包含公平鎖和讀寫鎖等用法,使用更靈活

java虛擬機器記憶體模型虛擬機器棧:執行緒私有,隨執行緒建立而建立。棧裡面是一個一個“棧幀”,每個棧幀對應一次方法呼叫。棧幀中存放了區域性變量表(基本資料型別變數和物件引用)、運算元棧、方法出口等資訊。當棧呼叫深度大於JVM所允許的範圍,會丟擲StackOverflowError的錯誤。本地方法棧:執行緒私有,這部分主要與虛擬機器用到的Native方法相關,一般情況下並不需要關心這部分內容。程式計數器:也叫PC暫存器,JVM支援多個執行緒同時執行,每個執行緒都有自己的程式計數器。倘若當前執行的是 JVM 的方法,則該暫存器中儲存當前執行指令的地址;倘若執行的是native方法,則PC暫存器中為空。(PS:執行緒執行過程中並不都是一口氣執行完,有可能在一個CPU時鐘週期內沒有執行完,由於時間片用完了,所以不得不暫停執行,當下一次獲得CPU資源時,透過程式計數器就知道該從什麼地方開始執行)方法區:方法區存放類的資訊(包括類的位元組碼,類的結構)、常量、靜態變數等。字串常量池就是在方法區中。雖然Java虛擬機器規範把方法區描述為堆的一個邏輯部分,但是它卻有一個別名叫做Non-Heap(非堆),目的是與Java堆區分開來。很多人都更願意把方法區稱為“永久代”(Permanent Generation)。從jdk1.7已經開始準備“去永久代”的規劃,jdk1.7的HotSpot中,已經把原本放在方法區中的靜態變數、字串常量池等移到堆記憶體中。:堆中存放的是陣列(PS:陣列也是物件)和物件。當申請不到空間時會丟擲OutOfMemoryError。class載入過程裝載,將class檔案載入進記憶體,在堆中生成class物件連結,驗證二進位制資料流(類結構是否正確),分配靜態變數設定預設值(初始化時才真正賦值),將符號引用轉換為直接引用初始化,初始化靜態變數,靜態程式碼塊java記憶體模型、volatile的作用記憶體模型本地記憶體:存放的是 私有變數主記憶體資料的副本。如果私有變數是基本資料型別,則直接存放在本地記憶體,如果是引用型別變數,存放的是引用,實際的資料存放在主記憶體。本地記憶體是不共享的,只有屬於它的執行緒可以訪問。主記憶體:存放的是共享的資料,所有執行緒都可以訪問。當然它也有不少其他稱呼,比如 堆記憶體,共享記憶體等等。

Java記憶體模型規定了所有對共享變數的讀寫操作都必須在本地記憶體中進行,需要先從主記憶體中拿到資料,複製到本地記憶體,然後在本地記憶體中對資料進行修改,再重新整理回主記憶體,這就導致了多執行緒情況下資料的可見性問題,可以使用volatile關鍵字來修飾

volatile變數在修改後,會立即重新整理主記憶體的值,對所有執行緒可見,當volatile變數寫後,執行緒中本地記憶體中共享變數就會置為失效的狀態,因此執行緒需要從主記憶體中去讀取該變數的最新值。volatile還可以防止指令重排序造成的執行緒安全問題,例如雙重校驗的懶漢式單例中,不加volatile的物件編譯後的指令有可能重排序成:物件引用已經被賦值不等於null了,但是物件的構造方法還沒有呼叫完成的情況。第二個執行緒去判斷不為空,拿到的物件還未初始化完成造成錯誤。如何安全停止一個執行緒

stop方法,被廢棄。強行停止一個執行緒,沒有資源的機會,如果正在處理任務,會留下一堆異常的資料。另一個執行緒再訪問時就會發生錯誤。那麼如何安全的結束呢: 1、設定volatile的boolean標誌位,修改標誌位來判斷是否繼續執行還是清理現場。 2、Interrupt方法:執行緒內部也需要做支援,判斷是否被中斷,和標誌位類似的處理。支援sleep等系統方法(sleep過程中中斷)。 判斷是否中斷的兩個方法的區別: interrupted,靜態方法,獲取當前正在執行的執行緒是否被中斷,中斷之後會清空狀態,重複獲取就返回false isInterrupted,執行緒的方法,獲取當前執行緒的中斷狀態,不會被清空狀態

HashMap原理

底層是陣列+連結串列的結構,預設陣列長度16,載入因子0.75,在put時,(如果第一次put,會建立陣列)如果元素個數大於陣列長度*載入因子時,將觸發擴容操作,陣列長度翻倍,並重新計算hash將元素放入陣列;

Java1.8中,如果元素過多,陣列長64,連結串列長度超過8,將進行樹化操作,將連結串列轉為紅黑樹,紅黑樹的節點是連結串列節點佔用空間的兩倍,提高查詢效率;

如何計算元素儲存的位置,如何解決hash衝突,為何陣列長度必須為2的整數冪:

先把key取hash值,然後進行一個二次hash,方式為(n-1)&hash,這個二次hash是因為如果n正好等於2的冪,(n-1)&hash相當於對n取模,這樣位運算效率很高,這樣就相當於把元素均勻分佈到了陣列中,如果陣列的位置沒有元素,直接儲存元素,如果已經有元素了,表示發生了hash衝突,將改為連結串列的儲存方式,把新元素放在頭部(1.8中是尾插法)

為什麼載入因子為0.75?設為1和0.5有什麼問題?

loadFactor太大,比如等於1,那麼就會有很高的雜湊衝突的機率,會大大降低查詢速度。 loadFactor太小,比如等於0.5,那麼頻繁擴容沒,就會大大浪費空間。

Hashtable

初始化容量不一樣(11),執行緒安全對整個陣列加鎖,不允許null值,資料結構一直是陣列+連結串列,不會轉換為紅黑樹;

ConcurrentHashMap:

1.5-1.7採用分段鎖segment機制,不再是整個陣列加鎖,而是對單條或者幾條連結串列和紅黑樹進行加鎖。內部結構如圖:segment陣列,segment中類似HashMap的陣列+連結串列。要透過hash讓元素儘可能的均勻分佈到不同的segment和陣列中,所以對key取hash,用高位確定segment的位置,然後用低位確定陣列的位置。

1.5的hash演算法不好,元素多的時候會造成新加的節點分佈在最後的幾個桶,分佈不均勻,

1.6就改善了hash演算法。

1.7的最佳化是採用segment的懶載入機制,並用volatile的方式訪問陣列,保證陣列的執行緒可見性,結合CAS指令來避免加鎖。 1.8中則基於hashmap做最佳化,不再採用分段鎖,而是對桶節點加鎖,使用volatile和CAS樂觀鎖來實現讀和寫,再次提高了效率。

透過對Hashtable和ConcurrentHashMap的比較,得出一些鎖最佳化的方法論,比如大鎖不如小鎖,長鎖不如短鎖,讀寫鎖的分離等等

執行緒池原理執行緒池的引數corePoolSize:執行緒池大小,當向執行緒池提交任務時,如果執行緒池已建立的執行緒數小於corePoolSize,即便此時存在空閒執行緒,也會建立一個新的執行緒來執行任務,直到執行緒數大於或等於corePoolSize。(除了提交新任務來建立執行緒,也可以透過prestartCoreThread或prestartAllCoreThreads來提前建立核心執行緒)maximumPoolSize:執行緒池最大大小,當任務佇列滿了,且已建立的執行緒數小於最大執行緒數,則建立新執行緒來執行任務,如果執行緒池任務佇列為無界佇列可以忽略該引數keepAliveTime:執行緒存活時間,當執行緒數大於核心執行緒數時,執行緒空閒時間大於存活時間,那麼這個執行緒將被銷燬,如果執行緒池任務佇列為無界佇列可以忽略該引數workQueue:任務佇列,用於儲存等待執行任務的阻塞佇列threadFactory:執行緒工廠,用於建立新執行緒,可以設定統一風格的執行緒名handler:執行緒飽和策略,當任務佇列和執行緒池都滿了,繼續提交任務將執行此策略如何配置執行緒池?需要看任務的型別cpu密集型需要配置較小的執行緒數,避免cpu過度切換反而效率低下IO密集型,執行緒池可以稍大,提高cpu的利用率;混合型任務則可配置兩個執行緒池分別來執行;java自帶的執行緒池

執行緒池核心執行緒最大執行緒存活時間任務佇列CachedThreadPool0Integer.MAX_VALUE60SSynchronousQueueFixedThreadPoolnn0LinkedBlockingQueueSingleThreadExecutor110LinkedBlockingQueueScheduledThreadPoolnInteger.MAX_VALUE0DelayWorkQueue

SynchronousQueue:只能有一個元素的佇列,插入和獲取元素都會阻塞執行緒

java方法分派(多型)

子類複寫父類方法,呼叫方法呼叫子類還是父類? 取決於執行時具體的呼叫者型別,例項是子類就呼叫子類的方法。

HTTPS對稱加密和非對稱加密對稱加密:加密和解密使用同一個秘鑰,使用對應的加密和解密演算法進行加解密非對稱加密:加密和解密使用不同的秘鑰,分為公鑰和私鑰,公鑰和私鑰相互可解,意思就是私鑰加密的密文只有公鑰可解,反之亦然。數字簽名技術

非對稱加密在實際使用中,公鑰會公開出來,私鑰儲存在自己手中不公開。由於私鑰加密的密文只有公鑰可解,那麼如果有一個密文用你的公鑰可以解開,那麼可以說明這個密文肯定是你的私鑰加密的,這就誕生了數字簽名技術。

​ 只有資訊的傳送者才能產生的別人無法偽造的一段數字串,這段數字串同時也是對資訊的傳送者傳送資訊真實性的一個有效證明

https的本質

https的本質就是:用非對稱加密的方式協商出一個對稱加密的會話秘鑰來進行會話

首先服務端需要有一個證書,證書包含了自己的公鑰和服務端資訊,如hash演算法、加密演算法、域名、有效期等等。此證書需要由可信任的第三方(CA機構)的私鑰進行簽名,實際上是對證書做一個hash,得到hash值然後簽名,CA機構也可能不止一級而是一個證書鏈為什麼要用第三方機構來頒發證書呢?為了安全的傳輸自己的公鑰,系統都預置了可信任的根證書,三方機構是否可信任由系統來保證客戶端如何校驗CA證書客戶端收到證書後,用證書中的公鑰去解密該Hash值,得到hash-a客戶端用證書中指定的簽名演算法,計算出一個hash-b,比較hash-a和hash-b除了校驗hash值,還會校驗CA證書有效期和域名等SSL握手過程客戶端A訪問服務端B,客戶端生成一個隨機數1、將自己支援的SSL版本號、加密套件(包括雜湊演算法和加密演算法)等資訊傳送給服務端服務端B收到請求,選擇一個加密套件,也生成一個隨機數2,將隨機數和自己的證書一同返回給客戶端客戶端收到證書,校驗證書是否有效(方法之前說過了),透過校驗後,生成一個隨機數3,用證書中的公鑰加密隨機數3,傳送給服務端B服務端收到加密的隨機數,用私鑰解密服務端和客戶端都有了隨機數1、2、3,透過這三個隨機數,生成一個對稱加密的會話金鑰服務端和客戶端分別通知對方之後的會話用會話秘鑰來完成,握手結束為什麼要用非對稱加密來握手,而用對稱加密來會話

對稱加密握手的話,由於雙方的秘鑰是一樣的,相當於秘鑰公開了,和沒加密沒有區別

而會話階段,對稱加密效率較非對稱高

TCP為什麼要三次握手和四次揮手“三次握手”的目的是“為了防止已失效的連線請求報文段突然又傳送到了服務端,因而產生錯誤”。例如:第一次請求由於網路擁堵沒有到達服務端,客戶端又發起第二次請求,正常完成了連線,傳輸完資料之後斷開,這時第一次的請求到達了服務端,如果沒有第三次握手,會直接建立一條沒有用的連線,server端一直等待,浪費資源。“四次揮手”原因是因為tcp是全雙工模式,接收到FIN時意味對方將沒有資料再發來,但是自己還是可以繼續傳送資料。為什麼TCP是可靠的?TCP基於連線,具有以下機制:確認和重傳:接收方收到報文後就會進行確認,傳送方一段時間沒有收到確認就會重傳。資料校驗。資料合理分片與排序,TCP會對資料進行分片,接收方會快取為按序到達的資料,重新排序後再提交給應用層。流程控制:當接收方來不及接收發送的資料時,則會提示傳送方降低傳送的速度,防止包丟失。擁塞控制:當網路發生擁塞時,減少資料的傳送。

UDP是無連線、不安全的,每個資料包都包含接收的ip等資訊,客戶端只管傳送,沒有確認重傳機制,所以速度更快,但是可能會丟包。

HTTP1.0、1.1、2.0的區別1.1和1.0:增加新的控制快取策略的Header,如Entity tag,If-Unmodified-Since, If-Match, If-None-Match;增加了range請求頭,允許請求資源的一部分,支援了多執行緒斷點續傳下載,優化了頻寬和連線;增加了Host頭,允許一臺物理伺服器上存在多個虛擬主機,共享一個IP地址,透過Host來區分;增加了keep-alive支援TCP長連線,一定程度彌補了每次請求都重新建立連線的情況;SPDY:

SPDY是Http1.x版本的最佳化方案,包括多路複用技術、請求優先順序(多路複用時,多個請求並行於共用的TCP連線,可以設定請求的優先順序防止關鍵請求被阻塞)、header壓縮和服務端推送功能;SPDY的特性併入了Http2.0中;

1.1和2.0:支援了新的二進位制格式,1.x版本只支援文字協議多路複用技術,在HTTP/1.1協議中,同一時間針對同一域名下的請求有一定數量限制,超過限制數目的請求會被阻塞。多個請求是序列處理,當一個請求超時,後續請求就會被阻塞,而在2.0中,一個TCP連線上並行多個請求,某個請求耗時不影響其他連線;Header壓縮,多個請求可以差量更新Header欄位,降低流量提高效率;服務端推送功能三方授權方式Basic:格式:Authorization: Basic username:password(Base64ed)Bearer:格式:Authorization: Bearerbearer token 的獲取⽅式( OAuth2 的授權流程):第三⽅⽹站向授權⽅⽹站申請第三⽅授權合作,拿到 client id 和 client secret⽤戶在使⽤第三⽅⽹站時,點選「透過 XX (如 GitHub) 授權」按鈕,第三⽅⽹站將⻚⾯跳轉到授權⽅⽹站,並傳⼊ client id 作為⾃⼰的身份標識授權⽅⽹站根據 client id ,將第三⽅⽹站的資訊和需要的⽤戶許可權展示給⽤戶,詢問⽤戶是否同意授權⽤戶點選「同意授權」按鈕後,授權⽅⽹站將⻚⾯跳轉回第三⽅⽹站,並傳⼊ Authorization code 作為⽤戶認可的憑證。第三⽅⽹站將 Authorization code 傳送回⾃⼰的伺服器伺服器將 Authorization code 和 client secret ⼀併發送給授權⽅的伺服器,授權⽅返回 access token。WebSocket和Socket的區別WebSocket是應用層的一個持久化協議,http它一次請求和響應就斷開連線,屬於非持久化協議。WebSocket分為握手和資料傳輸兩個階段,採用http協議握手然後建立全雙工的tcp連線。Socket是傳輸層的一個協議抽象,包括TCP和UDP,TCP基於連線,擁有確認和重傳,擁塞控制和流程控制等機制的可靠的協議。UDP則面向無連線,基於資料報,相對於TCP速度快但不可靠。多執行緒下載和斷點續傳

兩個核心Header,Content-Length表示檔案的總位元組數,RANGE表示從某一個位置開始傳輸。

首先,獲取到檔案大小後,透過執行緒數來計算每個執行緒下載的開始位置。

然後,透過range來設定從哪個位置傳輸。

當暫停或者退出時,記錄已下載的位置,下次恢復後從記錄的位置下載。

使用RandAccessFile來儲存檔案,這個類的特點是可以透過移動檔案指標來設定寫入的位置。

這裡給大家提供一個方向,進行體系化的學習:

1、看影片進行系統學習

前幾年的Crud經歷,讓我明白自己真的算是菜雞中的戰鬥機,也正因為Crud,導致自己技術比較零散,也不夠深入不夠系統,所以重新進行學習是很有必要的。我差的是系統知識,差的結構框架和思路,所以透過影片來學習,效果更好,也更全面。關於影片學習,個人可以推薦去B站進行學習,B站上有很多學習影片,唯一的缺點就是免費的容易過時。

另外,我自己也珍藏了好幾套影片,有需要的我也可以分享給你。

2、進行系統梳理知識,提升儲備

客戶端開發的知識點就那麼多,面試問來問去還是那麼點東西。所以面試沒有其他的訣竅,只看你對這些知識點準備的充分程度。so,出去面試時先看看自己複習到了哪個階段就好。

系統學習方向:

架構師築基必備技能:深入Java泛型+註解深入淺出+併發程式設計+資料傳輸與序列化+Java虛擬機器原理+反射與類載入+動態代理+高效IOAndroid高階UI與FrameWork原始碼:高階UI晉升+Framework核心解析+Android元件核心+資料持久化360°全方面效能調優:設計思想與程式碼質量最佳化+程式效能最佳化+開發效率最佳化解讀開源框架設計思想:熱修復設計+外掛化框架解讀+元件化框架設計+圖片載入框架+網路訪問框架設計+RXJava響應式程式設計框架設計+IOC架構設計+Android架構元件JetpackNDK模組開發:NDK基礎知識體系+底層圖片處理+音影片開發微信小程式:小程式介紹+UI開發+API操作+微信對接Hybrid 開發與Flutter:Html5專案實戰+Flutter進階

知識梳理完之後,就需要進行查漏補缺,所以針對這些知識點,我手頭上也準備了不少的電子書和筆記,這些筆記將各個知識點進行了完美的總結。

3、讀原始碼,看實戰筆記,學習大神思路

“程式語言是程式設計師的表達的方式,而架構是程式設計師對世界的認知”。所以,程式設計師要想快速認知並學習架構,讀原始碼是必不可少的。閱讀原始碼,是解決問題 + 理解事物,更重要的:看到原始碼背後的想法;程式設計師說:讀萬行原始碼,行萬種實踐。

4、面試前夕,刷題衝刺

面試的前一週時間內,就可以開始刷題衝刺了。請記住,刷題的時候,技術的優先,演算法的看些基本的,比如排序等即可,而智力題,除非是校招,否則一般不怎麼會問。

關於面試刷題,我個人也準備了一套系統的面試題,幫助你舉一反三:

總結

改變人生,沒有什麼捷徑可言,這條路需要自己親自去走一走,只有深入思考,不斷反思總結,保持學習的熱情,一步一步構建自己完整的知識體系,才是最終的制勝之道,也是程式設計師應該承擔的使命。

18
最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • 前端工程師不得不懂的架構知識,值得收藏(上)