這是一份面向Android開(kāi)發(fā)者的復(fù)習(xí)指南

技術(shù)不止,文章有料虐先,加 JiuXinDev 入群膀息,Android 搬磚路上不孤單

前言

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


悲傷辣么大.

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

必讀

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

覆蓋的知識(shí)點(diǎn)有Android膀跌、Java、Kotlin、Jvm、網(wǎng)絡(luò)和設(shè)計(jì)模式。

面向人群

正在求職的中高級(jí)Android開(kāi)發(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ì)問(wèn)哪些內(nèi)容守谓,達(dá)到這種境界以后,你就可以從容的面對(duì)每次面試了。

一起快樂(lè)

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

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

一、Android基礎(chǔ)

Android基礎(chǔ)

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

1. Activity

# Activity的四大啟動(dòng)模式,以及應(yīng)用場(chǎ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í)例珠十。比較常見(jiàn)的場(chǎng)景就是給通知跳轉(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í)例都移除棧。常見(jiàn)于跳轉(zhuǎn)到主界面。
  • singleInstance:?jiǎn)螌?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爪模。
  • 可見(jiàn)但非前臺(tái)的Activity:常見(jiàn)于棧頂?shù)?code>Activity背景透明应狱,處在其下面的Activity就是可見(jiàn)但是不可和用戶交互。
  • 后臺(tái)Activity:已經(jīng)被暫停的Activity,比如已經(jīng)執(zhí)行了onStop方法。

所以,onStartonStop通常指的是當(dāng)前活動(dòng)是否位于前臺(tái)這個(gè)角度,而onResumeonPause從是否可見(jiàn)這個(gè)角度來(lái)講的。

2. 屏幕適配

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

平時(shí)的屏幕適配一般采用的頭條的屏幕適配方案拌倍。簡(jiǎn)單來(lái)說(shuō)找爱,以屏幕的一邊作為適配仑鸥,通常是寬。

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

px = dp * density

假設(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ì)列缭黔,看是否有新的消息到來(lái)。
  • Handler:具體處理邏輯的地方。

過(guò)程:

  1. 準(zhǔn)備工作:創(chuàng)建Handler,如果是在子線程中創(chuàng)建匆赃,還需要調(diào)用Looper#prepare()荚斯,在Handler的構(gòu)造函數(shù)中,會(huì)綁定其中的LooperMessageQueue
  2. 發(fā)送消息:創(chuàng)建消息,使用Handler發(fā)送熙暴。
  3. 進(jìn)入MessageQueue:因?yàn)?code>Handler中綁定著消息隊(duì)列,所以Message很自然的被放進(jìn)消息隊(duì)列匠楚。
  4. Looper輪詢消息隊(duì)列:Looper是一個(gè)死循環(huán),一直觀察有沒(méi)有新的消息到來(lái)撩穿,之后從Message取出綁定的Handler,最后調(diào)用Handler中的處理邏輯,這一切都發(fā)生在Looper循環(huán)的線程,這也是Handler能夠在指定線程處理任務(wù)的原因叙甸。

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

  1. 導(dǎo)致卡死的是在Ui線程中執(zhí)行耗時(shí)操作導(dǎo)致界面出現(xiàn)掉幀久妆,甚至ANR烂琴,Looper.loop()這個(gè)操作本身不會(huì)導(dǎo)致這個(gè)情況。
  2. 有人可能會(huì)說(shuō)号醉,我在點(diǎn)擊事件中設(shè)置死循環(huán)會(huì)導(dǎo)致界面卡死反症,同樣都是死循環(huán),不都一樣的嗎铅碍?Looper會(huì)在沒(méi)有消息的時(shí)候阻塞當(dāng)前線程,釋放CPU資源线椰,等到有消息到來(lái)的時(shí)候胞谈,再喚醒主線程。
  3. App進(jìn)程中是需要死循環(huán)的,如果循環(huán)結(jié)束的話烦绳,App進(jìn)程就結(jié)束了悔叽。

建議閱讀:

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

# IdleHandler介紹爵嗅?

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

執(zhí)行場(chǎng)景:

  • MessageQueue沒(méi)有消息笨蚁,隊(duì)列為空的時(shí)候睹晒。
  • MessageQueue屬于延遲消息,當(dāng)前沒(méi)有消息執(zhí)行的時(shí)候括细。

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

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

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

5. Bitmap

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

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

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

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

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

資源文件夾

比如一個(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

如果
如果它僅僅存放在drawable-xxhdpi乏苦,則有:

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

所以株扛,對(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

建議閱讀:

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

# Bitmap的高效加載旬蟋?

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

  1. 獲取需要的長(zhǎng)和寬革娄,一般獲取控件的長(zhǎng)和寬咖为。
  2. 設(shè)置BitmapFactory.Options中的inJustDecodeBounds為true,可以幫助我們?cè)诓患虞d進(jìn)內(nèi)存的方式獲得Bitmap的長(zhǎng)和寬稠腊。
  3. 對(duì)需要的長(zhǎng)和寬和Bitmap的長(zhǎng)和寬進(jìn)行對(duì)比躁染,從而獲得壓縮比例,放入BitmapFactory.Options中的inSampleSize屬性架忌。
  4. 設(shè)置BitmapFactory.Options中的inJustDecodeBounds為false吞彤,將圖片加載進(jìn)內(nèi)存,進(jìn)而設(shè)置到控件中。

二饰恕、Android進(jìn)階

Android進(jìn)階

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

1. Binder

# Binder的介紹埋嵌?與其他IPC方式的優(yōu)缺點(diǎn)破加?

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

從IPC角度來(lái)說(shuō)雹嗦,Binder是Android中的一種跨進(jìn)程通信方式范舀;Binder還可以理解為虛擬的物理設(shè)備,它的設(shè)備驅(qū)動(dòng)是/dev/binder了罪;從Android Framework來(lái)講锭环,Binder是Service Manager連接各種Manager和對(duì)應(yīng)的ManagerService的橋梁。從面向?qū)ο蠛虲S模型來(lái)講泊藕,Client通過(guò)Binder和遠(yuǎn)程的Server進(jìn)行通訊辅辩。

基于Binder,Android還實(shí)現(xiàn)了其他的IPC方式娃圆,比如AIDL玫锋、MessengerContentProvider

與其他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ā)送的過(guò)程中被修改散劫。

# Binder的通信過(guò)程?Binder的原理幕帆?

圖片:


Binder通信過(guò)程

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

原理:

Binder結(jié)構(gòu)

Binder的結(jié)構(gòu):
Client:服務(wù)的請(qǐng)求方。
Server:服務(wù)的提供方失乾。
Service Manager:為Server提供Binder的注冊(cè)服務(wù)常熙,為Client提供Binder的查詢服務(wù),Server碱茁、ClientService Manager的通訊都是通過(guò)Binder裸卫。
Binder驅(qū)動(dòng):負(fù)責(zé)Binder通信機(jī)制的建立,提供一系列底層支持纽竣。

從上圖中墓贿,Binder通信的過(guò)程是這樣的:

  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通過(guò)Service Manager獲取服務(wù):Client知道服務(wù)中Binder實(shí)體的名字后,通過(guò)名字從Service Manager獲取Binder實(shí)體的引用幽勒。
  3. Client使用服務(wù)與Server進(jìn)行通信:Client通過(guò)調(diào)用Binder實(shí)體與Server進(jìn)行通信嗜侮。

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

Binder通信的實(shí)質(zhì)是利用內(nèi)存映射啥容,將用戶進(jìn)程的內(nèi)存地址和內(nèi)核的內(nèi)存地址映射為同一塊物理地址锈颗,也就是說(shuō)他們使用的同一塊物理空間,每次創(chuàng)建Binder的時(shí)候大概分配128的空間干毅。數(shù)據(jù)進(jìn)行傳輸?shù)臅r(shí)候,從這個(gè)內(nèi)存空間分配一點(diǎn)泼返,用完了再釋放即可硝逢。

2. 序列化

# Android有哪些序列化方式?

為了解決Android中內(nèi)存序列化速度過(guò)慢的問(wèn)題绅喉,Android使用了Parcelable渠鸽。

對(duì)比 Serializable Parcelable
易用性 簡(jiǎn)單 不是很簡(jiǎn)單
效率
場(chǎng)景 IO、網(wǎng)絡(luò)和數(shù)據(jù)庫(kù) 內(nèi)存中

3. Framework

Zygote孕育進(jìn)程過(guò)程柴罐?

Zygote工作流程

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

Activity啟動(dòng)流程

建議閱讀:

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

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

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


App進(jìn)程
  • ActivityThread:依賴于Ui線程革屠,實(shí)際處理與AMS中交互的工作凿试。
  • ActivityManagerService:負(fù)責(zé)ActivityService等的生命周期工作似芝。
  • ApplicationThreadSystem Server進(jìn)程中ApplicatonThreadProxy的服務(wù)端那婉,幫助System Server進(jìn)程跟App進(jìn)程交流。
  • System Server:Android核心的進(jìn)程党瓮,掌管著Android系統(tǒng)中各種重要的服務(wù)详炬。
App啟動(dòng)流程

具體過(guò)程:

  1. 用戶點(diǎn)擊App圖標(biāo),Lanuacher進(jìn)程通過(guò)Binder聯(lián)系到System Server進(jìn)程發(fā)起startActivity寞奸。
  2. System Server通過(guò)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的安裝過(guò)程单旁?

建議閱讀:

《Android Apk安裝過(guò)程分析》

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

建議閱讀:

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

# Activity蔫饰、Window、ViewRoot和DecorView之間的關(guān)系愉豺?

建議閱讀:

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

4. Context

# 關(guān)于Context的理解篓吁?

建議閱讀:

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

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

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

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

  • Http基礎(chǔ):在Http請(qǐng)求中蚪拦,可以加入請(qǐng)求頭Range杖剪,下載指定區(qū)間的文件數(shù)。
  • RandomAccessFile:支持隨機(jī)訪問(wèn)驰贷,可以從指定位置進(jìn)行數(shù)據(jù)的讀寫(xiě)盛嘿。

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

  1. 通過(guò)HttpUrlConnection獲取文件長(zhǎng)度括袒。
  2. 自己分配好線程進(jìn)行制定區(qū)間的文件數(shù)據(jù)的下載次兆。
  3. 獲取到數(shù)據(jù)流以后森爽,使用RandomAccessFile進(jìn)行指定位置的讀寫(xiě)弟晚。

6. 性能優(yōu)化

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

建議閱讀:

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

7. 第三方庫(kù)

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

# Glide

Glide考察的頻率挺高的恃慧,常見(jiàn)的問(wèn)題有:

  • Glide和其他圖片加載框架的比較蚤认?
  • 如何設(shè)計(jì)一個(gè)圖片加載框架?
  • Glide緩存實(shí)現(xiàn)機(jī)制糕伐?
  • Glide如何處理生命周期砰琢?
  • ...

建議閱讀:

《Glide最全解析》
《面試官:簡(jiǎn)歷上最好不要寫(xiě)Glide,不是問(wèn)源碼那么簡(jiǎn)單》

# OkHttp

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

  • 責(zé)任鏈模式
  • interceptorsnetworkInterceptors的區(qū)別良瞧?

建議看一遍源碼陪汽,過(guò)程并不復(fù)雜。

# Retrofit

Retrofit常見(jiàn)問(wèn)題:

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

建議看一遍源碼褥蚯,過(guò)程并不復(fù)雜挚冤。

# RxJava

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

建議尋找一些RxJava的文章训挡。

# Android Jetpack(非必須)

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

  • 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ù)的生命周期盡可能的延長(zhǎng)忘分。
  • Paging:設(shè)計(jì)思想棋枕。

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

建議閱讀:

《Android Jetpack源碼分析系列》

8. 插件化和組件化

這個(gè)我基本沒(méi)用過(guò)妒峦,等用過(guò)了重斑,再和大家分享。

三肯骇、Java基礎(chǔ)

Java基礎(chǔ)

Java基礎(chǔ)中考察頻率比較高的是Object窥浪、String、面向?qū)ο罄鄣痢⒓虾蟆⒎盒秃头瓷洹?/p>

1. Object

# equals和==的區(qū)別突琳?equals和hashcode的關(guān)系若债?

  • ==:基本類型比較值,引用類型比較地址拆融。
  • equals:默認(rèn)情況下蠢琳,equals作為對(duì)象中的方法,比較的是地址镜豹,不過(guò)可以根據(jù)業(yè)務(wù)傲须,修改equals方法。

equalshashcode之間的關(guān)系:

默認(rèn)情況下趟脂,equals相等泰讽,hashcode必相等,hashcode相等昔期,equals不是必相等已卸。hashcode基于內(nèi)存地址計(jì)算得出,可能會(huì)相等硼一,雖然幾率微乎其微累澡。

2. String

# String、StringBuffer和StringBuilder的區(qū)別般贼?

  • StringString屬于不可變對(duì)象愧哟,每次修改都會(huì)生成新的對(duì)象奥吩。
  • StringBuilder:可變對(duì)象,非多線程安全蕊梧。
  • StringBuffer:可變對(duì)象霞赫,多線程安全。

大部分情況下望几,效率是:StringBuilder>StringBuffer>String绩脆。

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

# 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ù)寫(xiě)力九。
  3. 父類引用指向子類對(duì)象耍铜。

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

4. 集合

# HashMap的特點(diǎn)是什么?HashMap的原理抵乓?

HashMap的特點(diǎn):

  1. 基于Map接口伴挚,存放鍵值對(duì)。
  2. 允許key/value為空灾炭。
  3. 非多線程安全茎芋。
  4. 不保證有序,也不保證使用的過(guò)程中順序不會(huì)改變咆贬。

簡(jiǎn)單來(lái)講败徊,核心是數(shù)組+鏈表/紅黑樹(shù),HashMap的原理就是存鍵值對(duì)的時(shí)候:

  1. 通過(guò)鍵的Hash值確定數(shù)組的位置掏缎。
  2. 找到以后皱蹦,如果該位置無(wú)節(jié)點(diǎn)煤杀,直接存放。
  3. 該位置有節(jié)點(diǎn)即位置發(fā)生沖突沪哺,遍歷該節(jié)點(diǎn)以及后續(xù)的節(jié)點(diǎn)沈自,比較key值,相等則覆蓋辜妓。
  4. 沒(méi)有就新增節(jié)點(diǎn)枯途,默認(rèn)使用鏈表,相連節(jié)點(diǎn)數(shù)超過(guò)8的時(shí)候籍滴,在jdk 1.8中會(huì)變成紅黑樹(shù)酪夷。
  5. 如果Hashmap中的數(shù)組使用情況超過(guò)一定比例,就會(huì)擴(kuò)容孽惰,默認(rèn)擴(kuò)容兩倍晚岭。

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

  • key的hash值計(jì)算過(guò)程是高16位不變坦报,低16位和高16位取抑或,讓更多位參與進(jìn)來(lái)狂鞋,可以有效的減少碰撞的發(fā)生片择。
  • 初始數(shù)組容量為16,默認(rèn)不超過(guò)的比例為0.75骚揍。

5. 泛型

# 說(shuō)一下對(duì)泛型的理解字管?

泛型的本質(zhì)是參數(shù)化類型,在不創(chuàng)建新的類型的情況下疏咐,通過(guò)泛型指定不同的類型來(lái)控制形參具體限制的類型纤掸。也就是說(shuō)在泛型的使用中脐供,操作的數(shù)據(jù)類型被指定為一個(gè)參數(shù)浑塞,這種參數(shù)可以被用在類、接口和方法中政己,分別被稱為泛型類酌壕、泛型接口和泛型方法。

泛型是Java中的一種語(yǔ)法糖歇由,能夠在代碼編寫(xiě)的時(shí)候起到類型檢測(cè)的作用卵牍,但是虛擬機(jī)是不支持這些語(yǔ)法的。

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

  1. 類型安全沦泌,避免類型的強(qiáng)轉(zhuǎn)糊昙。
  2. 提高了代碼的可讀性,不必要等到運(yùn)行的時(shí)候才去強(qiáng)制轉(zhuǎn)換谢谦。

# 什么是類型擦除释牺?

不管泛型的類型傳入哪一種類型實(shí)參萝衩,對(duì)于Java來(lái)說(shuō),都會(huì)被當(dāng)成同一類處理没咙,在內(nèi)存中也只占用一塊空間猩谊。通俗一點(diǎn)來(lái)說(shuō),就是泛型只作用于代碼編譯階段祭刚,在編譯過(guò)程中牌捷,對(duì)于正確檢驗(yàn)泛型結(jié)果后,會(huì)將泛型的信息擦除涡驮,也就是說(shuō)暗甥,成功編譯過(guò)后的class文件是不包含任何泛型信息的。

6. 反射

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

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


代理模式

聲明一個(gè)接口淋袖,再分別實(shí)現(xiàn)一個(gè)真實(shí)的主題類和代理主題類,通過(guò)讓代理類持有真實(shí)主題類锯梁,從而控制用戶對(duì)真實(shí)主題的訪問(wèn)即碗。

動(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寫(xiě)一個(gè)形式完全一樣的代理類合敦。
  2. 使用一些動(dòng)態(tài)代理的方法可以在運(yùn)行時(shí)制定代理類的邏輯初橘,從而提升系統(tǒng)的靈活性。

四充岛、Java并發(fā)

Java并發(fā)

Java并發(fā)中考察頻率較高的有線程保檐、線程池、鎖崔梗、線程間的等待和喚醒夜只、線程特性和阻塞隊(duì)列等。

1. 線程

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

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

  • new:新創(chuàng)建的線程
  • Ready:準(zhǔn)備就緒的線程扔亥,由于CPU分配的時(shí)間片的關(guān)系,此時(shí)的任務(wù)不在執(zhí)行過(guò)程中谈为。
  • Running:正在執(zhí)行的任務(wù)
  • Block:被阻塞的任務(wù)
  • Time Waiting:計(jì)時(shí)等待的任務(wù)
  • Terminated:終止的任務(wù)

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


線程狀態(tài)轉(zhuǎn)換

# 線程中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è)主線程师妙,主線程可以用來(lái)操作界面元素屹培,如果有耗時(shí)的操作,必須開(kāi)啟子線程執(zhí)行锯岖,不然會(huì)出現(xiàn)ANR巍耗,除此以外孽水,進(jìn)程間的數(shù)據(jù)是獨(dú)立的,線程間的數(shù)據(jù)可以共享擎颖。

2. 線程池

線程池的地位十分重要,基本上涉及到跨線程的框架都使用到了線程池每篷,比如說(shuō)OkHttp鸵贬、RxJavaLiveData以及協(xié)程等畏铆。

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

  1. 節(jié)省開(kāi)銷: 線程池中的線程可以重復(fù)利用纳账。
  2. 速度快:任務(wù)來(lái)了就能開(kāi)始,省去創(chuàng)建線程的時(shí)間捺疼。
  3. 線程可控:線程數(shù)量可空和任務(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);
}

參數(shù)解釋如下:

  • corePoolSize:核心線程數(shù)量,不會(huì)釋放官扣。
  • maximumPoolSize:允許使用的最大線程池?cái)?shù)量翅敌,非核心線程數(shù)量,閑置時(shí)會(huì)釋放醇锚。
  • keepAliveTime:閑置線程允許的最大閑置時(shí)間哼御。
  • unit:閑置時(shí)間的單位坯临。
  • workQueue:阻塞隊(duì)列焊唬,不同的阻塞隊(duì)列有不同的特性恋昼。

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

  • CachedThreadPool:閑置線程超時(shí)會(huì)釋放,沒(méi)有閑置線程的情況下赶促,每次都會(huì)創(chuàng)建新的線程液肌。
  • FixedThreadPool:線程池只能存放指定數(shù)量的線程池,線程不會(huì)釋放鸥滨,可重復(fù)利用嗦哆。
  • SingleThreadExecutor:?jiǎn)尉€程的線程池。
  • ScheduledThreadPool:可定時(shí)和重復(fù)執(zhí)行的線程池婿滓。

# 線程池的工作流程老速?

線程池工作流程
圖片來(lái)自《線程池是怎樣工作的》

簡(jiǎn)而言之:

  1. 任務(wù)來(lái)了,優(yōu)先考慮核心線程凸主。
  2. 核心線程滿了橘券,進(jìn)入阻塞隊(duì)列。
  3. 阻塞隊(duì)列滿了卿吐,考慮非核心線程(圖上好像少了這個(gè)過(guò)程)旁舰。
  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. 訪問(wèn)控制不同:如果鎖住的是實(shí)例擅耽,只會(huì)針對(duì)同一個(gè)對(duì)象方法進(jìn)行同步訪問(wèn)活孩,多線程訪問(wèn)同一個(gè)對(duì)象的synchronized代碼塊是串行的,訪問(wèn)不同對(duì)象是并行的乖仇。如果鎖住的是類憾儒,多線程訪問(wèn)的不管是同一對(duì)象還是不同對(duì)象的synchronized代碼塊是都是串行的。

# synchronized的原理乃沙?

任何一個(gè)對(duì)象都有一個(gè)monitor與之相關(guān)聯(lián)起趾,JVM基于進(jìn)入和退出mointor對(duì)象來(lái)實(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不能去嘗試獲得鎖扩氢,沒(méi)有獲得鎖就會(huì)被阻塞; Lock可以去嘗試獲得鎖爷辱,如果未獲得可以嘗試處理其他邏輯录豺。
  5. synchronized多線程效率不如Lock掰伸,不過(guò)Java在1.6以后已經(jīng)對(duì)synchronized進(jìn)行大量的優(yōu)化摇邦,所以性能上來(lái)講,其實(shí)差不了多少椅寺。

# 悲觀鎖和樂(lè)觀鎖的舉例示启?以及它們的相關(guān)實(shí)現(xiàn)兢哭?

悲觀鎖和樂(lè)觀鎖的概念:

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

舉例:

  • 悲觀鎖:典型的悲觀鎖是獨(dú)占鎖排霉,有synchronized窍株、ReentrantLock
  • 樂(lè)觀鎖:典型的樂(lè)觀鎖是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喚醒所有等待的線程蘸秘,讓他們競(jìng)爭(zhēng)鎖官卡。

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

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

5. 多線程間的特性

# 多線程間的有序性哮翘、可見(jiàn)性和原子性是什么意思?

  • 原子性:執(zhí)行一個(gè)或者多個(gè)操作的時(shí)候毛秘,要么全部執(zhí)行饭寺,要么都不執(zhí)行,并且中間過(guò)程中不會(huì)被打斷叫挟。Java中的原子性可以通過(guò)獨(dú)占鎖和CAS去保證
  • 可見(jiàn)性:指多線程訪問(wèn)同一個(gè)變量的時(shí)候艰匙,一個(gè)線程修改了變量的值,其他線程能夠立刻看得到修改的值抹恳。鎖和volatile能夠保證可見(jiàn)性
  • 有序性:程序執(zhí)行的順序按照代碼先后的順序執(zhí)行员凝。鎖和volatile能夠保證有序性

# happens-before原則有哪些?

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

如果兩個(gè)操作的先后順序不能通過(guò)happens-before原則推倒出來(lái),那就不能保證它們的先后執(zhí)行順序瓶蚂,虛擬機(jī)就可以隨意打亂執(zhí)行指令糖埋。happens-before原則有:

  1. 程序次序規(guī)則:?jiǎn)尉€程程序的執(zhí)行結(jié)果得和看上去代碼執(zhí)行的結(jié)果要一致。
  2. 鎖定規(guī)則:一個(gè)鎖的lock操作一定發(fā)生在上一個(gè)unlock操作之后窃这。
  3. volatile規(guī)則:對(duì)volatile變量的寫(xiě)操作一定先行于后面對(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的原理叼架?

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

多處理器的環(huán)境下扮饶,其他處理器的緩存還是舊的,為了保證各個(gè)處理器一致乍构,會(huì)通過(guò)嗅探在總線上傳播的數(shù)據(jù)來(lái)檢測(cè)自己的數(shù)據(jù)是否過(guò)期甜无,如果過(guò)期,會(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)的無(wú)界的FIFO(先進(jìn)先出)阻塞隊(duì)列。
  • SynchronousQueue:內(nèi)部沒(méi)有任何緩存的阻塞隊(duì)列扔茅。
  • PriorityBlockingQueue:具有優(yōu)先級(jí)的無(wú)限阻塞隊(duì)列已旧。

# ConcurrentHashMap的原理

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

JDK 1.8之前采用的是分段鎖运褪,核心類是一個(gè)SegmentSegment繼承了ReentrantLock萤晴,每個(gè)Segment對(duì)象管理若干個(gè)桶吐句,多個(gè)線程訪問(wèn)同一個(gè)元素的時(shí)候只能去競(jìng)爭(zhēng)獲取鎖。

JDK 1.8采用了CAS + synchronized店读,插入鍵值對(duì)的時(shí)候如果當(dāng)前桶中沒(méi)有Node節(jié)點(diǎn)嗦枢,使用CAS方式進(jìn)行更新,如果有Node節(jié)點(diǎn)屯断,則使用synchronized的方式進(jìn)行更新文虏。

五、Jvm

JVM

Jvm中考察頻率較高的內(nèi)容有:Jvm內(nèi)存區(qū)域的劃分殖演、GC機(jī)制和類加載機(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)存讀取過(guò)來(lái)的您机。
  3. 不同線程間的工作內(nèi)存無(wú)法進(jìn)行直接交流穿肄,必須通過(guò)主內(nèi)存完成。


    Java內(nèi)存模型

    主內(nèi)存和工作內(nèi)存之間的交互協(xié)議际看,即變量如何從主內(nèi)存?zhèn)鬟f到工作內(nèi)存咸产、工作內(nèi)存如何將變量傳遞到主內(nèi)存,Java內(nèi)存模型定義了8種操作來(lái)完成仲闽,并且每一種操作都是原子的脑溢,不可再分的。

類型 說(shuō)明
lock 作用于主內(nèi)存的變量赖欣,把一個(gè)變量標(biāo)識(shí)一個(gè)線程獨(dú)占的狀態(tài)
unlock 作用于主內(nèi)存的變量屑彻,把一個(gè)處于鎖定狀態(tài)的變量釋放出來(lá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傳遞過(guò)來(lái)的工作內(nèi)存中的變量寫(xiě)入到主內(nèi)存中的變量

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

  1. 指向方法區(qū):"abc"是常量搏恤,所以它會(huì)在方法區(qū)中分配內(nèi)存,如果方法區(qū)已經(jīng)給"abc"分配過(guò)內(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á)性算法的思路是:通過(guò)一些列被成為GC Roots的對(duì)象作為起始點(diǎn)弊添,自上往下從這些起點(diǎn)往下搜索录淡,搜索所有走過(guò)的路徑稱為引用鏈,如果一個(gè)對(duì)象沒(méi)有跟任何引用鏈相關(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和速度慢刊懈。

# 說(shuō)一下四種引用以及他們的區(qū)別这弧?

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

3. 類加載

# 類加載的過(guò)程渐白?

類加載的過(guò)程可以分為:

  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++語(yǔ)言實(shí)現(xiàn),負(fù)責(zé)加載Java中的核心類拴鸵。
  • 擴(kuò)展類加載器:負(fù)責(zé)加載Java擴(kuò)展的核心類之外的類。
  • 應(yīng)用程序類加載器:負(fù)責(zé)加載用戶類路徑上指定的類庫(kù)蜗搔。

雙親委派模型如下:


雙親委派模型

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

雙親委派模型的工作流程:
當(dāng)一個(gè)類加載的任務(wù)來(lái)臨的時(shí)候聘芜,先交給父類加載器完成,父類加載器交給父父類加載器完成缝龄,知道傳遞給啟動(dòng)類加載器汰现,如果完成不了的情況下,再依次往下傳遞類加載的任務(wù)叔壤。

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

六驮捍、kotlin

Kotlin

1. 基礎(chǔ)

# ==疟呐、===和equal的區(qū)別?

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

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

  • var:可變引用,具有可讀和可寫(xiě)權(quán)限珊泳,值可變富纸,類型不可變
  • val:不可變引用,具有可讀權(quán)限旨椒,值不可變晓褪,但是對(duì)象的屬性可變

2. 函數(shù)

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

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

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

頂層函數(shù)實(shí)質(zhì)就是Java中的靜態(tài)函數(shù)好港,可以通過(guò)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)
}

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

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

解構(gòu)聲明將對(duì)象中的所有屬性隘竭,解構(gòu)成一組屬性變量塘秦,而且這些變量可以單獨(dú)使用,可以單數(shù)使用的原因是通過(guò)獲取對(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ì)象去訪問(wèn)對(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ù)訪問(wèn)不了私有的屬性和成員方法,成員函數(shù)可以吭产。
  3. 繼承:擴(kuò)展函數(shù)不可復(fù)寫(xiě)侣监,成員函數(shù)可以復(fù)寫(xiě)。

它們的使用方式類似臣淤。

3. 類橄霉、對(duì)象和接口

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

  • open:運(yùn)行創(chuàng)建子類或者復(fù)寫(xiě)子類的方法邑蒋。
  • final:不允許創(chuàng)建子類和復(fù)寫(xiě)子類的方法姓蜂。
  • abstract:抽象類,必須復(fù)寫(xiě)子類的方法医吊。

在Kotlin中钱慢,默認(rèn)的類和方法的修飾符都是final的,如果想讓類和方法能夠被繼承或者復(fù)寫(xiě)卿堂,需要顯示的添加open修飾符束莫。

# Kotlin中可見(jiàn)性修飾符有哪些?

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

Java默認(rèn)的訪問(wèn)權(quán)限是包訪問(wèn)權(quán)限览绿,Kotlin中默認(rèn)的訪問(wèn)權(quán)限是public。

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

  • Kotlin:默認(rèn)相當(dāng)于Java中的靜態(tài)內(nèi)部類饿敲,如果想訪問(wèn)類中的成員方法和屬性,需要添加inner關(guān)鍵字修飾逛绵。
  • Java:默認(rèn)持有外部類引用怀各,可以訪問(wèn)成員方法和屬性,如果想聲明為靜態(tài)內(nèi)部類暑脆,需要添加static關(guān)鍵字修飾渠啤。

# Kotlin屬性代理背后原理?

可以簡(jiǎn)單理解為屬性的settter添吗、getter訪問(wèn)器內(nèi)部實(shí)現(xiàn)交給了代理對(duì)象來(lái)實(shí)現(xiàn),相當(dāng)于使用一個(gè)代理對(duì)象代替了原來(lái)簡(jiǎn)單屬性的讀寫(xiě)過(guò)程份名,而暴露外部屬性操作還是不變 的碟联,照樣是屬性賦值和讀取,只是setter僵腺、getter內(nèi)部具體實(shí)現(xiàn)變了鲤孵。

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

共同點(diǎn):
定義單例的一種方式辰如,提供靜態(tài)成員和方法普监。

不同點(diǎn):

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

4. lambda

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

  • 普通表達(dá)式:()->R
  • 帶接收者對(duì)象的表達(dá)式:T.()->R廊散,可以訪問(wèn)接收者對(duì)象的屬性和成員方法桑滩。如apply

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

  • Java中的內(nèi)部類:局部變量必須是final聲明的运准,無(wú)法去修改局部變量的值。
  • Kotlin中l(wèi)ambda表達(dá)式:不要求final聲明缭受,對(duì)于非final修飾的lambda表達(dá)式胁澳,可以修改局部變量的值。

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

class Ref<T>(var value:T)

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

# 使用lambda表達(dá)式訪問(wèn)的局部變量有什么不同塘雳?

默認(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)
}

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

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

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

# 序列是什么?集合類和序列的操作符比較拔恰?

Sequence(序列)是一種惰性集合因谎,可以更高效地對(duì)元素進(jìn)行鏈?zhǔn)讲僮鳎恍枰獎(jiǎng)?chuàng)建額外的集合保存過(guò)程中產(chǎn)生的中間結(jié)果颜懊,簡(jiǎn)單來(lái)講财岔,就是序列中所有的操作都是按順序應(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()
}

對(duì)于上述序列中的"1",它會(huì)先執(zhí)行filter匠璧,再執(zhí)行map桐款,之后再對(duì)"2"重復(fù)操作。除此以外患朱,序列中所有的中間操作都是惰性的鲁僚。

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

  • 集合類:mapfilter方法是內(nèi)聯(lián),不會(huì)生成匿名類的實(shí)例裁厅,但每次進(jìn)行mapfilter都會(huì)生成新的集合冰沙,當(dāng)數(shù)據(jù)量大的時(shí)候,消耗的內(nèi)存也比較大执虹。
  • 序列:mapfitler非內(nèi)聯(lián)拓挥,會(huì)生成匿名類實(shí)例,但不需要?jiǎng)?chuàng)建額外的集合保存中間操作的結(jié)果袋励。

# 為什么要使用內(nèi)聯(lián)函數(shù)侥啤??jī)?nèi)聯(lián)函數(shù)的作用?

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

  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í)的開(kāi)銷钾腺。內(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í)擦除的問(wèn)題间螟。

5. 類型系統(tǒng)

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

在Kotlin中,使用的時(shí)候是不區(qū)分基本類型的厢破,統(tǒng)一如下:
Int邮府、ByteShort溉奕、LongFloat忍啤、Double加勤、CharBoolean仙辟。

使用統(tǒng)一的類型并不意味著Kotlin中所有的基本類型都是引用類型,大多數(shù)情況下鳄梅,對(duì)于變量叠国、參數(shù)、返回類型和屬性都會(huì)被編譯成基本類型戴尸,泛型類會(huì)被編譯成Java中的包裝類粟焊,即引用類型。

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

在Kotlin中项棠,集合會(huì)被分為兩大類型,只讀集合和可變集合挎峦。

  • 只讀集合:對(duì)集合只有讀取權(quán)限香追。
  • 可變集合:能夠刪除、新增坦胶、修改和讀取元素透典。

但是有一點(diǎn)需要注意,只讀集合不一定是不可變的顿苇,如果你使用的變量是只讀集合峭咒,它可能是眾多集合引用中的一個(gè),任何一個(gè)集合引用都有可能是可變集合纪岁。

# Array<Int>和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官方文檔上說(shuō):

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

為什么說(shuō)它是輕量級(jí)的線程蚓耽,因?yàn)閺墓俜浇嵌葋?lái)講,創(chuàng)建十萬(wàn)個(gè)協(xié)程沒(méi)什么問(wèn)題打印任務(wù)不會(huì)存在問(wèn)題旋炒,創(chuàng)建十萬(wàn)個(gè)線程會(huì)造成內(nèi)存問(wèn)題步悠,可能會(huì)造成內(nèi)存溢出。但是這個(gè)對(duì)比有問(wèn)題瘫镇,因?yàn)閰f(xié)程本質(zhì)上是基于Java的線程池的鼎兽,你去用線程池創(chuàng)建十萬(wàn)個(gè)打印任務(wù)是不會(huì)造成內(nèi)存溢出的答姥。

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

# 協(xié)程的原理敲长?

很多人都會(huì)講,協(xié)程中處理耗時(shí)任務(wù)秉继,協(xié)程會(huì)先掛起祈噪,執(zhí)行完,再切回來(lái)秕噪。我在這就淺顯的分析這兩步钳降。

  • 掛起:協(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ò)

網(wǎng)絡(luò)

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

高頻網(wǎng)絡(luò)知識(shí)有TCP哆窿、HTTP和HTTPS。

1. HTTP和HTTPS

# HTTP是哪一層的協(xié)議厉斟,常見(jiàn)的HTTP狀態(tài)碼有哪些挚躯,分別代表什么意思?

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

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

類別 解釋
1xx 請(qǐng)求已經(jīng)接收码荔,繼續(xù)處理
2xx 服務(wù)器已經(jīng)正確處理請(qǐng)求,比如200
3xx 重定向感挥,需要做進(jìn)一步的處理才能完成請(qǐng)求
4xx 服務(wù)器無(wú)法理解的請(qǐng)求缩搅,比如404,訪問(wèn)的資源不存在
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í)慢宗。


    HTTP2連接

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

簡(jiǎn)單來(lái)說(shuō)奔穿,HTTP和HTTPS的關(guān)系是這樣的

HTTPS = HTTP + SSL/TLS

區(qū)別如下:
HTTP作用于應(yīng)用層镜沽,使用80端口,起始地址是http://贱田,明文傳輸缅茉,消息容易被攔截,串改男摧。
HTTPS作用域傳輸層蔬墩,使用443端口,起始地址是https://耗拓,需要下載CA證書(shū)拇颅,傳輸?shù)倪^(guò)程需要加密,安全性高乔询。

# SSL/TLS的握手過(guò)程樟插?

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

SSL/TLS

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

過(guò)程和上圖類似黄锤,依次獲取證書(shū),公鑰食拜,最后生成對(duì)稱加密的鑰匙進(jìn)行對(duì)稱加密鸵熟。

對(duì)稱加密可以保證加密效率,但是不能解決密鑰傳輸問(wèn)題监婶;非對(duì)稱加密可以解決傳輸問(wèn)題旅赢,但是效率不高。

2. TCP相關(guān)

# TCP的三次握手過(guò)程惑惶,為什么需要三次煮盼,而不是兩次或者四次?

三次握手

只發(fā)送兩次带污,服務(wù)端是不知道自己發(fā)送的消息能不能被客戶端接收到僵控。
因?yàn)門(mén)CP握手是三次,所以此時(shí)雙方都已經(jīng)知道自己發(fā)送的消息能夠被對(duì)方收到鱼冀,所以报破,第四次的發(fā)送就顯得多余了悠就。

# TCP的四次揮手過(guò)程?

四次揮手

大致意思就是:

  • Client:我要斷開(kāi)連接了
  • Server:我收到你的消息了
  • Server:我也要斷開(kāi)連接了
  • Client:收到你要斷開(kāi)連接的消息了

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

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

  • TCP:基于字節(jié)流盹靴、面向連接炸茧、可靠、能夠進(jìn)行全雙工通信稿静,除此以外梭冠,還能進(jìn)行流量控制和擁塞控制,不過(guò)效率略低
  • UDP:基于報(bào)文改备、面向無(wú)連接控漠、不可靠,但是傳輸效率高悬钳。

總的來(lái)說(shuō)盐捷,TCP適用于傳輸效率要求低,準(zhǔn)確性要求高或要求有連接他去。而UDP適用于對(duì)準(zhǔn)確性要求較低毙驯,傳輸效率要求較高的場(chǎng)景,比如語(yǔ)音通話灾测、直播等爆价。

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

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

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

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

經(jīng)常考察的設(shè)計(jì)模式不多筹误,活學(xué)活用即可桐早。

1. 六大原則

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

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

2. 單例模式

單例模式被問(wèn)到的幾率很大,通常會(huì)問(wèn)如下幾種問(wèn)題哄酝。

# 單例的常用寫(xiě)法有哪幾種友存?

懶漢模式

public class SingleInstance {
    private static SingleInstance instance;
    private SingleInstance() {}
    public static synchronized SingleInstance getInstance() {
        if(instance == null) {
            instance = new SingleInstance();
        }
        return instance;
    }
}

該模式的主要問(wèn)題是每次獲取實(shí)例都需要同步,造成不必要的同步開(kāi)銷陶衅。
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ā)環(huán)境下可能會(huì)發(fā)生問(wèn)題屡立。
靜態(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();
    }
}

枚舉單例

public enum SingletonEnum {
    INSTANCE
}

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

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

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

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

由于Jvm會(huì)優(yōu)化指令順序,也就是說(shuō)2和3的順序是不能保證的儒士。在多線程的情況下的止,當(dāng)一個(gè)線程完成了1、3過(guò)程后着撩,當(dāng)前線程的時(shí)間片已用完诅福,這個(gè)時(shí)候會(huì)切換到另一個(gè)線程,另一個(gè)線程調(diào)用這個(gè)單例拖叙,會(huì)使用這個(gè)還沒(méi)初始化完成的實(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;
    }
}

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

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

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

4. MVC\MVP\MVVM

MVC挖滤、MVP和MVVM應(yīng)該是設(shè)計(jì)模式中考察頻率最高的知識(shí)點(diǎn)了崩溪,嚴(yán)格意義上來(lái)說(shuō),它們不能算是設(shè)計(jì)模式斩松,而是框架伶唯。

# MVC、MVP和MVVM是什么惧盹?

圖片已有乳幸,不再給出

  • MVC:Model-View-Controller,是一種分層解偶的框架钧椰,Model層提供本地?cái)?shù)據(jù)和網(wǎng)絡(luò)請(qǐng)求粹断,View層處理視圖,Controller處理邏輯演侯,存在問(wèn)題是Controller層和View層的劃分不明顯姿染,Model層和View層的存在耦合。
  • MVP:Model-View-Presenter,是對(duì)MVC的升級(jí)悬赏,Model層和View層與MVC的意思一致狡汉,但Model層和View層不再存在耦合,而是通過(guò)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)一步解耦衅斩,簡(jiǎn)單來(lái)講,在MVC中怠褐,View層既可以和Controller層交互畏梆,又可以和Model層交互;而在MVP中奈懒,View層只能和Presenter層交互奠涌,Model層也只能和Presenter層交互,減少了View層和Model層的耦合磷杏,更容易定位錯(cuò)誤的來(lái)源溜畅。

# 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)聽(tīng)到這個(gè)變化峦椰,從而主動(dòng)去更新,這其實(shí)是主動(dòng)的汰规。

# ViewModel如何知道View層的生命周期汤功?

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

九述吸、算法題

沒(méi)什么好說(shuō)的忿族,Leetcode + 《劍指Offer》锣笨,著重記住一些解決問(wèn)題的思路。

除此以外道批,你還得記住一些常用的算法:排序错英、反轉(zhuǎn)鏈表、樹(shù)的遍歷和手寫(xiě)LruCache隆豹,這些都寫(xiě)不出來(lái)椭岩,就尷尬了。

如果你不想閱讀書(shū)籍璃赡,可以參考一下這個(gè)Github判哥,親眼見(jiàn)證了從3k Star到34k Star,跪了:

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

十碉考、簡(jiǎn)歷

簡(jiǎn)歷中最重要的是項(xiàng)目經(jīng)歷塌计,

可能有的同學(xué)會(huì)說(shuō),我天天在公司擰螺絲侯谁,根本沒(méi)什么東西可寫(xiě)夺荒。

所以我們?cè)谄綍r(shí)的工作中,不應(yīng)該僅僅滿足于寫(xiě)一些業(yè)務(wù)代碼良蒸,而應(yīng)該常常思考:

  • 在結(jié)合的業(yè)務(wù)的情況下,我可以再做一點(diǎn)什么伍玖?
  • 對(duì)于已經(jīng)寫(xiě)完的代碼嫩痰,我還可以做哪一些優(yōu)化?

寫(xiě)在最后

經(jīng)常聽(tīng)到一些同學(xué)調(diào)侃窍箍,Boss不聘串纺、前程堪憂、拉不上鉤椰棘,確實(shí)纺棺,今年的大環(huán)境比較嚴(yán)峻,但是一些高級(jí)崗位仍然稀缺邪狞。

談一下我自己祷蝌,小廠背景、18年畢業(yè)帆卓、普通學(xué)校巨朦,所以,大廠都沒(méi)給過(guò)面試機(jī)會(huì)剑令,好在前兩周內(nèi)推成功了糊啡,我也抓住了這次機(jī)會(huì),成功獲得了大廠的Offer吁津。

所以我想表達(dá)什么棚蓄?打鐵還需自身硬,一定是得建立完比較完整的知識(shí)體系的前提下,當(dāng)機(jī)會(huì)來(lái)臨的時(shí)候梭依,才能夠穩(wěn)穩(wěn)地把握住稍算,希望和大家共勉~

如果大家還有什么問(wèn)題,歡迎在下方留言和我討論睛挚。

分享不易邪蛔,你的點(diǎn)贊是我分享的動(dòng)力。

文章里的腦圖有可能被壓縮扎狱,如需高清大圖:

關(guān)注 九心說(shuō)侧到,回復(fù) 大圖,下載高清大圖淤击。

招聘

最近組里缺人匠抗,急招 Android 高級(jí)開(kāi)發(fā),如有需要污抬,歡迎私信我內(nèi)推汞贸。

閱文集團(tuán),騰訊旗下文娛類產(chǎn)品印机,包括QQ閱讀矢腻、起點(diǎn)讀書(shū)等。

主要參考:

https://github.com/LRH1993

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末射赛,一起剝皮案震驚了整個(gè)濱河市多柑,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌楣责,老刑警劉巖竣灌,帶你破解...
    沈念sama閱讀 216,324評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異秆麸,居然都是意外死亡初嘹,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)沮趣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)屯烦,“玉大人,你說(shuō)我怎么就攤上這事兔毒÷辏” “怎么了潮太?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,328評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵骨杂,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我疾嗅,道長(zhǎng)豪嗽,這世上最難降的妖魔是什么谴蔑? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,147評(píng)論 1 292
  • 正文 為了忘掉前任豌骏,我火速辦了婚禮,結(jié)果婚禮上隐锭,老公的妹妹穿的比我還像新娘窃躲。我一直安慰自己,他們只是感情好钦睡,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布蒂窒。 她就那樣靜靜地躺著,像睡著了一般荞怒。 火紅的嫁衣襯著肌膚如雪洒琢。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,115評(píng)論 1 296
  • 那天褐桌,我揣著相機(jī)與錄音衰抑,去河邊找鬼。 笑死荧嵌,一個(gè)胖子當(dāng)著我的面吹牛呛踊,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播啦撮,決...
    沈念sama閱讀 40,025評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼谭网,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了赃春?” 一聲冷哼從身側(cè)響起蜻底,我...
    開(kāi)封第一講書(shū)人閱讀 38,867評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎聘鳞,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體要拂,經(jīng)...
    沈念sama閱讀 45,307評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡抠璃,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了脱惰。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片搏嗡。...
    茶點(diǎn)故事閱讀 39,688評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖拉一,靈堂內(nèi)的尸體忽然破棺而出采盒,到底是詐尸還是另有隱情,我是刑警寧澤蔚润,帶...
    沈念sama閱讀 35,409評(píng)論 5 343
  • 正文 年R本政府宣布磅氨,位于F島的核電站,受9級(jí)特大地震影響嫡纠,放射性物質(zhì)發(fā)生泄漏烦租。R本人自食惡果不足惜延赌,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望叉橱。 院中可真熱鬧挫以,春花似錦、人聲如沸窃祝。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,657評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)粪小。三九已至大磺,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間糕再,已是汗流浹背量没。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,811評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留突想,地道東北人殴蹄。 一個(gè)月前我還...
    沈念sama閱讀 47,685評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像猾担,于是被迫代替她去往敵國(guó)和親袭灯。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評(píng)論 2 353

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