面試崗位
Java開(kāi)發(fā)工程師(校招)
一面(2021.0914)
快手不知道為啥沒(méi)有統(tǒng)一筆試,直接發(fā)起的面試。問(wèn)的八股文很全面,算法題出的也讓我感覺(jué)很好。
基礎(chǔ)題
-
Java基礎(chǔ)數(shù)據(jù)類(lèi)型有哪些,占多少位?
答:共有8種,分別是:- boolean 布爾:1位
- byte 字節(jié):8位
- short 短整數(shù):16位,2字節(jié)
- char 字符:16位,2字節(jié)
- int 整型:32位,4字節(jié)
- float 單精度:32位,4字節(jié)
- long 長(zhǎng)整數(shù):64位,8字節(jié)
- double 雙精度:64位,8字節(jié)
double運(yùn)算時(shí)需要注意什么?為什么
答:精度丟失,比方說(shuō)當(dāng)比較時(shí),可以用差的絕對(duì)值小于一個(gè)極小值來(lái)說(shuō)明兩個(gè)變量相等。原因是做計(jì)算時(shí)十進(jìn)制會(huì)先轉(zhuǎn)換成二進(jìn)制,但是有的十進(jìn)制轉(zhuǎn)換成二進(jìn)制是無(wú)限小數(shù),必然會(huì)有精度舍棄。
你知道
float是怎么存儲(chǔ)小數(shù)的嗎?
答:以6.5432為例,整數(shù)部分用模二取余法:
6 / 2 = 3 …… 0
3 / 2 = 1 …… 1
1 / 2 = 0 …… 1
得到6的二進(jìn)制表示110。小數(shù)部分用乘二取整法:
0.5432 * 2 = 1 + 0.0864
0.0864 * 2 = 0 + 0.1728
0.1728 * 2 = 0 + 0.3456
0.3456 * 2 = 0 + 0.6912
0.6912 * 2 = 1 + 0.3824
0.3824 * 2 = 0 + 0.7648
0.7648 * 2 = 1 + 0.5296
可以得到
組合得到二進(jìn)制為
左移兩位,使小數(shù)點(diǎn)前只有一位1,即,則存儲(chǔ)時(shí)指數(shù)為
則最終6.5432存儲(chǔ)結(jié)果為0(符號(hào)位) 10000001(指數(shù)) 1010001 01000000 00000000-
volatile關(guān)鍵字作用
參考資料《【Java線程】volatile的適用場(chǎng)景》
答:volatile具有可見(jiàn)性和有序性,但是沒(méi)有原子性。他可以使得它修飾的變量,每次修改時(shí)先同步到主存,每次使用前都從主存直接獲取。這個(gè)操作保證了它的可見(jiàn)性。同時(shí)他的引入將避免指令重排的現(xiàn)象,保證了有序性。
適用場(chǎng)景:- 用來(lái)標(biāo)記某一個(gè)一次性狀態(tài)已發(fā)生
- 單例模式取消指令重排
- 獨(dú)立觀察
- volatile bean
- 開(kāi)銷(xiāo)較低的讀-寫(xiě)鎖策略
泛型了解嗎,他有什么用處?
答:在集合類(lèi)實(shí)現(xiàn)的時(shí)候,如果想實(shí)現(xiàn)一個(gè)通用的可以處理不同類(lèi)型的類(lèi),需要使用Object作為屬性和方法參數(shù)。然后具體操作時(shí)再去做強(qiáng)制轉(zhuǎn)換,一來(lái)是使用時(shí)不方便,二來(lái)是只有運(yùn)行時(shí)才能知道傳入集合的值類(lèi)型是否正確。因此在JDK 1.5之后,引入了泛型的概念。
泛型的本質(zhì)是參數(shù)化類(lèi)型,把要操作的數(shù)據(jù)類(lèi)型當(dāng)做一個(gè)參數(shù)傳入,這樣在編譯時(shí)就可以對(duì)存放的內(nèi)容的類(lèi)型做安全判斷。private關(guān)鍵字作用
答:加了之后的變量和方法,只有在類(lèi)內(nèi)部才能使用,外部不能直接調(diào)用,子類(lèi)也不會(huì)繼承。想調(diào)用的話可以配套寫(xiě)一對(duì)get,set。反射了解嗎?他是在編譯階段還是運(yùn)行階段。
答:運(yùn)行階段,反射就是在運(yùn)行時(shí)才知道要操作的類(lèi)是什么,并且可以在運(yùn)行時(shí)獲取類(lèi)的完整構(gòu)造,并調(diào)用對(duì)應(yīng)的方法。
比如正常的調(diào)用應(yīng)該是先new一個(gè)對(duì)象,然后使用對(duì)象的一些屬性和方法做一些操作。但是反射中可能是先獲得類(lèi),然后通過(guò)getConstructor方法和newInstance方法來(lái)創(chuàng)建一個(gè)對(duì)象以供使用。反射可以獲得 private 屬性嗎?他們倆是不是沖突的。
答:確實(shí)可以用getDeclaredField獲得包含私有變量在內(nèi)的所有成員變量。
private最主要的目的實(shí)際上是為了實(shí)現(xiàn)封裝性,為類(lèi)內(nèi)的public方法提供一些支持,同時(shí)不希望被外部直接調(diào)用,或是繼承。
反射如果單獨(dú)的使用某個(gè)private方法或?qū)傩?,大概率是沒(méi)有什么作用的,因?yàn)楸旧?private變量可能就已經(jīng)配套有get和set;而單獨(dú)調(diào)用private方法可能導(dǎo)致程序異常;同時(shí)原本繼承該類(lèi)的子類(lèi)依然不能繼承得到private成員變量。因此我們可以認(rèn)為反射對(duì)我們希望實(shí)現(xiàn)的封裝性(及一個(gè)父類(lèi)的一些內(nèi)部使用方法不影響外部環(huán)境),基本沒(méi)有影響。線程池了解嗎?
答:由于每次創(chuàng)建、銷(xiāo)毀、或是管理線程都有一定的資源消耗,因此使用線程池,對(duì)線程做統(tǒng)一管理,當(dāng)程序需要線程時(shí),只需要向線程池申請(qǐng),如果某個(gè)線程異常掛掉,那線程池還可以及時(shí)補(bǔ)充。ThreadPoolExecutor知道哪些?
答:SingleThreadExecutor:返回一個(gè)只有一個(gè)線程的線程池,多余的任務(wù)會(huì)被放到消息隊(duì)列里慢慢執(zhí)行。
FixedThreadPoolExecutor:返回一個(gè)固定線程數(shù)的線程池,任務(wù)來(lái)時(shí),如果有空閑線程就立刻執(zhí)行,否則同樣存放在消息隊(duì)列中;如果線程掛掉,及時(shí)重新創(chuàng)建線程。
CachedThreadPoolExecutor:可以動(dòng)態(tài)調(diào)整線程池線程數(shù),只要JVM能支持,可以無(wú)限開(kāi)線程,同時(shí)如果沒(méi)有任務(wù)需要也會(huì)自動(dòng)回收。
ScheduledThreadPoolExecutor:周期性執(zhí)行任務(wù)ThreadPoolExecutor有哪些參數(shù)?
答:(當(dāng)時(shí)并沒(méi)有答出來(lái))
corePoolSize:核心線程數(shù),及最小同時(shí)運(yùn)行線程數(shù)量。
workQueue:當(dāng)新任務(wù)來(lái)時(shí)先判斷線程數(shù)是否到核心線程數(shù),若達(dá)到,則放進(jìn)任務(wù)隊(duì)列中。
maximumPoolSize:當(dāng)隊(duì)列達(dá)到容量上限時(shí),將同時(shí)運(yùn)行線程數(shù)變?yōu)樽畲缶€程數(shù)。
keepAliveTime:若當(dāng)前實(shí)際任務(wù)數(shù)不超過(guò)核心線程數(shù),但運(yùn)行中的線程數(shù)超過(guò)了時(shí),等待keepAliveTime時(shí)間后,才進(jìn)行銷(xiāo)毀。
unit:keepAliveTime參數(shù)的實(shí)踐單位
threadFactory:創(chuàng)建新線程的工廠類(lèi)
handler:飽和策略:線程容量和隊(duì)列容量同時(shí)飽和時(shí)執(zhí)行策略。包括①拒絕新任務(wù) ②增加隊(duì)列容量 ③ 直接丟棄 ④ 丟棄最早任務(wù)。默認(rèn)使用第一種。GC了解嗎,怎么判斷某個(gè)對(duì)象應(yīng)該被回收?
答:兩種方法:引用計(jì)數(shù)法和可達(dá)性分析。
引用計(jì)數(shù):對(duì)于某個(gè)對(duì)象,每有一個(gè)地方引用他,計(jì)數(shù)器加 1,引用時(shí)效,計(jì)數(shù)器減 1。當(dāng)計(jì)數(shù)值為 0 時(shí)說(shuō)明對(duì)象應(yīng)該被回收。
可達(dá)性分析:從GC root出發(fā),所有不可達(dá)的對(duì)象會(huì)被標(biāo)記。GC root有以下幾種: ①虛擬機(jī)棧:棧幀中的本地變量表引用的對(duì)象 ②native方法引用的對(duì)象 ③方法去中的靜態(tài)變量和常量引用的對(duì)象-
G1怎么做的標(biāo)記和清除,過(guò)程是什么?
答:G1會(huì)將內(nèi)存劃分成若干個(gè)小塊,且標(biāo)記為老年代、Eden、Survivor,然后執(zhí)行Young GC,如果對(duì)象存活就會(huì)被轉(zhuǎn)移到Survivor上,如果存活時(shí)間多于閾值,就會(huì)晉升老年代。而G1的老年代垃圾回收,就或用到標(biāo)記清理過(guò)程:- 初始標(biāo)記:
Stop the World,標(biāo)記可能有引用指向老年代對(duì)象的Survivor區(qū),此操作與一次Young GC同時(shí)。 - 掃描根區(qū)域:掃描
Survivor區(qū)中引用到老年代的引用。 - 并發(fā)標(biāo)記:在堆上找活著的對(duì)象,并標(biāo)記
- 再次標(biāo)記:
Stop the World,完成堆內(nèi)存中存活對(duì)象標(biāo)記,使用SATB算法 - 清理:
Stop the World,統(tǒng)計(jì)+擦寫(xiě)+重置空heap區(qū) - 拷貝:
Stop the World,轉(zhuǎn)移或拷貝存活對(duì)象到未使用的heap區(qū)
- 初始標(biāo)記:
三次握手四次揮手
答:握手時(shí):甲說(shuō)嘿我要和你聊天了,乙回復(fù)我知道了,甲回復(fù)乙我知道你知道了。此時(shí)雙方確認(rèn)對(duì)方可以接收消息,因此開(kāi)始建立連接發(fā)送消息。
揮手時(shí):甲說(shuō)我說(shuō)完了,乙說(shuō)好的我知道了,但此時(shí)乙可能還有消息沒(méi)有發(fā)送完畢。等了一會(huì)之后乙也發(fā)送完畢了,通知甲自己要中斷連接了,甲收到之后回信知道了,并中斷連接,乙收到后也中斷連接。
在兩個(gè)過(guò)程中,都是通過(guò)比方說(shuō)一個(gè)發(fā)送信號(hào)x,另一個(gè)發(fā)送x+1這樣的形式,驗(yàn)證對(duì)方確實(shí)收到自己發(fā)出的信號(hào)。TCP是什么?
答:TCP是面向連接的,全雙工的,可以提供可靠地連接服務(wù)。連接時(shí)使用三次握手,斷連時(shí)使用四次揮手。TCP可以通過(guò)確認(rèn)、重傳、窗口、擁塞控制等機(jī)制保證數(shù)據(jù)正確性,但效率較低,且開(kāi)銷(xiāo)比UDP要大。MYSQL 索引的數(shù)據(jù)結(jié)構(gòu)什么樣?
答:B+樹(shù),B樹(shù)是在中間節(jié)點(diǎn)及葉子節(jié)點(diǎn)都可以存放信息的一棵樹(shù),而B+樹(shù)則是所有信息均被存放在葉子節(jié)點(diǎn),且在所有葉子節(jié)點(diǎn)間,從左至右存在一條鏈表,這樣的好處是比方說(shuō)我們想查一段區(qū)間中的索引值,那我們只需要找到兩端,然后通過(guò)那條鏈表就可以很快的獲取到我們想要的所有數(shù)據(jù)信息。而且B+樹(shù)查詢(xún)更穩(wěn)定。
算法題
很喜歡這個(gè)算法題,不難但是考察有沒(méi)有刷過(guò)題非常好。
- 給一個(gè)鏈表
1,2,3,4,5,...,n,把他變成1,n,2,n-1,...這種形式的鏈表返回
答:先快慢指針找到中間點(diǎn),然后把右半段鏈表逆序,然后雙指針兩頭向中間靠近。