2021-03-04

前言

相信很多同學(xué)都會(huì)有這樣的感受卧须,前三天剛剛復(fù)習(xí)的知識(shí)點(diǎn)宅此,今天問的時(shí)候怎么就講不出個(gè)所以然了呢袍暴?

本文的目的就是致力于幫助大家盡可能的建立Android知識(shí)體系,希望大家會(huì)喜歡~

必讀

知識(shí)結(jié)構(gòu)

覆蓋的知識(shí)點(diǎn)有Android抢蚀、Java研铆、Kotlin拌汇、Jvm瞧预、網(wǎng)絡(luò)和設(shè)計(jì)模式艺谆。

面向人群

正在求職的中高級(jí)Android開發(fā)

食用指南

和大部分人一樣榨惰,我在復(fù)習(xí)完第一遍Android知識(shí)的情況下,看到相關(guān)的知識(shí)回答的仍然不能夠令自己滿意静汤。

在第二遍系統(tǒng)復(fù)習(xí)的時(shí)候琅催,我著重記住每個(gè)知識(shí)點(diǎn)的關(guān)鍵字,根據(jù)這些關(guān)鍵字拼湊出大概的知識(shí)點(diǎn)虫给,最后看到每個(gè)知識(shí)點(diǎn)的時(shí)候藤抡,就知道大概會(huì)問哪些內(nèi)容,達(dá)到這種境界以后抹估,你就可以從容的面對(duì)每次面試了缠黍。

簡單的做法就是為每個(gè)知識(shí)點(diǎn)建立腦圖,盡可能把自己想到的關(guān)鍵點(diǎn)羅列出來棋蚌,也就是下面每個(gè)章節(jié)前面的腦圖嫁佳。

除此以外,我還為大家提供了可能會(huì)問到的面試題谷暮。

一蒿往、Android基礎(chǔ)

圖片.png

Android基礎(chǔ)知識(shí)點(diǎn)比較多,看圖湿弦。

[圖片上傳失敗...(image-10132e-1614839133175)]

建議閱讀:

《Android開發(fā)藝術(shù)探索》

1. Activity

# Activity的四大啟動(dòng)模式瓤漏,以及應(yīng)用場景?

Activity的四大啟動(dòng)模式:

  • standard:標(biāo)準(zhǔn)模式颊埃,每次都會(huì)在活動(dòng)棧中生成一個(gè)新的Activity實(shí)例蔬充。通常我們使用的活動(dòng)都是標(biāo)準(zhǔn)模式。
  • singleTop:棧頂復(fù)用班利,如果Activity實(shí)例已經(jīng)存在棧頂饥漫,那么就不會(huì)在活動(dòng)棧中創(chuàng)建新的實(shí)例。比較常見的場景就是給通知跳轉(zhuǎn)的Activity設(shè)置罗标,因?yàn)槟憧隙ú幌肭芭_(tái)Activity已經(jīng)是該Activity的情況下庸队,點(diǎn)擊通知,又給你再創(chuàng)建一個(gè)同樣的Activity闯割。
  • singleTask:棧內(nèi)復(fù)用彻消,如果Activity實(shí)例在當(dāng)前棧中已經(jīng)存在,就會(huì)將當(dāng)前Activity實(shí)例上面的其他Activity實(shí)例都移除棧宙拉。常見于跳轉(zhuǎn)到主界面宾尚。
  • singleInstance:單實(shí)例模式,創(chuàng)建一個(gè)新的任務(wù)棧谢澈,這個(gè)活動(dòng)實(shí)例獨(dú)自處在這個(gè)活動(dòng)棧中煌贴。

# Activity中onStart和onResume的區(qū)別御板?onPause和onStop的區(qū)別?

首先牛郑,Activity有三類:

  • 前臺(tái)Activity:活躍的Activity稳吮,正在和用戶交互的Activity。
  • 可見但非前臺(tái)的Activity:常見于棧頂?shù)腁ctivity背景透明井濒,處在其下面的Activity就是可見但是不可和用戶交互灶似。
  • 后臺(tái)Activity:已經(jīng)被暫停的Activity,比如已經(jīng)執(zhí)行了onStop方法瑞你。

所以酪惭,onStart和onStop通常指的是當(dāng)前活動(dòng)是否位于前臺(tái)這個(gè)角度,而onResume和onPause從是否可見這個(gè)角度來講的者甲。

2. 屏幕適配

# 平時(shí)如何有使用屏幕適配嗎春感?原理是什么呢?

平時(shí)的屏幕適配一般采用的頭條的屏幕適配方案虏缸。簡單來說鲫懒,以屏幕的一邊作為適配,通常是寬刽辙。

原理:設(shè)備像素px和設(shè)備獨(dú)立像素dp之間的關(guān)系是

px = dp * density
復(fù)制代碼

假設(shè)UI給的設(shè)計(jì)圖屏幕寬度基于360dp窥岩,那么設(shè)備寬的像素點(diǎn)已知,即px宰缤,dp也已知颂翼,360dp,所以density = px / dp慨灭,之后根據(jù)這個(gè)修改系統(tǒng)中跟density相關(guān)的知識(shí)點(diǎn)即可朦乏。

3. Android消息機(jī)制

# Android消息機(jī)制介紹?

Android消息機(jī)制中的四大概念:

  • ThreadLocal:當(dāng)前線程存儲(chǔ)的數(shù)據(jù)僅能從當(dāng)前線程取出氧骤。
  • MessageQueue:具有時(shí)間優(yōu)先級(jí)的消息隊(duì)列呻疹。
  • Looper:輪詢消息隊(duì)列,看是否有新的消息到來筹陵。
  • Handler:具體處理邏輯的地方刽锤。

過程:

  1. 準(zhǔn)備工作:創(chuàng)建Handler,如果是在子線程中創(chuàng)建惶翻,還需要調(diào)用Looper#prepare()姑蓝,在Handler的構(gòu)造函數(shù)中鹅心,會(huì)綁定其中的Looper和MessageQueue吕粗。
  2. 發(fā)送消息:創(chuàng)建消息,使用Handler發(fā)送旭愧。
  3. 進(jìn)入MessageQueue:因?yàn)镠andler中綁定著消息隊(duì)列颅筋,所以Message很自然的被放進(jìn)消息隊(duì)列宙暇。
  4. Looper輪詢消息隊(duì)列:Looper是一個(gè)死循環(huán),一直觀察有沒有新的消息到來议泵,之后從Message取出綁定的Handler占贫,最后調(diào)用Handler中的處理邏輯,這一切都發(fā)生在Looper循環(huán)的線程先口,這也是Handler能夠在指定線程處理任務(wù)的原因型奥。

# Looper在主線程中死循環(huán)為什么沒有導(dǎo)致界面的卡死?

  1. 導(dǎo)致卡死的是在Ui線程中執(zhí)行耗時(shí)操作導(dǎo)致界面出現(xiàn)掉幀碉京,甚至ANR厢汹,Looper.loop()這個(gè)操作本身不會(huì)導(dǎo)致這個(gè)情況。
  2. 有人可能會(huì)說谐宙,我在點(diǎn)擊事件中設(shè)置死循環(huán)會(huì)導(dǎo)致界面卡死烫葬,同樣都是死循環(huán),不都一樣的嗎凡蜻?Looper會(huì)在沒有消息的時(shí)候阻塞當(dāng)前線程搭综,釋放CPU資源,等到有消息到來的時(shí)候划栓,再喚醒主線程兑巾。
  3. App進(jìn)程中是需要死循環(huán)的,如果循環(huán)結(jié)束的話忠荞,App進(jìn)程就結(jié)束了闪朱。

建議閱讀:

《Android中為什么主線程不會(huì)因?yàn)長ooper.loop()里的死循環(huán)卡死?》

https://www.zhihu.com/question/34652589

# IdleHandler介紹钻洒?

介紹: IdleHandler是在Hanlder空閑時(shí)處理空閑任務(wù)的一種機(jī)制奋姿。

執(zhí)行場景:

  • MessageQueue沒有消息,隊(duì)列為空的時(shí)候素标。
  • MessageQueue屬于延遲消息称诗,當(dāng)前沒有消息執(zhí)行的時(shí)候。

會(huì)不會(huì)發(fā)生死循環(huán): 答案是否定的头遭,MessageQueue使用計(jì)數(shù)的方法保證一次調(diào)用MessageQueue#next方法只會(huì)使用一次的IdleHandler集合寓免。

4. View事件分發(fā)機(jī)制和View繪制原理

剛哥的《Android開發(fā)藝術(shù)探索》已經(jīng)很全面了,建議閱讀计维。

5. Bitmap

# Bitmap的內(nèi)存計(jì)算方式袜香?

在已知圖片的長和寬的像素的情況下,影響內(nèi)存大小的因素會(huì)有資源文件位置和像素點(diǎn)大小鲫惶。

像素點(diǎn)大小: 常見的像素點(diǎn)有:

  • ARGB_8888:4個(gè)字節(jié)
  • ARGB_4444蜈首、ARGB_565:2個(gè)字節(jié)

資源文件位置: 不同dpi對(duì)應(yīng)存放的文件夾

[圖片上傳失敗...(image-9d3471-1614839133175)]

比如一個(gè)一張圖片的像素為180*180px,dpi(設(shè)備獨(dú)立像素密度)為320,如果它僅僅存放在drawable-hdpi欢策,則有:

橫向像素點(diǎn) = 180 * 320/240 + 0.5f = 240 px
縱向像素點(diǎn) = 180 * 320/240 + 0.5f = 240 px
復(fù)制代碼

如果 如果它僅僅存放在drawable-xxhdpi吆寨,則有:

橫向像素點(diǎn) = 180 * 320/480 + 0.5f = 120 px
縱向像素點(diǎn) = 180 * 320/480 + 0.5f = 120 px
復(fù)制代碼

所以,對(duì)于一張180*180px的圖片踩寇,設(shè)備dpi為320啄清,資源圖片僅僅存在drawable-hdpi,像素點(diǎn)大小為ARGB_4444俺孙,最后生成的文件內(nèi)存大小為:

橫向像素點(diǎn) = 180 * 320/240 + 0.5f = 240 px
縱向像素點(diǎn) = 180 * 320/240 + 0.5f = 240 px
內(nèi)存大小 = 240 * 240 * 2 = 115200byte 約等于 112.5kb
復(fù)制代碼

建議閱讀:

《Android Bitmap的內(nèi)存大小是如何計(jì)算的辣卒?》

# Bitmap的高效加載?

Bitmap的高效加載在Glide中也用到了睛榄,思路:

  1. 獲取需要的長和寬添寺,一般獲取<typo id="typo-3160" data-origin="控件" ignoretag="true">控件</typo>的長和寬。
  2. 設(shè)置BitmapFactory.Options中的inJustDecodeBounds為true懈费,可以幫助我們?cè)诓患虞d進(jìn)內(nèi)存的方式獲得Bitmap的長和寬计露。
  3. 對(duì)需要的長和寬和Bitmap的長和寬進(jìn)行對(duì)比,從而獲得壓縮比例憎乙,放入BitmapFactory.Options中的inSampleSize屬性票罐。
  4. 設(shè)置BitmapFactory.Options中的inJustDecodeBounds為false,將圖片加載進(jìn)內(nèi)存泞边,進(jìn)而設(shè)置到控件中该押。

二、Android進(jìn)階

image

Android進(jìn)階中重點(diǎn)考察Android Framework阵谚、性能優(yōu)化和第三方框架蚕礼。

1. Binder

# Binder的介紹?與其他IPC方式的優(yōu)缺點(diǎn)梢什?

Binder是Android中特有的IPC方式奠蹬,引用《Android開發(fā)藝術(shù)探索》中的話(略有改動(dòng)):

從IPC角度來說,Binder是Android中的一種跨進(jìn)程通信方式嗡午;Binder還可以理解為虛擬的物理設(shè)備囤躁,它的設(shè)備驅(qū)動(dòng)是/dev/binder;從Android Framework來講荔睹,Binder是Service Manager連接各種Manager和對(duì)應(yīng)的ManagerService的橋梁狸演。從面向?qū)ο蠛虲S模型來講,Client通過Binder和遠(yuǎn)程的Server進(jìn)行通訊僻他。

基于Binder宵距,Android還實(shí)現(xiàn)了其他的IPC方式,比如AIDL吨拗、Messenger和ContentProvider满哪。

與其他IPC比較:

  • 效率高:除了內(nèi)存共享外乡摹,其他IPC都需要進(jìn)行兩次數(shù)據(jù)拷貝徘键,而因?yàn)锽inder使用內(nèi)存映射的關(guān)系厕吉,僅需要一次數(shù)據(jù)拷貝序无。
  • 安全性好:接收方可以從數(shù)據(jù)包中獲取發(fā)送發(fā)的進(jìn)程Id和用戶Id携龟,方便驗(yàn)證發(fā)送方的身份兔跌,其他IPC想要實(shí)驗(yàn)只能夠主動(dòng)存入,但是這有可能在發(fā)送的過程中被修改峡蟋。

# Binder的通信過程坟桅?Binder的原理?

[圖片上傳失敗...(image-ace8fb-1614839133175)]

其實(shí)這個(gè)過程也可以從AIDL生成的代碼中看出蕊蝗。

原理:

image

Binder的結(jié)構(gòu): Client:服務(wù)的請(qǐng)求方仅乓。 Server:服務(wù)的提供方。 Service Manager:為Server提供Binder的注冊(cè)服務(wù)蓬戚,為Client提供Binder的查詢服務(wù)夸楣,Server、Client和Service Manager的通訊都是通過Binder子漩。 Binder驅(qū)動(dòng):負(fù)責(zé)Binder通信機(jī)制的建立豫喧,提供一系列底層支持。

從上圖中幢泼,Binder通信的過程是這樣的:

  1. Server在Service Manager中注冊(cè):Server進(jìn)程在創(chuàng)建的時(shí)候紧显,也會(huì)創(chuàng)建對(duì)應(yīng)的Binder實(shí)體,如果要提供服務(wù)給Client缕棵,就必須為Binder實(shí)體注冊(cè)一個(gè)名字孵班。
  2. Client通過Service Manager獲取服務(wù):Client知道服務(wù)中Binder實(shí)體的名字后,通過名字從Service Manager獲取Binder實(shí)體的引用招驴。
  3. Client使用服務(wù)與Server進(jìn)行通信:Client通過調(diào)用Binder實(shí)體與Server進(jìn)行通信篙程。

更詳細(xì)一點(diǎn)?

Binder通信的實(shí)質(zhì)是利用內(nèi)存映射别厘,將用戶進(jìn)程的內(nèi)存地址和內(nèi)核的內(nèi)存地址映射為同一塊物理地址房午,也就是說他們使用的同一塊物理空間,每次創(chuàng)建Binder的時(shí)候大概分配128的空間丹允。數(shù)據(jù)進(jìn)行傳輸?shù)臅r(shí)候郭厌,從這個(gè)內(nèi)存空間分配一點(diǎn),用完了再釋放即可雕蔽。

2. 序列化

# Android有哪些序列化方式折柠?

為了解決Android中內(nèi)存序列化速度過慢的問題,Android使用了Parcelable批狐。

|

對(duì)比

|

Serializable

|

Parcelable

|
|

易用性

|

簡單

|

不是很簡單

|
|

效率

|

|

|
|

場景

|

IO扇售、網(wǎng)絡(luò)和數(shù)據(jù)庫

|

內(nèi)存中

|

3. Framework

Zygote孕育進(jìn)程過程前塔?

[圖片上傳失敗...(image-45a4b3-1614839133175)]

# Activity的啟動(dòng)過程?

[圖片上傳失敗...(image-15c860-1614839133175)]

建議閱讀:

《3分鐘看懂Activity啟動(dòng)流程》

http://www.reibang.com/p/9ecea420eb52

# App的啟動(dòng)過程承冰?

介紹一下App進(jìn)程和System Server進(jìn)程如何聯(lián)系:

[圖片上傳失敗...(image-ef59cd-1614839133175)]

  • ActivityThread:依賴于Ui線程华弓,實(shí)際處理與AMS中交互的工作。
  • ActivityManagerService:負(fù)責(zé)Activity困乒、Service等的生命周期工作寂屏。
  • ApplicationThread:System Server進(jìn)程中ApplicatonThreadProxy的服務(wù)端,幫助System Server進(jìn)程跟App進(jìn)程交流娜搂。
  • System Server:Android核心的進(jìn)程迁霎,掌管著Android系統(tǒng)中各種重要的服務(wù)。

[圖片上傳失敗...(image-6e1346-1614839133175)]

具體過程:

  1. 用戶點(diǎn)擊App圖標(biāo)百宇,Lanuacher進(jìn)程通過Binder聯(lián)系到System Server進(jìn)程發(fā)起startActivity考廉。
  2. System Server通過Socket聯(lián)系到Zygote,fork出一個(gè)新的App進(jìn)程携御。
  3. 創(chuàng)建出一個(gè)新的App進(jìn)程以后昌粤,Zygote啟動(dòng)App進(jìn)程的ActivityThread#main()方法。
  4. 在ActivtiyThread中啄刹,調(diào)用AMS進(jìn)行ApplicationThread的綁定婚苹。
  5. AMS發(fā)送創(chuàng)建Application的消息給ApplicationThread,進(jìn)而轉(zhuǎn)交給ActivityThread中的H鸵膏,它是一個(gè)Handler膊升,接著進(jìn)行Application的創(chuàng)建工作。
  6. AMS以同樣的方式創(chuàng)建Activity谭企,接著就是大家熟悉的創(chuàng)建Activity的工作了廓译。

# Apk的安裝過程?

建議閱讀:

《Android Apk安裝過程分析》

http://www.reibang.com/p/953475cea991

# Activity啟動(dòng)過程跟Window的關(guān)系债查?

建議閱讀:

《簡析Window非区、Activity、DecorView以及ViewRoot之間的錯(cuò)綜關(guān)系》

http://www.reibang.com/p/8766babc40e0

# Activity盹廷、Window征绸、ViewRoot和DecorView之間的關(guān)系?

建議閱讀:

《總結(jié)UI原理和高級(jí)的UI優(yōu)化方式》

https://juejin.cn/post/6844903974294781965

4. Context

# 關(guān)于Context的理解俄占?

建議閱讀:

《Android Context 上下文 你必須知道的一切》

https://blog.csdn.net/lmj623565791/article/details/40481055

5. 斷點(diǎn)續(xù)傳

# 多線程斷點(diǎn)續(xù)傳管怠?

基礎(chǔ)知識(shí):

  • Http基礎(chǔ):在Http請(qǐng)求中,可以加入請(qǐng)求頭Range缸榄,下載指定區(qū)間的文件數(shù)渤弛。
  • RandomAccessFile:支持隨機(jī)訪問,可以從指定位置進(jìn)行數(shù)據(jù)的讀寫甚带。

有了這個(gè)基礎(chǔ)以后她肯,思路就清晰了:

  1. 通過HttpUrlConnection獲取文件長度佳头。
  2. 自己分配好線程進(jìn)行制定區(qū)間的文件數(shù)據(jù)的下載。
  3. 獲取到數(shù)據(jù)流以后晴氨,使用RandomAccessFile進(jìn)行指定位置的讀寫康嘉。

6. 性能優(yōu)化

# 平時(shí)做了哪些性能優(yōu)化?

建議閱讀:

《Android 性能優(yōu)化最佳實(shí)踐》

7. 第三方庫

一定要在熟練使用后再去查看原理籽前。

# Glide

Glide考察的頻率挺高的亭珍,常見的問題有:

  • Glide和其他圖片加載框架的比較?
  • 如何設(shè)計(jì)一個(gè)圖片加載框架聚假?
  • Glide緩存實(shí)現(xiàn)機(jī)制块蚌?
  • Glide如何處理生命周期闰非?
  • ...

建議閱讀:

《Glide最全解析》

https://blog.csdn.net/guolin_blog/category_9268670.html

《面試官:簡歷上最好不要寫Glide膘格,不是問源碼那么簡單》

https://juejin.cn/post/6844903986412126216

# OkHttp

OkHttp常見知識(shí)點(diǎn):

  • 責(zé)任鏈模式
  • interceptors和networkInterceptors的區(qū)別?

建議看一遍源碼财松,過程并不復(fù)雜瘪贱。

# Retrofit

Retrofit常見問題:

  • 設(shè)計(jì)模式和封層解耦的理念
  • 動(dòng)態(tài)代理

建議看一遍源碼,過程并不復(fù)雜辆毡。

# RxJava

RxJava難在各種操作符菜秦,我們了解一下大致的設(shè)計(jì)思想即可。

建議尋找一些RxJava的文章舶掖。

# Android Jetpack(非必須)

我主要閱讀了Android Jetpack中以下庫的源碼:

  • Lifecycle:觀察者模式球昨,組件生命周期中發(fā)送事件。
  • DataBinding:核心就是利用LiveData或者Observablexxx實(shí)現(xiàn)的觀察者模式眨攘,對(duì)16進(jìn)制的狀態(tài)位更新主慰,之后根據(jù)這個(gè)狀態(tài)位去更新對(duì)應(yīng)的內(nèi)容。
  • LiveData:觀察者模式鲫售,事件的生產(chǎn)消費(fèi)模型共螺。
  • ViewModel:借用Activty異常銷毀時(shí)存儲(chǔ)隱藏Fragment的機(jī)制存儲(chǔ)ViewModel,保證數(shù)據(jù)的生命周期盡可能的延長情竹。
  • Paging:設(shè)計(jì)思想藐不。

以后有時(shí)間再給大家做源碼分析。

建議閱讀:

《Android Jetpack源碼分析系列》

https://blog.csdn.net/mq2553299/column/info/24151

8. 插件化和組件化

這個(gè)我基本沒用過秦效,等用過了雏蛮,再和大家分享。

三阱州、Java基礎(chǔ)

[圖片上傳失敗...(image-7f21aa-1614839133175)]

Java基礎(chǔ)中考察頻率比較高的是Object底扳、String、面向?qū)ο蠊钡ⅰ⒓现阅!⒎盒秃头瓷洹?/p>

1. Object

# equals和==的區(qū)別鹊汛?equals和hashcode的關(guān)系?

  • ==:基本類型比較值阱冶,引用類型比較地址刁憋。
  • equals:默認(rèn)情況下,equals作為對(duì)象中的方法木蹬,比較的是地址至耻,不過可以根據(jù)業(yè)務(wù),修改equals方法镊叁。

equals和hashcode之間的關(guān)系:

默認(rèn)情況下尘颓,equals相等,hashcode必相等晦譬,hashcode相等疤苹,equals不是必相等。hashcode基于內(nèi)存地址計(jì)算得出敛腌,可能會(huì)相等卧土,雖然幾率微乎其微。

2. String

# String像樊、StringBuffer和StringBuilder的區(qū)別尤莺?

  • String:String屬于不可變對(duì)象,每次修改都會(huì)生成新的對(duì)象生棍。
  • StringBuilder:可變對(duì)象颤霎,非多線程安全。
  • StringBuffer:可變對(duì)象涂滴,多線程安全友酱。

大部分情況下,效率是:StringBuilder>StringBuffer>String氢妈。

3. 面向?qū)ο蟮奶匦?/h1>

# Java中抽象類和接口的特點(diǎn)粹污?

共同點(diǎn):

  • 抽象類和接口都不能生成具體的實(shí)例。
  • 都是作為上層使用首量。

不同點(diǎn):

  • 抽象類可以有屬性和成員方法壮吩,接口不可以。
  • 一個(gè)類只能繼承一個(gè)類加缘,但是可以實(shí)現(xiàn)多個(gè)接口鸭叙。
  • 抽象類中的變量是普通變量,接口中的變量是靜態(tài)變量拣宏。
  • 抽象類表達(dá)的是is-a的關(guān)系沈贝,接口表達(dá)的是like-a的關(guān)系。

# 關(guān)于多態(tài)的理解勋乾?

多態(tài)是面向?qū)ο蟮娜筇匦裕豪^承宋下、封裝和多態(tài)之一嗡善。

多態(tài)的定義:允許不同類對(duì)同一消息做出響應(yīng)。

多態(tài)存在的條件:

  1. 要有繼承学歧。
  2. 要有復(fù)寫罩引。
  3. 父類引用指向子類對(duì)象。

Java中多態(tài)的實(shí)現(xiàn)方式:接口實(shí)現(xiàn)枝笨,繼承父類進(jìn)行方法重寫袁铐,同一個(gè)類中的方法重載。

4. 集合

# HashMap的特點(diǎn)是什么横浑?HashMap的原理剔桨?

HashMap的特點(diǎn):

  1. 基于Map接口,存放鍵值對(duì)徙融。
  2. 允許key/value為空洒缀。
  3. 非多線程安全。
  4. 不保證有序张咳,也不保證使用的過程中順序不會(huì)改變帝洪。

簡單來講蛇摸,核心是數(shù)組+鏈表/紅黑樹窝趣,HashMap的原理就是存鍵值對(duì)的時(shí)候:

  1. 通過鍵的Hash值確定數(shù)組的位置新蟆。
  2. 找到以后,如果該位置無節(jié)點(diǎn)龙助,直接存放。
  3. 該位置有節(jié)點(diǎn)即位置發(fā)生沖突蛛芥,遍歷該節(jié)點(diǎn)以及后續(xù)的節(jié)點(diǎn)提鸟,比較key值,相等則覆蓋仅淑。
  4. 沒有就新增節(jié)點(diǎn)称勋,默認(rèn)使用鏈表,相連節(jié)點(diǎn)數(shù)超過8的時(shí)候涯竟,在jdk 1.8中會(huì)變成紅黑樹赡鲜。
  5. 如果Hashmap中的數(shù)組使用情況超過一定比例,就會(huì)擴(kuò)容庐船,默認(rèn)擴(kuò)容兩倍银酬。

當(dāng)然這是存入的過程,其他過程可以自行查閱筐钟。這里需要注意的是:

  • key的hash值計(jì)算過程是高16位不變揩瞪,低16位和高16位取抑或,讓更多位參與進(jìn)來篓冲,可以有效的減少碰撞的發(fā)生李破。
  • 初始數(shù)組容量為16宠哄,默認(rèn)不超過的比例為0.75。

5. 泛型

# 說一下對(duì)泛型的理解嗤攻?

泛型的本質(zhì)是參數(shù)化類型琳拨,在不創(chuàng)建新的類型的情況下,通過泛型指定不同的類型來控制形參具體限制的類型屯曹。也就是說在泛型的使用中狱庇,操作的數(shù)據(jù)類型被指定為一個(gè)參數(shù),這種參數(shù)可以被用在類恶耽、接口和方法中密任,分別被稱為泛型類、泛型接口和泛型方法偷俭。

泛型是Java中的一種語法糖浪讳,能夠在代碼編寫的時(shí)候起到類型檢測(cè)的作用,但是虛擬機(jī)是不支持這些語法的涌萤。

泛型的優(yōu)點(diǎn):

  1. 類型安全淹遵,避免類型的強(qiáng)轉(zhuǎn)。
  2. 提高了代碼的可讀性负溪,不必要等到運(yùn)行的時(shí)候才去強(qiáng)制轉(zhuǎn)換透揣。

# 什么是類型擦除?

不管泛型的類型傳入哪一種類型實(shí)參川抡,對(duì)于Java來說辐真,都會(huì)被當(dāng)成同一類處理,在內(nèi)存中也只占用一塊空間崖堤。通俗一點(diǎn)來說侍咱,就是泛型只作用于代碼編譯階段,在編譯過程中密幔,對(duì)于正確檢驗(yàn)泛型結(jié)果后楔脯,會(huì)將泛型的信息擦除,也就是說胯甩,成功編譯過后的class文件是不包含任何泛型信息的昧廷。

6. 反射

# 動(dòng)態(tài)代理和靜態(tài)代理

靜態(tài)代理很簡單,運(yùn)用的就是代理模式:

[圖片上傳失敗...(image-283c4b-1614839133174)]

聲明一個(gè)接口蜡豹,再分別實(shí)現(xiàn)一個(gè)真實(shí)的主題類和代理主題類麸粮,通過讓代理類持有真實(shí)主題類,從而控制用戶對(duì)真實(shí)主題的訪問镜廉。

動(dòng)態(tài)代理指的是在運(yùn)行時(shí)動(dòng)態(tài)生成代理類弄诲,即代理類的字節(jié)碼在運(yùn)行時(shí)生成并載入當(dāng)前的ClassLoader。

動(dòng)態(tài)代理的原理是使用反射,思路和上面的一致齐遵。

使用動(dòng)態(tài)代理的好處:

  1. 不需要為RealSubject寫一個(gè)形式完全一樣的代理類寂玲。
  2. 使用一些動(dòng)態(tài)代理的方法可以在運(yùn)行時(shí)制定代理類的邏輯,從而提升系統(tǒng)的靈活性梗摇。

四拓哟、Java并發(fā)

[圖片上傳失敗...(image-f6cd44-1614839133174)]

Java并發(fā)中考察頻率較高的有線程、線程池伶授、鎖断序、線程間的等待和喚醒、線程特性和阻塞隊(duì)列等糜烹。

1. 線程

# 線程的狀態(tài)有哪些(待修改)违诗?

線程的狀態(tài)有:

  • new:新創(chuàng)建的線程
  • Ready:準(zhǔn)備就緒的線程,由于CPU分配的時(shí)間片的關(guān)系疮蹦,此時(shí)的任務(wù)不在執(zhí)行過程中诸迟。
  • Running:正在執(zhí)行的任務(wù)
  • Block:被阻塞的任務(wù)
  • Time Waiting:<typo id="typo-9285" data-origin="計(jì)時(shí)" ignoretag="true">計(jì)時(shí)</typo>等待的任務(wù)
  • Terminated:終止的任務(wù)

附上一張狀態(tài)轉(zhuǎn)換的圖:

[圖片上傳失敗...(image-152d7d-1614839133174)]

# 線程中wait和sleep的區(qū)別?

wait方法既釋放cpu愕乎,又釋放鎖阵苇。 sleep方法只釋放cpu,但是不釋放鎖感论。

# 線程和進(jìn)程的區(qū)別绅项?

線程是CPU調(diào)度的最小單位,一個(gè)進(jìn)程中可以包含多個(gè)線程笛粘,在Android中趁怔,一個(gè)進(jìn)程通常是一個(gè)App湿硝,App中會(huì)有一個(gè)主線程薪前,主線程可以用來操作界面元素,如果有耗時(shí)的操作关斜,必須開啟子線程執(zhí)行示括,不然會(huì)出現(xiàn)ANR,除此以外痢畜,進(jìn)程間的數(shù)據(jù)是獨(dú)立的垛膝,線程間的數(shù)據(jù)可以共享。

2. 線程池

線程池的地位十分重要丁稀,基本上涉及到跨線程的框架都使用到了線程池吼拥,比如說OkHttp、RxJava线衫、LiveData以及協(xié)程等凿可。

# 與新建一個(gè)線程相比,線程池的特點(diǎn)?

  1. 節(jié)省開銷: 線程池中的線程可以重復(fù)利用枯跑。
  2. 速度快:任務(wù)來了就能開始惨驶,省去創(chuàng)建線程的時(shí)間。
  3. 線程可控:線程數(shù)量<typo id="typo-9688" data-origin="可空" ignoretag="true">可空</typo>和任務(wù)可控敛助。
  4. 功能強(qiáng)大:可以定時(shí)和重復(fù)執(zhí)行任務(wù)粗卜。

# 線程池中的幾個(gè)參數(shù)是什么意思,線程池的種類有哪些纳击?

線程池的構(gòu)造函數(shù)如下:

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
}
復(fù)制代碼

參數(shù)解釋如下:

  • corePoolSize:核心線程數(shù)量续扔,不會(huì)釋放。
  • maximumPoolSize :允許使用的最大線程池?cái)?shù)量焕数,非核心線程數(shù)量测砂,閑置時(shí)會(huì)釋放。
  • keepAliveTime :閑置線程允許的最大閑置時(shí)間百匆。
  • unit :閑置時(shí)間的單位砌些。
  • workQueue :阻塞隊(duì)列,不同的阻塞隊(duì)列有不同的特性加匈。

線程池分為四個(gè)類型:

  • CachedThreadPool:閑置線程超時(shí)會(huì)釋放存璃,沒有閑置線程的情況下,每次都會(huì)創(chuàng)建新的線程雕拼。
  • FixedThreadPool:線程池只能存放指定數(shù)量的線程池纵东,線程不會(huì)釋放,可重復(fù)利用啥寇。
  • SingleThreadExecutor:單線程的線程池偎球。
  • ScheduledThreadPool:可定時(shí)和重復(fù)執(zhí)行的線程池。

# 線程池的工作流程辑甜?

image

圖片來自《線程池是怎樣工作的》

簡而言之:

  1. 任務(wù)來了衰絮,優(yōu)先考慮核心線程。
  2. 核心線程滿了磷醋,進(jìn)入阻塞隊(duì)列猫牡。
  3. 阻塞隊(duì)列滿了,考慮非核心線程(圖上好像少了這個(gè)過程)邓线。
  4. 非核心線程滿了淌友,再觸發(fā)拒絕任務(wù)。

3. 鎖

# 死鎖觸發(fā)的四大條件骇陈?

  1. 互斥鎖
  2. 請(qǐng)求與保持
  3. 不可剝奪
  4. 循環(huán)的請(qǐng)求與等待

# synchronized關(guān)鍵字的使用震庭?synchronized的參數(shù)放入對(duì)象和Class有什么區(qū)別?

synchronized關(guān)鍵字的用法:

  • 修飾方法
  • 修飾代碼塊:需要自己提供鎖對(duì)象你雌,鎖對(duì)象包括對(duì)象本身器联、對(duì)象的Class和其他對(duì)象。

放入對(duì)象和Class的區(qū)別是:

  1. 鎖住的對(duì)象不同:成員方法鎖住的實(shí)例對(duì)象,靜態(tài)方法鎖住的是Class主籍。
  2. 訪問控制不同:如果鎖住的是實(shí)例习贫,只會(huì)針對(duì)同一個(gè)對(duì)象方法進(jìn)行同步訪問,多線程訪問同一個(gè)對(duì)象的synchronized代碼塊是串行的千元,訪問不同對(duì)象是并行的苫昌。如果鎖住的是類,多線程訪問的不管是同一對(duì)象還是不同對(duì)象的synchronized代碼塊是都是串行的幸海。

# synchronized的原理祟身?

任何一個(gè)對(duì)象都有一個(gè)monitor與之相關(guān)聯(lián),JVM基于進(jìn)入和退出mointor對(duì)象來實(shí)現(xiàn)代碼塊同步和方法同步物独,兩者實(shí)現(xiàn)細(xì)節(jié)不同:

  • 代碼塊同步:在編譯字節(jié)碼的時(shí)候袜硫,代碼塊起始的地方插入monitorenter

指令,異常和代碼塊結(jié)束處插入monitorexit指令挡篓,線程在執(zhí)行monitorenter指令的時(shí)候嘗試獲取monitor對(duì)象的所有權(quán)婉陷,獲取不到的情況下就是阻塞

  • 方法同步:synchronized方法在method_info結(jié)構(gòu)有AAC_synchronized標(biāo)記,線程在執(zhí)行的時(shí)候獲取對(duì)應(yīng)的鎖官研,從而實(shí)現(xiàn)同步方法

# synchronized和Lock的區(qū)別秽澳?

主要區(qū)別:

  1. synchronized是Java中的關(guān)鍵字,是Java的內(nèi)置實(shí)現(xiàn)戏羽;Lock是Java中的接口担神。
  2. synchronized遇到異常會(huì)釋放鎖;Lock需要在發(fā)生異常的時(shí)候調(diào)用成員方法Lock#unlock()方法始花。
  3. synchronized是不可以中斷的妄讯,Lock可中斷。
  4. synchronized不能去嘗試獲得鎖酷宵,沒有獲得鎖就會(huì)被阻塞亥贸; Lock可以去嘗試獲得鎖,如果未獲得可以嘗試處理其他邏輯忧吟。
  5. synchronized多線程效率不如Lock砌函,不過Java在1.6以后已經(jīng)對(duì)synchronized進(jìn)行大量的優(yōu)化,所以性能上來講溜族,其實(shí)差不了多少。

# 悲觀鎖和樂觀鎖的舉例垦沉?以及它們的相關(guān)實(shí)現(xiàn)煌抒?

悲觀鎖和樂觀鎖的概念:

  • 悲觀鎖:悲觀鎖會(huì)認(rèn)為,修改共享數(shù)據(jù)的時(shí)候其他線程也會(huì)修改數(shù)據(jù)厕倍,因此只在不會(huì)受到其他線程干擾的情況下執(zhí)行寡壮。這樣會(huì)導(dǎo)致其他有需要鎖的線程掛起,等到持有鎖的線程釋放鎖
  • 樂觀鎖:每次不加鎖,每次直接修改共享數(shù)據(jù)假設(shè)其他線程不會(huì)修改况既,如果發(fā)生沖突就直接重試这溅,直到成功為止

舉例:

  • 悲觀鎖:典型的悲觀鎖是獨(dú)占鎖,有synchronized棒仍、ReentrantLock悲靴。
  • 樂觀鎖:典型的樂觀鎖是CAS悦冀,實(shí)現(xiàn)CAS的atomic為代表的一系列類

# CAS是什么下愈?底層原理啊送?

CAS全稱Compare And Set墙牌,核心的三個(gè)元素是:內(nèi)存位置荐捻、預(yù)期原值和新值斯撮,執(zhí)行CAS的時(shí)候始藕,會(huì)將內(nèi)存位置的值與預(yù)期原值進(jìn)行比較绽榛,如果一致憨颠,就將原值更新為新值胳徽,否則就不更新。 底層原理:是借助CPU底層指令cmpxchg實(shí)現(xiàn)原子操作爽彤。

4. 線程間通信

# notify和notifyAll方法的區(qū)別膜廊?

notify隨機(jī)喚醒一個(gè)線程,notifyAll喚醒所有等待的線程淫茵,讓他們競爭鎖爪瓜。

# wait/notify和Condition類實(shí)現(xiàn)的等待通知有什么區(qū)別?

synchronized與wait/notify結(jié)合的等待通知只有一個(gè)條件匙瘪,而Condition類可以實(shí)現(xiàn)多個(gè)條件等待铆铆。

5. 多線程間的特性

# 多線程間的有序性、可見性和原子性是什么意思丹喻?

  • 原子性:執(zhí)行一個(gè)或者多個(gè)操作的時(shí)候薄货,要么全部執(zhí)行,要么都不執(zhí)行碍论,并且中間過程中不會(huì)被打斷谅猾。Java中的原子性可以通過獨(dú)占鎖和CAS去保證
  • 可見性:指多線程訪問同一個(gè)變量的時(shí)候,一個(gè)線程修改了變量的值鳍悠,其他線程能夠立刻看得到修改的值税娜。鎖和volatile能夠保證可見性
  • 有序性:程序執(zhí)行的順序按照代碼先后的順序執(zhí)行。鎖和volatile能夠保證有序性

# happens-before原則有哪些藏研?

Java內(nèi)存模型具有一些先天的有序性敬矩,它通常叫做happens-before原則。

如果兩個(gè)操作的先后順序不能通過happens-before原則推倒出來蠢挡,那就不能保證它們的先后執(zhí)行順序弧岳,虛擬機(jī)就可以隨意打亂執(zhí)行指令凳忙。happens-before原則有:

  1. 程序次序規(guī)則:單線程程序的執(zhí)行結(jié)果得和看上去代碼執(zhí)行的結(jié)果要一致。
  2. 鎖定規(guī)則:一個(gè)鎖的lock操作一定發(fā)生在上一個(gè)unlock操作之后禽炬。
  3. volatile規(guī)則:對(duì)volatile變量的寫操作一定先行于后面對(duì)這個(gè)變量的對(duì)操作涧卵。
  4. 傳遞規(guī)則:A發(fā)生在B前面,B發(fā)生在C前面腹尖,那么A一定發(fā)生在C前面柳恐。
  5. 線程啟動(dòng)規(guī)則:線程的start方法先行發(fā)生于線程中的每個(gè)動(dòng)作。
  6. 線程中斷規(guī)則:對(duì)線程的interrupt操作先行發(fā)生于中斷線程的檢測(cè)代碼桐臊。
  7. 線程終結(jié)原則:線程中所有的操作都先行發(fā)生于線程的終止檢測(cè)胎撤。
  8. 對(duì)象終止原則:一個(gè)對(duì)象的初始化先行發(fā)生于他的finalize()方法的執(zhí)行。

前四條規(guī)則比較重要断凶。

# volatile的原理伤提?

可見性 如果對(duì)聲明了volatile的變量進(jìn)行寫操作的時(shí)候,JVM會(huì)向處理器發(fā)送一條Lock前綴的指令认烁,將這個(gè)變量所在緩存行的數(shù)據(jù)寫入到系統(tǒng)內(nèi)存肿男。

多處理器的環(huán)境下,其他處理器的緩存還是舊的却嗡,為了保證各個(gè)處理器一致舶沛,會(huì)通過嗅探在總線上傳播的數(shù)據(jù)來檢測(cè)自己的數(shù)據(jù)是否過期,如果過期窗价,會(huì)強(qiáng)制重新將系統(tǒng)內(nèi)存的數(shù)據(jù)讀取到處理器緩存如庭。

有序性 Lock前綴的指令相當(dāng)于一個(gè)內(nèi)存柵欄,它確保指令排序的時(shí)候撼港,不會(huì)把后面的指令拍到內(nèi)存柵欄的前面坪它,也不會(huì)把前面的指令排到內(nèi)存柵欄的后面。

6. 阻塞隊(duì)列

# 通常的阻塞隊(duì)列有哪幾種帝牡,特點(diǎn)是什么往毡?

  • ArrayBlockQueue:基于數(shù)組實(shí)現(xiàn)的有界的FIFO(先進(jìn)先出)阻塞隊(duì)列。
  • LinkedBlockQueue:基于鏈表實(shí)現(xiàn)的無界的FIFO(先進(jìn)先出)阻塞隊(duì)列靶溜。
  • SynchronousQueue:內(nèi)部沒有任何緩存的阻塞隊(duì)列开瞭。
  • PriorityBlockingQueue:具有優(yōu)先級(jí)的無限阻塞隊(duì)列。

# ConcurrentHashMap的原理

數(shù)據(jù)結(jié)構(gòu)的實(shí)現(xiàn)跟HashMap一樣罩息,不做介紹嗤详。

JDK 1.8之前采用的是分段鎖,核心類是一個(gè)Segment扣汪,Segment繼承了ReentrantLock断楷,每個(gè)Segment對(duì)象管理若干個(gè)桶,多個(gè)線程訪問同一個(gè)元素的時(shí)候只能去競爭獲取鎖崭别。

JDK 1.8采用了CAS + synchronized冬筒,插入鍵值對(duì)的時(shí)候如果當(dāng)前桶中沒有Node節(jié)點(diǎn),使用CAS方式進(jìn)行更新茅主,如果有Node節(jié)點(diǎn)舞痰,則使用synchronized的方式進(jìn)行更新。

五诀姚、Jvm

image

Jvm中考察頻率較高的內(nèi)容有:Jvm內(nèi)存區(qū)域的劃分响牛、GC機(jī)制和類加載機(jī)制。

建議閱讀:

《深入理解Java虛擬機(jī)》

1. Java內(nèi)存模型

# Jvm內(nèi)存區(qū)域是如何劃分的赫段?

內(nèi)存區(qū)域劃分:

  • 程序計(jì)數(shù)器:當(dāng)前線程的字節(jié)碼執(zhí)行位置的指示器呀打,線程私有。
  • Java虛擬機(jī)棧:描述的Java方法執(zhí)行的內(nèi)存模型糯笙,每個(gè)方法在執(zhí)行的同時(shí)會(huì)創(chuàng)建一個(gè)棧幀贬丛,存儲(chǔ)著局部變量、操作數(shù)棧给涕、動(dòng)態(tài)鏈接和方法出口等豺憔,線程私有。
  • 本地方法棧:本地方法執(zhí)行的內(nèi)存模型够庙,線程私有恭应。
  • Java堆:所有對(duì)象實(shí)例分配的區(qū)域。
  • 方法區(qū):所有已經(jīng)被虛擬機(jī)加載的類的信息耘眨、常量昼榛、靜態(tài)變量和即時(shí)編輯器編譯后的代碼數(shù)據(jù)。

# Jvm內(nèi)存模型是怎么樣的剔难?

  1. Java規(guī)定所有變量的內(nèi)存都需要存儲(chǔ)在主內(nèi)存胆屿。
  2. 每個(gè)線程都有自己的工作內(nèi)存,線程中使用的所有變量以及對(duì)變量的操作都基于工作內(nèi)存钥飞,工作內(nèi)存中的所有變量都從主內(nèi)存讀取過來的莺掠。
  3. 不同線程間的工作內(nèi)存無法進(jìn)行直接交流,必須通過主內(nèi)存完成读宙。
image

主內(nèi)存和工作內(nèi)存之間的交互協(xié)議彻秆,即變量如何從主內(nèi)存?zhèn)鬟f到工作內(nèi)存、工作內(nèi)存如何將變量傳遞到主內(nèi)存结闸,Java內(nèi)存模型定義了8種操作來完成唇兑,并且每一種操作都是原子的,不可再分的桦锄。

|

類型

|

說明

|
|

lock

|

作用于主內(nèi)存的變量扎附,把一個(gè)變量標(biāo)識(shí)一個(gè)線程獨(dú)占的狀態(tài)

|
|

unlock

|

作用于主內(nèi)存的變量,把一個(gè)處于鎖定狀態(tài)的變量釋放出來

|
|

read

|

把一個(gè)變量從主內(nèi)存?zhèn)鬏數(shù)焦ぷ鲀?nèi)存结耀,以便隨后的load使用

|
|

load

|

把read操作讀取的變量存儲(chǔ)到工作內(nèi)存的變量副本中

|
|

use

|

把工作內(nèi)存中的變量的值傳遞給執(zhí)行引擎留夜,每當(dāng)虛擬機(jī)執(zhí)行到一個(gè)需要使用變量的字節(jié)碼指令的時(shí)候都會(huì)執(zhí)行這個(gè)操作

|
|

assign

|

把一個(gè)從執(zhí)行引擎中接收到的變量賦值給工作內(nèi)存中的變量匙铡,每當(dāng)虛擬機(jī)遇到賦值的字節(jié)碼指令都會(huì)執(zhí)行這個(gè)操作

|
|

store

|

把工作內(nèi)存中的一個(gè)變量的值傳遞給主內(nèi)存,以便以后的write使用

|
|

write

|

把store傳遞過來的工作內(nèi)存中的變量寫入到主內(nèi)存中的變量

|

# String s1 = "abc"和String s2 = new String("abc")的區(qū)別碍粥,生成對(duì)象的情況

  1. 指向方法區(qū):"abc"是常量鳖眼,所以它會(huì)在方法區(qū)中分配內(nèi)存,如果方法區(qū)已經(jīng)給"abc"分配過內(nèi)存嚼摩,則s1會(huì)直接指向這塊內(nèi)存區(qū)域钦讳。
  2. 指向Java堆:new String("abc")是重新生成了一個(gè)Java實(shí)例,它會(huì)在Java堆中分配一塊內(nèi)存枕面。

所以s1和s2的內(nèi)存地址肯定不一樣愿卒,但是內(nèi)容一樣。

2. GC機(jī)制

# 如何判斷對(duì)象可回收潮秘?

判斷一個(gè)對(duì)象可以回收通常采用的算法是引用幾算法和可達(dá)性算法琼开。由于互相引用導(dǎo)致的計(jì)數(shù)不好判斷,Java采用的可達(dá)性算法唇跨。

可達(dá)性算法的思路是:通過一些列被<typo id="typo-14770" data-origin="成為" ignoretag="true">成為</typo>GC Roots的對(duì)象作為起始點(diǎn)稠通,自上往下從這些起點(diǎn)往下搜索,搜索所有走過的路徑稱為引用鏈买猖,如果一個(gè)對(duì)象沒有跟任何引用鏈相關(guān)聯(lián)的時(shí)候改橘,則證明該對(duì)象不可用,所以這些對(duì)象就會(huì)被判定為可以回收玉控。

可以被當(dāng)作GC Roots的對(duì)象包括:

  • Java虛擬機(jī)棧中的引用的對(duì)象
  • 方法區(qū)中靜態(tài)屬性引用的對(duì)象
  • 方法區(qū)中常量引用的對(duì)象
  • 本地方法中JNI引用的對(duì)象

# GC的常用算法飞主?

  • 標(biāo)記 - 清除:首先標(biāo)記出需要回收的對(duì)象,標(biāo)記完成后統(tǒng)一回收所有被標(biāo)記的對(duì)象高诺。容易產(chǎn)生碎片空間碌识。
  • 復(fù)制算法:它將可用的內(nèi)存分為兩塊,每次只用其中的一塊虱而,當(dāng)需要內(nèi)存回收的時(shí)候筏餐,將存活的對(duì)象復(fù)制到另一塊內(nèi)存,然后將當(dāng)前已經(jīng)使用的內(nèi)存一次性回收掉牡拇。需要浪費(fèi)一半的內(nèi)存魁瞪。
  • 標(biāo)記 - 整理:讓存活的對(duì)象向一端移動(dòng),之后清除邊界外的內(nèi)存惠呼。
  • 分代搜集:根據(jù)對(duì)象存活的周期导俘,Java堆會(huì)被分為新生代和老年代,根據(jù)不同年代的特性剔蹋,選擇合適的GC收集算法旅薄。

# Minar GC和Full GC的區(qū)別?

  • Minar GC:頻率高泣崩、針對(duì)新生代少梁。
  • Full GC:頻率低洛口、發(fā)生在老年代、通常會(huì)伴隨一次Minar GC和速度慢猎莲。

# 說一下四種引用以及他們的區(qū)別绍弟?

  • 強(qiáng)引用:強(qiáng)引用還在技即,垃圾搜集器就不會(huì)回收被引用的對(duì)象著洼。
  • 軟引用:對(duì)于軟引用關(guān)聯(lián)的對(duì)象,在系統(tǒng)發(fā)生內(nèi)存溢出異常之前而叼,將會(huì)把這些對(duì)象列進(jìn)回收范圍進(jìn)行第二次回收身笤,如果這次回收還沒有足夠的內(nèi)存,才會(huì)拋出內(nèi)存溢出異常葵陵。
  • 弱引用:被若引用關(guān)聯(lián)的對(duì)象只能存活到下一次GC之前液荸。
  • 虛引用:為對(duì)象設(shè)置虛引用的目的僅僅是為了GC之前收到一個(gè)系統(tǒng)通知。

3. 類加載

# 類加載的過程脱篙?

類加載的過程可以分為:

  1. 加載:將類的全限定名轉(zhuǎn)化為二進(jìn)制流娇钱,再將二進(jìn)制流轉(zhuǎn)化為方法區(qū)中的類型信息,從而生成一個(gè)Class對(duì)象绊困。
  2. 驗(yàn)證:對(duì)類的驗(yàn)證文搂,包括格式、字節(jié)碼秤朗、屬性等煤蹭。
  3. 準(zhǔn)備:為類變量分配內(nèi)存并設(shè)置初始值。
  4. 解析:將常量池的符號(hào)引用轉(zhuǎn)化為直接引用取视。
  5. 初始化:執(zhí)行類中定義的Java程序代碼硝皂,包括類變量的賦值動(dòng)作和構(gòu)造函數(shù)的賦值。
  6. 使用
  7. 卸載

只有加載作谭、驗(yàn)證稽物、準(zhǔn)備、初始化和卸載的這個(gè)五個(gè)階段的順序是確定的折欠。

# 類加載的機(jī)制贝或,以及為什么要這樣設(shè)計(jì)?

類加載的機(jī)制是雙親委派模型怨酝。大部分Java程序需要使用的類加載器包括:

  • 啟動(dòng)類加載器:由C++語言實(shí)現(xiàn)傀缩,負(fù)責(zé)加載Java中的核心類。
  • 擴(kuò)展類加載器:負(fù)責(zé)加載Java擴(kuò)展的核心類之外的類农猬。
  • 應(yīng)用程序類加載器:負(fù)責(zé)加載用戶類路徑上指定的類庫赡艰。

雙親委派模型如下:

image

雙親委派模型要求出了頂層的啟動(dòng)類加載器之外,其他的類加載器都有自己的父加載器斤葱,通過組合實(shí)現(xiàn)慷垮。

雙親委派模型的工作流程: 當(dāng)一個(gè)類加載的任務(wù)來臨的時(shí)候揖闸,先交給父類加載器完成,父類加載器交給父父類加載器完成料身,知道傳遞給啟動(dòng)類加載器汤纸,如果完成不了的情況下拙泽,再依次往下傳遞類加載的任務(wù)檐嚣。

這樣設(shè)計(jì)的原因: 雙親委派模型能夠保證Java程序的穩(wěn)定運(yùn)行,不同層次的類加載器具有不同優(yōu)先級(jí)酬土,所有的對(duì)象的父類Object幔烛,無論哪一個(gè)類加載器加載啃擦,最后都會(huì)交給啟動(dòng)類加載器,保證安全饿悬。

六令蛉、kotlin

[圖片上傳失敗...(image-c7f2f2-1614839133173)]

建議閱讀:

《Kotlin》實(shí)戰(zhàn)

1. 基礎(chǔ)

# ==、===和equal的區(qū)別狡恬?

==和equal的作用相同珠叔,===比較內(nèi)存地址

# var和val的區(qū)別?

  • var:可變引用弟劲,具有可讀和可寫權(quán)限祷安,值可變,類型不可變
  • val:不可變引用函卒,具有可讀權(quán)限辆憔,值不可變,但是對(duì)象的屬性可變

2. 函數(shù)

# Kotlin中默認(rèn)參數(shù)的作用以及原理报嵌?

作用:配合@JavaOverloads可以解決Java調(diào)用Kotlin函數(shù)重載的問題虱咧。 原理:Kotlin編譯的默認(rèn)參數(shù)是被編譯到調(diào)用的函數(shù)中的,所以默認(rèn)參數(shù)改變的時(shí)候锚国,是需要重新編譯這個(gè)函數(shù)的腕巡。

# Kotlin中頂層函數(shù)的原理

頂層函數(shù)實(shí)質(zhì)就是Java中的靜態(tài)函數(shù),可以通過Kotlin中的@Jvm:fileName自動(dòng)生成對(duì)應(yīng)的Java調(diào)用類名血筑。

# 中綴函數(shù)是什么绘沉?注意點(diǎn)?

中綴函數(shù)需要是用infix關(guān)鍵字修飾豺总,如downTo:

public infix fun Int.downTo(to: Int): IntProgression {
    return IntProgression.fromClosedRange(this, to, -1)
}
復(fù)制代碼

注意點(diǎn)是函數(shù)的參數(shù)只能有一個(gè)车伞,函數(shù)的參與者只能有兩個(gè)。

# 解構(gòu)函數(shù)的本質(zhì)喻喳?

解構(gòu)聲明將對(duì)象中的所有屬性另玖,解構(gòu)成一組屬性變量,而且這些變量可以單獨(dú)使用,可以單數(shù)使用的原因是通過獲取對(duì)應(yīng)的component()方法對(duì)應(yīng)著類中每個(gè)屬性的值谦去,這些屬性的值被存儲(chǔ)在局部變量中慷丽,所以解構(gòu)聲明的實(shí)質(zhì)是局部變量。

# 擴(kuò)展函數(shù)的本質(zhì)鳄哭?

擴(kuò)展函數(shù)的本質(zhì)就是對(duì)應(yīng)Java中的靜態(tài)函數(shù)要糊,這個(gè)靜態(tài)函數(shù)參數(shù)為接受者類型的對(duì)象,然后利用這個(gè)對(duì)象去訪問對(duì)象中的屬性和成員方法妆丘,最后返回這個(gè)對(duì)象的本身锄俄。

# 擴(kuò)展函數(shù)和成員函數(shù)的區(qū)別?

  1. 實(shí)質(zhì)不同:擴(kuò)展函數(shù)實(shí)質(zhì)是靜態(tài)函數(shù)飘痛,是外部函數(shù)珊膜,成員函數(shù)是內(nèi)部函數(shù)。
  2. 權(quán)限不同:擴(kuò)展函數(shù)訪問不了私有的屬性和成員方法宣脉,成員函數(shù)可以。
  3. 繼承:擴(kuò)展函數(shù)不可復(fù)寫剔氏,成員函數(shù)可以復(fù)寫塑猖。

它們的使用方式類似。

3. 類谈跛、對(duì)象和接口

# Kotlin中常用的類的修飾符有哪些羊苟?

  • open:運(yùn)行創(chuàng)建子類或者復(fù)寫子類的方法。
  • final:不允許創(chuàng)建子類和復(fù)寫子類的方法感憾。
  • abstract:抽象類蜡励,必須復(fù)寫子類的方法。

在Kotlin中阻桅,默認(rèn)的類和方法的修飾符都是final的凉倚,如果想讓類和方法能夠被繼承或者復(fù)寫,需要顯示的添加open修飾符嫂沉。

# Kotlin中可見性修飾符有哪些稽寒?

  • public:所有地方可見
  • protected:子類中可見
  • private:類中可見
  • internal:模塊中可見,一個(gè)模塊就是一組一起編譯的Kotlin文件

Java默認(rèn)的訪問權(quán)限是包訪問權(quán)限趟章,Kotlin中默認(rèn)的訪問權(quán)限是public杏糙。

# Kotlin中的內(nèi)部類和Java中的內(nèi)部類有什么不同?

  • Kotlin:默認(rèn)相當(dāng)于Java中的靜態(tài)內(nèi)部類蚓土,如果想訪問類中的成員方法和屬性宏侍,需要添加inner關(guān)鍵字修飾。
  • Java:默認(rèn)持有外部類引用蜀漆,可以訪問成員方法和屬性谅河,如果想聲明為靜態(tài)內(nèi)部類,需要添加static關(guān)鍵字修飾。

# Kotlin屬性代理背后原理旧蛾?

可以簡單理解為屬性的settter莽龟、getter訪問器內(nèi)部實(shí)現(xiàn)交給了代理對(duì)象來實(shí)現(xiàn),相當(dāng)于使用一個(gè)代理對(duì)象代替了原來簡單屬性的讀寫過程锨天,而暴露外部屬性操作還是不變 的毯盈,照樣是屬性賦值和讀取,只是setter病袄、getter內(nèi)部具體實(shí)現(xiàn)變了搂赋。

# object和companion object的一些特點(diǎn)?

共同點(diǎn): 定義單例的一種方式益缠,提供靜態(tài)成員和方法脑奠。

不同點(diǎn):

  • object:用來生成匿名內(nèi)部類。
  • companion object:提供工廠方法幅慌,訪問私有的構(gòu)造方法宋欺。

4. lambda

# lambda表達(dá)式有幾種?

  • 普通表達(dá)式:()->R胰伍。
  • 帶接收者對(duì)象的表達(dá)式:T.()->R齿诞,可以訪問接收者對(duì)象的屬性和成員方法。如apply骂租。

# kotlin和Java內(nèi)部類或者lambda表達(dá)式訪問局部變量有什么不同祷杈?

  • Java中的內(nèi)部類:局部變量必須是final聲明的,無法去修改局部變量的值渗饮。
  • Kotlin中l(wèi)ambda表達(dá)式:不要求final聲明但汞,對(duì)于非final修飾的lambda表達(dá)式,可以修改局部變量的值互站。

如果想在Java中的內(nèi)部類修改外層局部變量的值私蕾,有兩種方法:用數(shù)組包裝或者提供包裝類,Kotlin中l(wèi)ambda能夠訪問并修改局部變量的本質(zhì)就是提供了一層包裝類:

class Ref<T>(var value:T)
復(fù)制代碼

修改局部變量的值就是修改value中的值云茸。

# 使用lambda表達(dá)式訪問的局部變量有什么不同是目?

默認(rèn)情況下,局部變量的生命周期會(huì)被限制在聲明這個(gè)變量的函數(shù)中标捺,但是如果它被lambda捕捉了懊纳,使用這個(gè)變量的代碼可以被存儲(chǔ)并稍后執(zhí)行。

class Apple {
    lateinit var num:(() -> Int)

    fun initCount(){
        val count = 2
        num = fun():Int{
            return count * count
        }
    }

    fun res():Int{
        return num()
    }
}

fun main(args: Array<String>) {
    val a = Apple()
    a.initCount()
    val res = a.res()
    println(res)
}
復(fù)制代碼

如上面代碼所示亡容,局部變量count就被存儲(chǔ)在lambda表達(dá)式中嗤疯,最后通過Apple#res方法引用表達(dá)式。

原理:當(dāng)你捕捉final變量的時(shí)候闺兢,它的值會(huì)和lambda代碼一起存儲(chǔ)茂缚。對(duì)于非final變量戏罢,它的值會(huì)被封裝在一層包裝器中,包裝器的引用會(huì)和lambda代碼一起被存儲(chǔ)脚囊。

帶來的問題:默認(rèn)情況下龟糕,lambda表達(dá)式會(huì)生成匿名內(nèi)部類,在非顯示聲明對(duì)象的情況下可以多次重用悔耘,但是如果捕獲了局部變量讲岁,每次調(diào)用的時(shí)候都需要生成新的實(shí)例。

# 序列是什么衬以?集合類和序列的操作符比較缓艳?

Sequence(序列)是一種惰性集合,可以更高效地對(duì)元素進(jìn)行鏈?zhǔn)讲僮骺淳恍枰獎(jiǎng)?chuàng)建額外的集合保存過程中產(chǎn)生的中間結(jié)果阶淘,簡單來講,就是序列中所有的操作都是按順序應(yīng)用在每一個(gè)元素中互妓。比如:

fun main(args: Array<String>) {
    val list = mutableListOf<String>("1","2","3","4","5","6","7","8","9")
    val l = list.asSequence()
            .filter { it.toCharArray()[0] < '4' }
            .map { it.toInt() * it.toInt() }
            .toList()
}
復(fù)制代碼

對(duì)于上述序列中的"1"溪窒,它會(huì)先執(zhí)行filter,再執(zhí)行map车猬,之后再對(duì)"2"重復(fù)操作霉猛。除此以外,序列中所有的中間操作都是惰性的珠闰。

集合和序列操作符的比較:

  • 集合類:map和filter方法是內(nèi)聯(lián),不會(huì)生成匿名類的實(shí)例瘫辩,但每次進(jìn)行map和filter都會(huì)生成新的集合伏嗜,當(dāng)數(shù)據(jù)量大的時(shí)候,消耗的內(nèi)存也比較大伐厌。
  • 序列:map和fitler非內(nèi)聯(lián)承绸,會(huì)生成匿名類實(shí)例,但不需要?jiǎng)?chuàng)建額外的集合保存中間操作的結(jié)果挣轨。

# 為什么要使用內(nèi)聯(lián)函數(shù)军熏?內(nèi)聯(lián)函數(shù)的作用?

使用lambda表達(dá)式可能帶來的開銷:

  1. lambda表達(dá)式正常會(huì)被編譯成匿名類卷扮。
  2. 正常情況下荡澎,使用lambda表達(dá)式至少會(huì)生成一個(gè)對(duì)象,如果很不幸的使用了局部變量晤锹,那么每次使用該lambda表達(dá)式都會(huì)生成一個(gè)新的對(duì)象摩幔,導(dǎo)致使用lambda的效率比不使用還要低。

使用內(nèi)聯(lián)函數(shù)可以減少運(yùn)行時(shí)的開銷鞭铆。內(nèi)聯(lián)函數(shù)主要作用:

  1. 使用內(nèi)聯(lián)函數(shù)可以減少中間類和對(duì)象的創(chuàng)建或衡,進(jìn)而提升性能。主要原因是內(nèi)聯(lián)函數(shù)可以做到函數(shù)被使用的時(shí)候編譯器不會(huì)生成函數(shù)調(diào)用的代碼,而是使用函數(shù)實(shí)現(xiàn)的真實(shí)代碼區(qū)替換每一次的調(diào)用封断。
  2. 結(jié)合reified實(shí)化類型參數(shù)斯辰,解決泛型類型運(yùn)行時(shí)擦除的問題。

5. 類型系統(tǒng)

# Kotlin中的基本數(shù)據(jù)類型的理解坡疼?

在Kotlin中彬呻,使用的時(shí)候是不區(qū)分基本類型的,統(tǒng)一如下: Int回梧、Byte废岂、Short、Long狱意、Float湖苞、Double、Char和Boolean详囤。

使用統(tǒng)一的類型并不意味著Kotlin中所有的基本類型都是引用類型财骨,大多數(shù)情況下,對(duì)于變量藏姐、參數(shù)隆箩、返回類型和屬性都會(huì)被編譯成基本類型,泛型類會(huì)被編譯成Java中的包裝類羔杨,即引用類型捌臊。

# 只讀集合和可變集合的區(qū)別?

在Kotlin中兜材,集合會(huì)被分為兩大類型理澎,只讀集合和可變集合。

  • 只讀集合:對(duì)集合只有讀取權(quán)限曙寡。
  • 可變集合:能夠刪除糠爬、新增、修改和讀取元素举庶。

但是有一點(diǎn)需要注意执隧,只讀集合不一定是不可變的,如果你使用的變量是只讀集合户侥,它可能是眾多集合引用中的一個(gè)镀琉,任何一個(gè)集合引用都有可能是可變集合。

# Array和IntArray的區(qū)別添祸?

Array<Int>相當(dāng)于Java中的Integer[]滚粟,IntArray對(duì)應(yīng)Java中的int[]。

# 使用實(shí)化類型參數(shù)解決泛型擦除的原理是什么刃泌?

內(nèi)聯(lián)函數(shù)的原理是編譯器把實(shí)現(xiàn)的字節(jié)碼動(dòng)態(tài)插入到每一次調(diào)用的地方凡壤。實(shí)化類型參數(shù)也正是基于這個(gè)原理署尤,每次調(diào)用實(shí)化類型參數(shù)的函數(shù)的時(shí)候,編譯器都知道此次作為泛型類型實(shí)參的具體類型亚侠,所以編譯器每次調(diào)用的時(shí)候生成不同類型實(shí)參調(diào)用的字節(jié)碼插入到調(diào)用點(diǎn)曹体。

6. 協(xié)程

# 協(xié)程是什么?協(xié)程的有什么特點(diǎn)硝烂?

Kotlin官方文檔上說:

協(xié)程的本質(zhì)是輕量級(jí)的線程箕别。

為什么說它是輕量級(jí)的線程,因?yàn)閺墓俜浇嵌葋碇v滞谢,創(chuàng)建十萬個(gè)協(xié)程沒什么問題打印任務(wù)不會(huì)存在問題串稀,創(chuàng)建十萬個(gè)線程會(huì)造成內(nèi)存問題,可能會(huì)造成內(nèi)存溢出狮杨。但是這個(gè)對(duì)比有問題母截,因?yàn)閰f(xié)程本質(zhì)上是基于Java的線程池的,你去用線程池創(chuàng)建十萬個(gè)打印任務(wù)是不會(huì)造成內(nèi)存溢出的橄教。

從上面我們可以得出結(jié)果清寇,協(xié)程就是基于線程實(shí)現(xiàn)的更上層的Api,只不過它可以用阻塞式的寫法寫出非阻塞式的代碼护蝶,避免了大量的回調(diào)华烟,核心就是協(xié)程可以幫我自動(dòng)的切換線程。

# 協(xié)程的原理持灰?

很多人都會(huì)講盔夜,協(xié)程中處理耗時(shí)任務(wù),協(xié)程會(huì)先掛起堤魁,執(zhí)行完比吭,再切回來。我在這就淺顯<typo id="typo-20563" data-origin="的" ignoretag="true">的</typo>分析這兩步。

  • 掛起:協(xié)程掛起的時(shí)候會(huì)從掛起處將后面的代碼封裝成續(xù)體悦荒,協(xié)程掛起的時(shí)候添履,將掛起的任務(wù)根據(jù)調(diào)度器放到線程池中執(zhí)行,會(huì)有一個(gè)線程監(jiān)視任務(wù)的完成情況革砸。
  • 線程切回:監(jiān)視線程看到任務(wù)結(jié)束以后,根據(jù)需要再切到指定的線程中(主線程or子線程),執(zhí)行續(xù)體中剩余的代碼匈仗。

詳解請(qǐng)查看:

《Kotlin/JVM 協(xié)程實(shí)現(xiàn)原理》

七、網(wǎng)絡(luò)

[圖片上傳失敗...(image-4a4d4e-1614839133172)]

掌握網(wǎng)絡(luò)知識(shí)其實(shí)是需要一個(gè)系統(tǒng)的過程逢慌,在時(shí)間充裕的情況下悠轩,建議還是系統(tǒng)化的學(xué)習(xí)。

高頻網(wǎng)絡(luò)知識(shí)有TCP攻泼、HTTP和HTTPS火架。

建議閱讀:

《趣談網(wǎng)絡(luò)協(xié)議》

https://time.geekbang.org/column/intro/85

《圖解Http》

1. HTTP和HTTPS

# HTTP是哪一層的協(xié)議鉴象,常見的HTTP狀態(tài)碼有哪些,分別代表什么意思何鸡?

HTTP協(xié)議是應(yīng)用層的協(xié)議纺弊。

常見的HTTP狀態(tài)碼有:

|

類別

|

解釋

|
|

1xx

|

請(qǐng)求已經(jīng)接收,繼續(xù)處理

|
|

2xx

|

服務(wù)器已經(jīng)正確處理請(qǐng)求骡男,比如200

|
|

3xx

|

重定向淆游,需要做進(jìn)一步的處理才能完成請(qǐng)求

|
|

4xx

|

服務(wù)器無法理解的請(qǐng)求,比如404隔盛,訪問的資源不存在

|
|

5xx

|

服務(wù)器收到請(qǐng)求以后犹菱,處理錯(cuò)誤

|
|

# HTTP 1.1 和HTTP 2有什么區(qū)別?

| |
|

HTTP 2.0基于HTTP 1.1吮炕,與HTTP 2.0增加了:

| |

  • 二進(jìn)制格式:HTTP 1.1使用純文本進(jìn)行通信腊脱,HTTP 2.0使用二進(jìn)制進(jìn)行傳輸。
  • Head壓縮:對(duì)已經(jīng)發(fā)送的Header使用鍵值建立索引表来屠,相同的Header使用索引表示虑椎。
  • 服務(wù)器推送:服務(wù)器可以進(jìn)行主動(dòng)推送
  • 多路復(fù)用:一個(gè)TCP連接可以劃分成多個(gè)流,每個(gè)流都會(huì)分配Id俱笛,客戶端可以借助流和服務(wù)端建立全雙工進(jìn)行通信捆姜,并且流具有優(yōu)先級(jí)。

[圖片上傳失敗...(image-954a83-1614839133172)]

# HTTP和HTTPS有什么區(qū)別迎膜?

簡單來說泥技,HTTP和HTTPS的關(guān)系是這樣的

HTTPS = HTTP + SSL/TLS
復(fù)制代碼

區(qū)別如下: HTTP作用于應(yīng)用層,使用80端口磕仅,起始地址是http://珊豹,明文傳輸,消息容易被攔截榕订,串改店茶。 HTTPS作用域傳輸層,使用443端口劫恒,起始地址是https://贩幻,需要下載CA證書,傳輸?shù)倪^程需要加密两嘴,安全性高丛楚。

# SSL/TLS的握手過程?

這里借用《趣談網(wǎng)絡(luò)協(xié)議》的圖片:

[圖片上傳失敗...(image-b47b27-1614839133172)]

# HTTPS傳輸過程中是如何處理進(jìn)行加密的憔辫?為什么有對(duì)稱加密的情況下仍然需要進(jìn)行非對(duì)稱加密趣些?

過程和上圖類似,依次獲取證書贰您,公鑰坏平,最后生成對(duì)稱加密的鑰匙進(jìn)行對(duì)稱加密拢操。

對(duì)稱加密可以保證加密效率,但是不能解決密鑰傳輸問題功茴;非對(duì)稱加密可以解決傳輸問題庐冯,但是效率不高。

2. TCP相關(guān)

# TCP的三次握手過程坎穿,為什么需要三次展父,而不是兩次或者四次?

[圖片上傳失敗...(image-cf3de9-1614839133172)]

只發(fā)送兩次玲昧,服務(wù)端是不知道自己發(fā)送的消息能不能被客戶端接收到栖茉。 因?yàn)門CP握手是三次,所以此時(shí)雙方都已經(jīng)知道自己發(fā)送的消息能夠被對(duì)方收到孵延,所以吕漂,第四次的發(fā)送就顯得多余了。

# TCP的四次揮手過程尘应?

[圖片上傳失敗...(image-5d9788-1614839133172)]

大致意思就是:

  • Client:我要斷開連接了
  • Server:我收到你的消息了
  • Server:我也要斷開連接了
  • Client:收到你要斷開連接的消息了

之后Client等待兩個(gè)MSL(數(shù)據(jù)包在網(wǎng)絡(luò)上生存的最長時(shí)間)惶凝,如果服務(wù)端沒有回消息就徹底斷開了。

# TCP和UDP有什么區(qū)別犬钢?

  • TCP:基于字節(jié)流苍鲜、面向連接、可靠玷犹、能夠進(jìn)行全雙工通信混滔,除此以外,還能進(jìn)行流量控制和擁塞控制歹颓,不過效率略低
  • UDP:基于報(bào)文坯屿、面向無連接、不可靠巍扛,但是傳輸效率高领跛。

總的來說,TCP適用于傳輸效率要求低撤奸,準(zhǔn)確性要求高或要求有連接隔节。而UDP適用于對(duì)準(zhǔn)確性要求較低,傳輸效率要求較高的場景寂呛,比如語音通話、直播等瘾晃。

# TCP為什么是一種可靠的協(xié)議贷痪?如何做到流量控制和擁塞控制?

  • TCP可靠:是因?yàn)榭梢宰龅綌?shù)據(jù)包發(fā)送的有序蹦误、無差錯(cuò)和無重復(fù)劫拢。
  • 流量控制:是通過滑動(dòng)窗口實(shí)現(xiàn)的肉津,因?yàn)榘l(fā)送發(fā)和接收方消息發(fā)送速度和接收速度不一定對(duì)等,所以需要一個(gè)滑動(dòng)窗口來平衡處理效率舱沧,并且保證沒有差錯(cuò)和有序的接收數(shù)據(jù)包妹沙。
  • 擁塞控制:慢開始和擁塞避免、快重傳和快恢復(fù)算法熟吏。這寫算法主要是為了適應(yīng)網(wǎng)絡(luò)中的帶寬而作出的調(diào)整距糖。

八、設(shè)計(jì)模式

[圖片上傳失敗...(image-73f735-1614839133172)]

經(jīng)城K拢考察的設(shè)計(jì)模式不多悍引,但是我們應(yīng)該在平時(shí)業(yè)務(wù)中應(yīng)該多多思考,用一些設(shè)計(jì)模式會(huì)不會(huì)更好帽氓。

建議閱讀:

《Android 源碼設(shè)計(jì)模式解析與實(shí)戰(zhàn)》

https://book.douban.com/subject/26644935/

1. 六大原則

設(shè)計(jì)模式的六大原則是:

  • 單一職責(zé):合理分配類和函數(shù)的職責(zé)
  • 開閉原則:開放擴(kuò)展趣斤,關(guān)閉修改
  • 里式替換:繼承
  • 依賴倒置:面向接口
  • 接口隔離:控制接口的粒度
  • 迪米特:一個(gè)類應(yīng)該對(duì)其他的類了解最少

2. 單例模式

單例模式被問到的幾率很大,通常會(huì)問如下幾種問題黎休。

# 單例的常用寫法有哪幾種浓领?

懶漢模式

public class SingleInstance {
    private static SingleInstance instance;
    private SingleInstance() {}
    public static synchronized SingleInstance getInstance() {
        if(instance == null) {
            instance = new SingleInstance();
        }
        return instance;
    }
}
復(fù)制代碼

該模式的主要問題是每次獲取實(shí)例都需要同步,造成不必要的同步開銷势腮。 DCL模式

public class SingleInstance {
    private static SingleInstance instance;
    private SingleInstance() {}
    public static SingleInstance getInstance() {
        if(instance == null) {
            synchronized (SingleInstance.class) {
                if(instance == null) {
                    instance = new SingleInstance();
                }
            }
        }
        return instance;
    }
}
復(fù)制代碼

高并發(fā)環(huán)境下可能會(huì)發(fā)生問題联贩。 靜態(tài)內(nèi)部類單例

public class SingleInstance {
    private SingleInstance() {}
    public static SingleInstance getInstance() {
        return SingleHolder.instance;
    }

    private static class SingleHolder{
        private static final SingleInstance instance = new SingleInstance();
    }
}
復(fù)制代碼

枚舉單例

public enum SingletonEnum {
    INSTANCE
}
復(fù)制代碼

優(yōu)點(diǎn):線程安全和反序列化不會(huì)生成新的實(shí)例

# DCL模式會(huì)有什么問題?

對(duì)象生成實(shí)例的過程中嫉鲸,大概會(huì)經(jīng)過以下過程:

  1. 為對(duì)象分配內(nèi)存空間撑蒜。
  2. 初始化對(duì)象中的成員變量。
  3. 將對(duì)象指向分配的內(nèi)存空間(此時(shí)對(duì)象就不為null)玄渗。

由于Jvm會(huì)優(yōu)化指令順序座菠,也就是說2和3的順序是不能保證的。在多線程的情況下藤树,當(dāng)一個(gè)線程完成了1浴滴、3過程后,當(dāng)前線程的時(shí)間片已用完岁钓,這個(gè)時(shí)候會(huì)切換到另一個(gè)線程升略,另一個(gè)線程調(diào)用這個(gè)單例,會(huì)使用這個(gè)還沒初始化完成的實(shí)例屡限。 解決方法是使用volatile關(guān)鍵字:

public class SingleInstance {
    private static volatile SingleInstance instance;
    private SingleInstance() {}
    public static SingleInstance getInstance() {
        if(instance == null) {
            synchronized (SingleInstance.class) {
                if(instance == null) {
                    instance = new SingleInstance();
                }
            }
        }
        return instance;
    }
}
復(fù)制代碼

3. 需要關(guān)注的設(shè)計(jì)模式

重點(diǎn)了解以下的幾種常用的設(shè)計(jì)模式:

  • 工廠模式和抽象工廠模式:注意他們的區(qū)別品嚣。
  • 責(zé)任鏈模式:View的事件分發(fā)和OkHttp的調(diào)用過程都使用到了責(zé)任鏈模式。
  • 觀察者模式:重要性不言而喻钧大。
  • 代理模式:建議了解一下動(dòng)態(tài)代理翰撑。

4. MVC\MVP\MVVM

MVC、MVP和MVVM應(yīng)該是設(shè)計(jì)模式中考察頻率最高的知識(shí)點(diǎn)了啊央,嚴(yán)格意義上來說眶诈,它們不能算是設(shè)計(jì)模式涨醋,而是框架。

# MVC逝撬、MVP和MVVM是什么浴骂?

圖片已有,不再給出

  • MVC:Model-View-Controller宪潮,是一種分層解偶的框架溯警,Model層提供本地?cái)?shù)據(jù)和網(wǎng)絡(luò)請(qǐng)求,View層處理視圖坎炼,Controller處理邏輯愧膀,存在問題是Controller層和View層的劃分不明顯,Model層和View層的存在耦合谣光。
  • MVP:Model-View-Presenter檩淋,是對(duì)MVC的升級(jí),Model層和View層與MVC的意思一致萄金,但Model層和View層不再存在耦合蟀悦,而是通過Presenter層這個(gè)橋梁進(jìn)行交流。
  • MVVM:Model-View-ViewModel氧敢,不同于上面的兩個(gè)框架日戈,ViewModel持有數(shù)據(jù)狀態(tài),當(dāng)數(shù)據(jù)狀態(tài)改變的時(shí)候孙乖,會(huì)自動(dòng)通知View層進(jìn)行更新浙炼。

# MVC和MVP的區(qū)別是什么?

MVP是MVC的進(jìn)一步解耦唯袄,簡單來講弯屈,在MVC中,View層既可以和Controller層交互恋拷,又可以和Model層交互资厉;而在MVP中,View層只能和Presenter層交互蔬顾,Model層也只能和Presenter層交互宴偿,減少了View層和Model層的耦合,更容易定位錯(cuò)誤的來源诀豁。

# MVVM和MVP的最大區(qū)別在哪窄刘?

MVP中的每個(gè)方法都需要你去主動(dòng)調(diào)用,它其實(shí)是被動(dòng)的舷胜,而MVVM中有數(shù)據(jù)驅(qū)動(dòng)這個(gè)概念都哭,當(dāng)你的持有的數(shù)據(jù)狀態(tài)發(fā)生變更的時(shí)候,你的View你可以監(jiān)聽到這個(gè)變化,從而主動(dòng)去更新欺矫,這其實(shí)是主動(dòng)的。

# ViewModel如何知道View層的生命周期展氓?

事實(shí)上穆趴,如果你僅僅使用ViewModel,它是感知不了生命周期遇汞,它需要結(jié)合LiveData去感知生命周期未妹,如果僅僅使用DataBinding去實(shí)現(xiàn)MVVM,它對(duì)數(shù)據(jù)源使用了弱引用空入,所以一定程度上可以避免內(nèi)存泄漏的發(fā)生络它。

九、算法題

沒什么好說的歪赢,Leetcode + 《劍指Offer》化戳,著重記住一些解決問題的思路。

除此以外埋凯,你還得記住一些常用的算法:排序点楼、反轉(zhuǎn)鏈表、樹的遍歷和手寫LruCache白对,這些都寫不出來掠廓,就尷尬了。

如果你不想閱讀書籍甩恼,可以參考一下這個(gè)Github蟀瞧,親眼見證了從3k Star到34k Star,跪了:

【fucking-algorithm】:github.com/labuladong/…

github.com/LRH1993

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末条摸,一起剝皮案震驚了整個(gè)濱河市悦污,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌屈溉,老刑警劉巖塞关,帶你破解...
    沈念sama閱讀 216,496評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異子巾,居然都是意外死亡帆赢,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門线梗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來椰于,“玉大人,你說我怎么就攤上這事仪搔●觯” “怎么了?”我有些...
    開封第一講書人閱讀 162,632評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長偏陪。 經(jīng)常有香客問我抢呆,道長,這世上最難降的妖魔是什么笛谦? 我笑而不...
    開封第一講書人閱讀 58,180評(píng)論 1 292
  • 正文 為了忘掉前任抱虐,我火速辦了婚禮,結(jié)果婚禮上饥脑,老公的妹妹穿的比我還像新娘恳邀。我一直安慰自己,他們只是感情好灶轰,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評(píng)論 6 388
  • 文/花漫 我一把揭開白布谣沸。 她就那樣靜靜地躺著,像睡著了一般笋颤。 火紅的嫁衣襯著肌膚如雪乳附。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,165評(píng)論 1 299
  • 那天椰弊,我揣著相機(jī)與錄音许溅,去河邊找鬼。 笑死秉版,一個(gè)胖子當(dāng)著我的面吹牛贤重,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播清焕,決...
    沈念sama閱讀 40,052評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼并蝗,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了秸妥?” 一聲冷哼從身側(cè)響起滚停,我...
    開封第一講書人閱讀 38,910評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎粥惧,沒想到半個(gè)月后键畴,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,324評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡突雪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評(píng)論 2 332
  • 正文 我和宋清朗相戀三年起惕,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片咏删。...
    茶點(diǎn)故事閱讀 39,711評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡惹想,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出督函,到底是詐尸還是另有隱情嘀粱,我是刑警寧澤激挪,帶...
    沈念sama閱讀 35,424評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站锋叨,受9級(jí)特大地震影響垄分,放射性物質(zhì)發(fā)生泄漏室囊。R本人自食惡果不足惜棉浸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望棒搜。 院中可真熱鬧豌鸡,春花似錦、人聲如沸段标。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽逼庞。三九已至蛇更,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間赛糟,已是汗流浹背派任。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留璧南,地道東北人掌逛。 一個(gè)月前我還...
    沈念sama閱讀 47,722評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像司倚,于是被迫代替她去往敵國和親豆混。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • 是1995年推出的高級(jí)程序設(shè)計(jì)語言动知,可以運(yùn)行多個(gè)平臺(tái)皿伺,如Windows,麥克OS及其他多種版本的系統(tǒng)盒粮,本教程通過簡...
    承超越閱讀 145評(píng)論 0 0
  • python 生成器- >yield: 菜鳥參考鏈接①[https://www.runoob.com/w3cnot...
    歲杪二十七閱讀 250評(píng)論 0 0
  • 今天感恩節(jié)哎鸵鸥,感謝一直在我身邊的親朋好友。感恩相遇丹皱!感恩不離不棄妒穴。 中午開了第一次的黨會(huì),身份的轉(zhuǎn)變要...
    迷月閃星情閱讀 10,562評(píng)論 0 11
  • 彩排完种呐,天已黑
    劉凱書法閱讀 4,212評(píng)論 1 3
  • 沒事就多看看書宰翅,因?yàn)楦褂性姇鴼庾匀A,讀書萬卷始通神爽室。沒事就多出去旅游汁讼,別因?yàn)闆]錢而找借口淆攻,因?yàn)橹灰闶〕詢€用,來...
    向陽之心閱讀 4,781評(píng)論 3 11