前言
好久沒更新了母廷,我裂開來,一直在忙新項目的事情糊肤,唉琴昆! 陸陸續(xù)續(xù)也沒啥時候?qū)憱|西了,剛過完元旦轩褐,才有了休息時間給大家做做分享椎咧。 這個2020,程序員實在是太難了把介。
眼瞅著馬上又要過年了勤讽,口袋里的錢是一天比一天少,誰誰誰擺滿月酒拗踢,哪家鄰居的二兒子要結(jié)婚了脚牍,大堂姐又要辦喬遷宴了...... 一大堆的人情往來,但是你又莫得辦法巢墅,這個年可能過得會有些寒冷V钕痢!君纫!
過完年就是金三銀四了驯遇,各位兄弟姐妹、英雄好漢們蓄髓,我可能會換工作了叉庐,不知道你們都準(zhǔn)備好了沒有。
本篇獻給所有準(zhǔn)備在金三銀四找工作的你們;岷取6傅!
1. JAVA 中面向?qū)ο蟮奶卣饔心男?/h3>
主要有四大特性:封裝肢执、繼承枉阵、多態(tài)、抽象(很多人也認為只有三大特性)
封裝
封裝的思想保證了類內(nèi)部數(shù)據(jù)結(jié)構(gòu)的完整性预茄,使用戶無法輕易直接操作類的內(nèi)部數(shù)據(jù)兴溜,這樣降低了對內(nèi)部數(shù)據(jù)的影響,提高了程序的安全性和可維護性耻陕。
優(yōu)點:
- 只能通過規(guī)定方法訪問數(shù)據(jù)昵慌。
- 隱藏類數(shù)實現(xiàn)細節(jié)。
- 方便修改實現(xiàn)淮蜈。
- 方便加入控制語句。
繼承
繼承就是子類繼承父類的特征和行為已卷,使得子類對象(實例)具有父類的實例域和方法梧田,或類從父 類繼承方法,使得子類具有父類相同的行為。
還有一個地方需要知道的是裁眯,這里還有一個概念叫做組合鹉梨,可以在面試的時候提一下。組合其實很簡單穿稳,就是單純的將某個對象引入當(dāng)前類存皂,讓當(dāng)前類有引入類的功能,因為 JAVA 只能單繼承逢艘,所以某些場景下組合其實更合適旦袋。
特點:
- 繼承父類的重用。
- 繼承可以多層繼承它改。
- 一個類只能繼承一個父類疤孕。
- 父類中
private
修飾的不能被繼承。 - 構(gòu)造方法不能被繼承央拖。
多態(tài)
多態(tài)是同一個行為具有多個不同表現(xiàn)形式或形態(tài)的能力祭阀。產(chǎn)生的場景是將子類對象賦給父類的引用,會產(chǎn)生多態(tài)鲜戒。如:Father son = new Son()
此時的對象son
只能調(diào)用Father
的方法(指的是子類重寫或者繼承父類的那些方法)专控,而不能調(diào)用本身定義的一些方法。因為多態(tài)中強調(diào):編寫 java 程序時遏餐,引用類型變量只能調(diào)用其編譯時類型的變量伦腐,不能調(diào)用其運行時類型變量。
特點:
- 繼承父類的重用境输。
- 繼承可以多層繼承蔗牡。
- 一個類只能繼承一個父類。
- 父類中 private 修飾的不能被繼承嗅剖。
- 構(gòu)造方法不能被繼承辩越。
必要條件 : 繼承、重寫信粮、父類引用指向子類對象黔攒。
作用:
- 不必編寫每一子類的功能調(diào)用,可以直接把不同子類當(dāng)父類看强缘,屏蔽子類間的差異(也可以說隱藏了細節(jié))督惰,提高代碼的通用率/復(fù)用率。
- 父類引用可以調(diào)用不同子類的功能旅掂,提高了代碼的擴充性和可維護性赏胚。
抽象
用 abstract
關(guān)鍵字來修飾一個類時,這個類叫作抽象類商虐。抽象類是它的所有子類的公共屬性的集合觉阅,是包含一個或多個抽象方法的類崖疤。但不意味著抽象類中只能有抽象方法,它和普通類一樣典勇,可以擁有普通的成員變量劫哼、方法。
特點:
- 抽象類不能被實例化割笙。抽象類的子類必須給出抽象類中的抽象方法的具體實現(xiàn)权烧,除非該子類也是抽象類。
- 抽象類中不一定包含抽象方法伤溉,但是有抽象方法的類必定是抽象類般码。
- 抽象類中的抽象方法只是聲明,不包含方法體谈火,就是不給出方法的具體實現(xiàn)也就是方法的具體功能侈询。
- 構(gòu)造方法,類方法(用
static
修飾的方法)不能聲明為抽象方法糯耍。 - 被定義為
abstract
的類需要被子類繼承扔字,但是被修飾為final
的類是不能被繼承和改寫的,這兩者是不能一起用來做修飾的温技。
2. JAVA 中的基本數(shù)據(jù)類型有哪些革为,對應(yīng)的大小和包裝類型是什么?
八種基本數(shù)據(jù)類型: int
舵鳞、short
震檩、float
、double
蜓堕、long
抛虏、boolean
、byte
套才、char
迂猴。
封裝類分別是: Integer
、Short
背伴、Float
沸毁、Double
、Long
傻寂、Boolean
息尺、Byte
、Character
疾掰。
大小分別是(byte): int:4
搂誉、short:2
、float:4
静檬、double:8
炭懊、long:8
浪汪、boolean:1
、byte:1
凛虽、char:2
。
3. Java 的引用類型有哪些广恢?
一共有四種引用凯旋,分別是強引用、軟引用(SoftReference
)钉迷、弱引用(WeakReference
)至非、虛引用(PhantomReference
)。
- 強引用
最普遍的一種引用方式糠聪。如String s = "abc"
荒椭, 變量s
就是字符串abc
的強引用。只要強引用存在舰蟆,則垃圾回收器就不會回收這個對象趣惠。 - 軟引用(
SoftReference.java
)
用于描述還有用但非必須的對象,如果內(nèi)存足夠身害,不回收味悄,如果內(nèi)存不足,則回收塌鸯。一般用于實現(xiàn)內(nèi)存敏感的高速緩存侍瑟,軟引用可以和引用隊列ReferenceQueue
聯(lián)合使用,如果軟引用的對象被垃圾回收丙猬,JVM 就會把這個軟引用加入到與之關(guān)聯(lián)的引用隊列中涨颜。 - 弱引用(
WeakReference.java
)
弱引用和軟引用大致相同,弱引用與軟引用的區(qū)別在于:只具有弱引用的對象擁有更短暫的生命周期茧球。在垃圾回收器線程掃描它所管轄的內(nèi)存區(qū)域的過程中庭瑰,一旦發(fā)現(xiàn)了只具有弱引用的對象,不管當(dāng)前內(nèi)存空間足夠與否袜腥,都會回收它的內(nèi)存见擦。 - 虛引用(
PhantomReference.java
)
就是形同虛設(shè),與其他幾種引用都不同羹令,虛引用并不會決定對象的生命周期鲤屡。如果一個對象僅持有虛引用,那么它就和沒有任何引用一樣福侈,在任何時候都可能被垃圾回收器回收酒来。 虛引用主要用來跟蹤對象被垃圾回收器回收的活動。
4. String肪凛、StringBuffer 和 StringBuilder 的區(qū)別有哪些堰汉?
String
String
是一個不可變(值是不可變的)的類對象辽社,這就導(dǎo)致每次對String
的操作都會生成新的String
對象,在操作頻繁的場景中可能會出現(xiàn)性能問題和內(nèi)存溢出的情況翘鸭。
StringBuffer
StringBuffer
是一個值可變的類對象滴铅,內(nèi)部維護的是字符數(shù)組。另外 StringBuffer
是線程安全的就乓,因為StringBuffer
所有 public
方法都是 synchronized
修飾的汉匙。這也就會導(dǎo)致在數(shù)據(jù)量大的情況下的性能問題。
StringBuilder
StringBuilder
和StringBuffer
是一樣的生蚁,都是字符串變量噩翠。區(qū)別在于StringBuilder
的方法都沒有加synchronized
修飾。所以在性能上要優(yōu)于 StringBuffer
邦投,但是StringBuilder
在多線程環(huán)境下不是線程安全的伤锚。
5. 能說說進程和線程的區(qū)別嗎?
每次問到能說說這種類型的問題時志衣,我真想說一句不能屯援。
進程
進程指的是在系統(tǒng)中正在運行的一個應(yīng)用程序。程序一旦運行就是進程蠢涝,進程是資源分配的最小單位玄呛。
線程
線程是系統(tǒng)分配處理器時間資源的基本單元,是程序執(zhí)行的最小單位和二∨锹粒或者說進程內(nèi)獨立執(zhí)行的一個單元執(zhí)行流。
栗子
比如我們平常用的微信其實就是一個進程惯吕。而像微信中的朋友圈惕它、掃一掃等功能就可以理解成為一個個單獨的線程。
6. 你們平時是怎么創(chuàng)建線程的废登,還有其他的方式嗎淹魄?
- 通過實現(xiàn)
Runnable
接口,將Runnable
實現(xiàn)類當(dāng)成參數(shù)來創(chuàng)建線程類堡距。 - 通過繼承
Thread
類創(chuàng)建線程類(重寫run
方法)甲锡。 - 通過實現(xiàn)
Callable
和Future
創(chuàng)建線程(可以獲取線程的返回值)。
7. Thread 類中的 start 和 run 方法有什么區(qū)別羽戒?
start
方法被用來啟動新創(chuàng)建的線程缤沦,而run
一般是線程對應(yīng)的業(yè)務(wù)邏輯。
另一方面start
方法內(nèi)部調(diào)用了run
方法易稠,這和直接調(diào)用run
方法的效果不一樣缸废。當(dāng)你調(diào)用run
方法的時候,只會是在原來的線程中調(diào)用,沒有新的線程啟動企量,start
方法才會啟動新線程测萎。
8. 線程池用過嗎,原理是什么届巩?
用過的硅瞧,由于頻繁的創(chuàng)建和銷毀線程一方面會增加對系統(tǒng)資源的消耗,另一方面恕汇,會影響程序的性能零酪。在處理多任務(wù)的場景下,可以使用池化技術(shù)將創(chuàng)建和銷毀這過程給省略掉拇勃。 在 Java 中,常見的線程池有newSingleThreadExecutor
(單線程化的線程池孝凌,它只會用唯一的工作線程來執(zhí)行任務(wù)方咆,保證所有任務(wù)按照指定順序(FIFO, LIFO, 優(yōu)先級)執(zhí)行)、newFixedThreadPool
(定長線程池蟀架,可控制線程最大并發(fā)數(shù)瓣赂,超出的線程會在隊列中等待)、newCachedThreadPool
(可緩存線程池片拍,如果線程池長度超過處理需要煌集,可靈活回收空閑線程,若無可回收捌省,則新建線程)苫纤、newScheduledThreadPool
(定長線程池,支持定時及周期性任務(wù)執(zhí)行)纲缓。
原理
這個的話個人認為把整個線程池執(zhí)行的過程講清楚就可以了卷拘,當(dāng)然可以先把創(chuàng)建線程池的線程池的七個參數(shù)講一下(上面幾個常用的線程池也是通過指定默認的幾個參數(shù)來創(chuàng)建的)。
-
maximumPoolSize
最大線程數(shù)祝高。 -
corePoolSize
核心線程數(shù)栗弟。 -
keepAliveTime
線程活躍時間。 -
TimeUnit
線程活躍時間單位工闺。 -
workQueue
阻塞隊列乍赫。 -
RejectedExecutionHandler
拒絕策略。 -
ThreadFactory
創(chuàng)建線程的工廠陆蟆。
還是一樣雷厂,自己根據(jù)對線程池的理解組織一下話術(shù)。[圖片上傳中...(image-6969e6-1609767060566-50)]
9. 那線程池的拒絕策略有哪些遍搞?
當(dāng)線程池不能再創(chuàng)新的線程后(參考上圖)罗侯,線程池就會執(zhí)行拒絕策略。拒絕策略主要有一下(當(dāng)然排查自定義的策略溪猿,
如果有面試官問這個那還是問的比較深比較變態(tài)的):
-
AbrtPolicy
直接丟棄任務(wù)钩杰,拋出異常纫塌,這是默認策略。 -
CallerRunsPolicy
只用調(diào)用者所在的線程來處理任務(wù)讲弄。 -
DiscardOldestPolicy
丟棄等待隊列中最舊的任務(wù)措左,并執(zhí)行當(dāng)前任務(wù)。 -
DiscardPolicy
直接丟棄任務(wù)避除,也不拋出異常怎披。
10. 簡單講一下 wait 和 sleep 的區(qū)別?
wait | sleep | |
---|---|---|
調(diào)用時機 | 只能在同步上下文中調(diào)用瓶摆,否則拋出IllegalMonitorStateException 異常 |
不需要在同步上下文中調(diào)用 |
作用對象 | 定義在Object 類中凉逛,作用于對象本身 |
定義在Thread 中,作用于當(dāng)前線程 |
釋放資源 | 被調(diào)用時釋放鎖資源 | 被調(diào)不釋放資源群井,當(dāng)前線程阻塞 |
喚醒方式 | 調(diào)用notify 状飞、notifyAll 或者超時 |
調(diào)用interrupt 或超時 |
方法屬性 | 實例方法 | 靜態(tài)方法 |
11. volatile 關(guān)鍵字原理有了解過嗎?
首先使用volatile
聲明的變量书斜,可以確保值被更新的時候?qū)ζ渌€程立刻可見诬辈,并且可以防止指令重排序。
原理
這個問題的我會先回答上面的作用荐吉,然后在將一下MESI 協(xié)議
和 Java 的內(nèi)存模型焙糟。至于指令重排的原理我會忽略性不講。因為這一塊東西太多了样屠,扯下去的話時間會很久也比較難講清楚穿撮。
MESI 協(xié)議
多核CPU
在讀/寫取數(shù)據(jù)的過程大致是CPU
-> CPU緩存
(L1、L2痪欲、L3) -> 內(nèi)存
混巧,MESI
(Modified
、Exclusive
勤揩、Shared
咧党、Invalid
首字母縮寫) 協(xié)議就是用于保證這個過程中每個緩存中使用的共享變量的副本和內(nèi)存中是一致的。當(dāng)CPU
寫數(shù)據(jù)時陨亡,如果發(fā)現(xiàn)操作的變量是共享變量傍衡,先將變量設(shè)置為獨占(Exclusive
)狀態(tài),當(dāng)進行設(shè)值操作是把變量狀態(tài)設(shè)置為修改(Modified
)狀態(tài)负蠕,然后通過總線嗅探機制發(fā)出信號通知其他CPU
將該變量的緩存行置為無效(Invalid
)狀態(tài)蛙埂。設(shè)置完成后將變量狀態(tài)修改為共享(Shared
)。其他CPU
需要讀取這個變量時遮糖,會發(fā)現(xiàn)自己緩存中緩存該變量的緩存行是無效的(Invalid
)绣的,那么它就會從內(nèi)存重新讀取。
[圖片上傳中...(image-d0ac1a-1609767060565-49)]
volatile 原理
首先,Java 的內(nèi)存模型(JMM
)和上圖基本思想是一致的屡江。在JMM
中同樣也區(qū)分了主內(nèi)存(主存)芭概、線程副本(緩存)。當(dāng)修改volatile
變量時會強制將修改后的值刷新的主內(nèi)存中惩嘉。修改volatile
變量后會導(dǎo)致其他線程工作內(nèi)存中對應(yīng)的變量值失效(與上面是一致的)罢洲。因此,再讀取該變量值的時候就需要重新從讀取主內(nèi)存中的值(保證可見性)文黎。
12. synchronized 的作用和原理能講一講嗎惹苗?
synchronized
是 Java 中解決并發(fā)問題的一種最常用的方法,也是最簡單的一種方法耸峭。synchronized
的作用主要有三個:
- 確保線程互斥的訪問同步代碼桩蓉。
- 保證共享變量的修改能夠及時可見。
- 有效解決重排序問題劳闹。
原理
使用synchronized
之后触机,通過編譯后的字節(jié)碼會發(fā)現(xiàn)在同步的代碼塊前后會加上monitorenter
和monitorexit
字節(jié)碼指令,在同步方法中會添加ACC_SYNCHRONIZED
標(biāo)記玷或。然后在 Java 中每個對象都會有一個屬于自己的監(jiān)視器鎖(monitor
),執(zhí)行monitorenter
指令(ACC_SYNCHRONIZED
標(biāo)記執(zhí)行邏輯也是一樣的)時就是嘗試獲取 monitor
所有權(quán)的過程片任。如果對象沒有被鎖定或者已經(jīng)獲得了鎖(可重入性)偏友,鎖的計數(shù)器 +1
,此時其他競爭鎖的線程則會進入等待隊列中对供。
monitorexit
指令只能被持有 monitor
鎖的線程執(zhí)行位他。當(dāng)monitorexit
指令被執(zhí)行時,monitor
鎖的計數(shù)器就會做-1
操作产场。當(dāng)計數(shù)器值為0
時鹅髓,則釋放鎖,并且喚醒處于等待隊列中的線程再繼續(xù)競爭鎖京景。
13. synchronized 鎖優(yōu)化了解過嗎窿冯?
在 JDK1.6
版本前,synchronized
鎖是一把重量級鎖确徙。從JDK1.6
版本之后醒串,對synchronized
鎖經(jīng)過了一系列的優(yōu)化。優(yōu)化機制包括自適應(yīng)鎖
鄙皇、自旋鎖
芜赌、鎖消除
、鎖粗化
伴逸、輕量級鎖
和偏向鎖
缠沈。簡單點來說就是使用synchronized
鎖一開始會變成偏向鎖
而不是重量級鎖
,然后經(jīng)過鎖競爭會有一個從無鎖
->偏向鎖
->輕量級鎖
->重量級鎖
的鎖升級過程。
自旋鎖
自旋鎖主要發(fā)生在線程阻塞等待期間洲愤,由于大部分時候鎖被占用的時間很短颓芭,共享變量的鎖定時間也很短,所有沒有必要掛起線程禽篱,用戶態(tài)和內(nèi)核態(tài)的來回上下文切換嚴重影響性能畜伐。自旋的概念就是讓線程執(zhí)行一個忙循環(huán),可以理解為就是啥也不干躺率,防止從用戶態(tài)轉(zhuǎn)入內(nèi)核態(tài)玛界。
自適應(yīng)鎖
自適應(yīng)鎖就是自適應(yīng)的自旋鎖,自旋的時間不是固定時間悼吱,而是由前一次在同一個鎖上的自旋時間和鎖的持有者狀態(tài)來決定慎框。
鎖消除
鎖消除指的是JVM
檢測到一些同步的代碼塊,完全不存在數(shù)據(jù)競爭的場景后添,也就是不需要加鎖笨枯,就會進行鎖消除。
鎖粗化
鎖粗化指的是有很多操作都是對同一個對象進行加鎖遇西,就會把鎖的同步范圍擴展到整個操作序列之外馅精。
CAS 無鎖機制
CAS
(Compare And Swap/Set) 是一種樂觀鎖的實現(xiàn)方式,從字面意思上來說是比較并交換/賦值粱檀。CAS
機制當(dāng)中使用了內(nèi)存地址V
洲敢、舊的預(yù)期值A(chǔ)
、要修改的新值B
3 個基本操作數(shù)茄蚯,更新一個變量的時候压彭,只有當(dāng)舊的預(yù)期值A(chǔ)
和內(nèi)存地址V
當(dāng)中的實際值相同時,才會將內(nèi)存地址V
對應(yīng)的值修改為B
渗常。
偏向鎖
當(dāng)線程訪問同步塊獲取鎖時壮不,會在對象頭和棧幀中的鎖記錄里存儲偏向鎖的線程 ID,之后這個線程再次進入同步塊時都不需要CAS
來加鎖和解鎖了皱碘,偏向鎖會永遠偏向第一個獲得鎖的線程询一,如果后續(xù)沒有其他線程獲得過這個鎖,持有鎖的線程就永遠不需要進行同步癌椿,反之家凯,當(dāng)有其他線程競爭偏向鎖時,持有偏向鎖的線程就會釋放偏向鎖如失。
輕量級鎖
JVM
的對象的對象頭中包含有一些鎖的標(biāo)志位绊诲,代碼進入同步塊的時候,JVM 將會使用CAS
方式來嘗試獲取鎖褪贵,如果更新成功則會把對象頭中的狀態(tài)位標(biāo)記為輕量級鎖掂之,如果更新失敗抗俄,當(dāng)前線程就嘗試自旋來獲得鎖。
鎖升級過程
鎖升級的過程是非常復(fù)雜的世舰,好哥哥們自己總結(jié)一下語言动雹,盡量簡單一點。
14. 有用過 ThreadLocal 嗎跟压,原理有了解過嗎胰蝠?
這種問題真想回一個沒用過,不會(然后大結(jié)局)
首先ThreadLocal
是一個存儲泛型變量數(shù)據(jù)類震蒋,當(dāng)使用ThreadLocal
維護變量時茸塞,ThreadLocal
為每個使用該變量的線程提供獨立的變量副本,所以每一個線程都可以獨立地改變自己的副本查剖,而不會影響其它線程所對應(yīng)的副本钾虐。實際上是一種數(shù)據(jù)隔離(犧牲內(nèi)存空間)的方式來達到線程安全的目的。
原理
這個的話比較簡單笋庄,實際上ThreadLocal
內(nèi)部維護了一個Map
變量(ThreadLocalMap
,并不是HashMap
),當(dāng)調(diào)用set
方法時效扫,會將當(dāng)前線程作為key
(實際上這里并不那么準(zhǔn)確,應(yīng)該是將ThreadLocal
對象本身作為key
直砂,ThreadLocal
對象中包含了當(dāng)前線程)菌仁,參數(shù)作為value
,然后設(shè)值給ThreadLocal
的靜態(tài)內(nèi)部類Map
(ThreadLocalMap
)中。取值的話就是講當(dāng)前線程作為key
然后從ThreadLocalMap
獲取静暂。
最后
本期的內(nèi)容就到這啦济丘,有需要補充的地方 歡迎大家在評論區(qū)留言指出!
由于細節(jié)內(nèi)容實在太多啦籍嘹,所以只把部分知識點截圖出來粗略的介紹,每個小節(jié)點里面都有更細化的內(nèi)容弯院! 全套2021大廠面試合集給大家整理出來了辱士,需要的請扣一。
獲取資料方式:關(guān)注+轉(zhuǎn)發(fā)听绳,私信“書籍”免費領(lǐng)取
喜歡的話可以給小編三連一下哦