最近找了一段時(shí)間工作,從最開(kāi)始的無(wú)從下手到現(xiàn)在的漸入佳境熬苍,這個(gè)過(guò)程免不了對(duì)面試中遇到的問(wèn)題進(jìn)行一個(gè)總結(jié)稍走,其實(shí)自己以前懶得總結(jié),但現(xiàn)在記性越來(lái)越不好柴底,所以還是寫下來(lái)比較靠譜婿脸。
進(jìn)入正題:
最近半個(gè)月面試了大概有十家公司左右,面試的職位一般都是Android柄驻,如架構(gòu)師狐树,應(yīng)用開(kāi)發(fā)等等職位,由于我的職位定位在了高級(jí)職位鸿脓,所以一般的面試題都是特別基礎(chǔ)和深入抑钟,Android系統(tǒng)的知識(shí)問(wèn)的倒是不太多,現(xiàn)在就分java基礎(chǔ)野哭, java 內(nèi)存模型和Android相關(guān)內(nèi)容做一下總結(jié)在塔,也對(duì)這篇文章的讀者有一個(gè)方向性和針對(duì)性的準(zhǔn)備,以便大家能夠拿出最好的表現(xiàn)拨黔,得到自己滿意的offer
java基礎(chǔ) 容器相關(guān):
在java開(kāi)發(fā)中蛔溃,容器使用的頻率相當(dāng)高,最常用的無(wú)外乎幾個(gè) ArrayList, LinkedList, HashMap, LinkedHashMap,還有相對(duì)應(yīng)的線程安全的版本蓉驹。
常見(jiàn)的問(wèn)題就是ArrayList, LinkedList的效率對(duì)比,在這里我提供一個(gè)我自己認(rèn)為的“標(biāo)準(zhǔn)答案”揪利,或者說(shuō)面試時(shí)候的回答:對(duì)于任何一個(gè)容器來(lái)說(shuō)态兴,對(duì)其的操作只有三個(gè),插入疟位,訪問(wèn)瞻润,刪除。從插入角度來(lái)說(shuō),ArrayList是基于數(shù)組實(shí)現(xiàn)的绍撞,所以對(duì)于插入操作正勒,是有擴(kuò)容方面的效率損耗,而LinkedList是基于雙向鏈表結(jié)構(gòu)的傻铣,所以插入操作速度是優(yōu)于ArrayList的章贞;對(duì)于ArrayList的訪問(wèn)其實(shí)是相當(dāng)高效的,因?yàn)锳rrayList的實(shí)現(xiàn)是基于數(shù)組的非洲,所以隨機(jī)訪問(wèn)直接取出下標(biāo)對(duì)應(yīng)的元素即可鸭限,而LInkedList需要用二分法去查找這個(gè)元素,找到了才會(huì)給返回(具體請(qǐng)閱讀源碼)两踏;對(duì)于刪除操作败京,由于ArrayList刪除一個(gè)對(duì)象之后重新進(jìn)行一次數(shù)組的拷貝,所以效率相對(duì)低一些梦染∩穆螅總結(jié)一下,ArrayList的隨機(jī)訪問(wèn)效率很高帕识,但插入和刪除操作的效率很低泛粹,LinkedList與它正好相反,隨機(jī)反問(wèn)效率很低渡冻,插入和刪除操作很高效戚扳。如果需要真正理解他們的做法和區(qū)別,閱讀源碼才是王道~
HashMap:這個(gè)東西真的需要好好理解和深入分析的族吻,我不做源碼分析帽借,因?yàn)樽约阂呀?jīng)看過(guò)了,對(duì)于個(gè)別細(xì)節(jié)大家還是自己看超歌。
說(shuō)到HashMap砍艾,就必須了解一下hash表,這個(gè)才是HashMap的核心巍举,Hash表是一種特殊的數(shù)據(jù)結(jié)構(gòu)脆荷,它同數(shù)組、鏈表以及二叉排序樹(shù)等相比較有很明顯的區(qū)別懊悯,它能夠快速定位到想要查找的記錄蜓谋,而不是與表中存在的記錄的關(guān)鍵字進(jìn)行比較來(lái)進(jìn)行查找。這個(gè)源于Hash表設(shè)計(jì)的特殊性炭分,它采用了函數(shù)映射的思想將記錄的存儲(chǔ)位置與記錄的關(guān)鍵字關(guān)聯(lián)起來(lái)桃焕,從而能夠很快速地進(jìn)行查找。其實(shí)HashMap就是通過(guò)對(duì)key對(duì)象進(jìn)行hash運(yùn)算捧毛,得到一個(gè)對(duì)象的存儲(chǔ)地址观堂,然后將key,value對(duì)對(duì)象保存到相應(yīng)地址的让网。說(shuō)到這里,就不得不面對(duì)一個(gè)問(wèn)題师痕,如果不同的key計(jì)算出的hash相同怎么辦溃睹?這就是著名的hash沖突,也叫hash碰撞胰坟。在說(shuō)這個(gè)問(wèn)題之前因篇,我們其實(shí)有必要深入了解一下HashMap里面到底存的是個(gè)什么東東?key和value分別保存還是保存到一起腕铸,如果保存到一起惜犀,那么數(shù)據(jù)結(jié)構(gòu)或者說(shuō)對(duì)象是如何構(gòu)建的?其實(shí)答案都在Entry這個(gè)類里狠裹,其實(shí)HashMap最終保存的對(duì)象是Entry<K,V>虽界,看這個(gè)對(duì)象的封裝,我們發(fā)現(xiàn)了next這個(gè)變量涛菠,很明顯莉御,單向鏈表。也就是說(shuō)俗冻,為了解決hash沖突礁叔,HashMap在保存對(duì)象的時(shí)候使用了對(duì)于相同存儲(chǔ)地址的對(duì)象,使用鏈表來(lái)維護(hù)的策略迄薄。那么對(duì)于新添加的key value對(duì)象(Entry)來(lái)說(shuō)琅关,如果key的hash值已經(jīng)存在于HashMap中的話,會(huì)去接著判斷目前要添加的這個(gè)Entry對(duì)象與之前已經(jīng)保存的對(duì)象是否是同一個(gè)對(duì)象讥蔽,這里判斷同一個(gè)對(duì)象的做法是==與equals必須滿足其中一個(gè)涣易,到這里也就衍生出來(lái)了另外一個(gè)問(wèn)題,為什么保存到HashMap中的key冶伞,value對(duì)象必須都要同時(shí)override hashCode()和equals()這兩個(gè)方法新症,或者說(shuō)復(fù)寫這兩個(gè)方法的是干什么的。那么如果不去override這兩個(gè)方法會(huì)產(chǎn)生什么問(wèn)題响禽?那就是在HashMap中會(huì)保存很多相同的Entry徒爹,造成數(shù)據(jù)的不準(zhǔn)確。當(dāng)然芋类,HashMap也會(huì)有擴(kuò)容的問(wèn)題隆嗅,因?yàn)樗举|(zhì)上也是一個(gè)保存了Entry對(duì)象的數(shù)組,只不過(guò)保存的下標(biāo)是經(jīng)過(guò)hash運(yùn)算得出的侯繁,具體還是看源碼吧胖喳。
說(shuō)完了HashMap,必須提一下LinkedHashMap巫击,它是怎么回事兒禀晓,有哪些應(yīng)用場(chǎng)景?跟HashMap的區(qū)別是什么坝锰?
先說(shuō)應(yīng)用場(chǎng)景粹懒,在你需要保存插入對(duì)象的順序的時(shí)候,需要使用LinkedHashMap顷级。
它跟HashMap最大的區(qū)別在于兩點(diǎn)凫乖,一,重新定義了一個(gè)雙向鏈表的Entry弓颈,二帽芽,在計(jì)算數(shù)組空間的時(shí)候,override了transfer方法翔冀,將數(shù)組中的元素變成了雙向鏈表导街。它是繼承自HashMap的,大部分行為是相似的纤子。
線程安全版本的容器搬瑰,我目前還沒(méi)有去做源碼的分析,但肯定不是簡(jiǎn)單的對(duì)對(duì)象上鎖這么簡(jiǎn)單控硼,之后再分析
JAVA內(nèi)存模型:
這個(gè)被問(wèn)到的次數(shù)挺多的泽论,大多數(shù)都是用具體的例子去問(wèn) 比如 : String str = new String("abc"); 問(wèn) str 保存在哪,new String這個(gè)操作是做什么的卡乾,它跟str什么關(guān)系翼悴,"abc"保存在哪。
我不去直接回答這個(gè)問(wèn)題幔妨,有一幅圖能夠特別直觀準(zhǔn)確的描述java內(nèi)存模型
JAVA 內(nèi)存模型JMM其實(shí)主要分為五個(gè)部分鹦赎,線程相關(guān)的 pc寄存器,負(fù)責(zé)記錄執(zhí)行棧的地址信息陶冷;線程棧钙姊;本地方法棧;進(jìn)程相關(guān):堆(heap),方法執(zhí)行區(qū)埂伦,這里也包括常量區(qū)煞额。
那么回到剛才的那個(gè)語(yǔ)句,我們來(lái)分析一下沾谜。
String str定義了一個(gè)String對(duì)象的引用膊毁,這個(gè)一定是線程相關(guān)的,也就是說(shuō)是在線程中創(chuàng)建的引用基跑,所以str的存儲(chǔ)位置在線程的棧上婚温,new String在堆(heap)上創(chuàng)建的對(duì)象,str是對(duì)其的引用媳否,“abc"就在常量池中栅螟。
內(nèi)存泄露問(wèn)題:長(zhǎng)生命周期對(duì)象擁有短生命周期對(duì)象的引用荆秦,導(dǎo)致短生命周期對(duì)象的引用得不到釋放引起的問(wèn)題叫做內(nèi)存泄露。常見(jiàn)的就是在activity(短生命周期)中啟動(dòng)一個(gè)線程(長(zhǎng)生命周期)力图,如果線程沒(méi)有運(yùn)行結(jié)束步绸,并且activity被destroy掉,由于線程持有activity的引用吃媒,導(dǎo)致activity會(huì)跟隨線程的生命周期瓤介,從而不釋放,造成泄露赘那。這種問(wèn)題的一般解決辦法就是在activity內(nèi)部定義一個(gè)static類刑桑,這樣,這個(gè)static類就不會(huì)持有activity的引用了募舟,網(wǎng)上資料很多祠斧,建議查一下
類加載相關(guān)問(wèn)題:
在網(wǎng)易,面試官問(wèn)了我一個(gè)關(guān)于不同classloader加載同一個(gè)類的時(shí)候拱礁,這個(gè)類對(duì)象在jvm中是同一個(gè)class對(duì)象嗎梁肿?
我只知道類加載的方式是雙親委托模型,但是對(duì)于這個(gè)問(wèn)題卻沒(méi)思考過(guò)觅彰,面試官也應(yīng)該沒(méi)有深入的研究過(guò)吩蔑,他只是說(shuō)應(yīng)該是不同的對(duì)象,但是具體為什么他也咬不準(zhǔn)了填抬。但是遇到了這個(gè)問(wèn)題回來(lái)就要弄清楚烛芬,經(jīng)過(guò)查資料了解到,其實(shí)這個(gè)問(wèn)題是一個(gè)jvm的問(wèn)題飒责,涉及到j(luò)vm相關(guān)安全方面的赘娄,至于答案,肯定是不同的class對(duì)象宏蛉。為什么遣臼?因?yàn)樵陬惣虞d的過(guò)程中,會(huì)給這個(gè)class加上這個(gè)加載器相關(guān)的標(biāo)簽拾并,防止不明來(lái)源的類加載器進(jìn)行攻擊揍堰,這方面我研究的不多,之后好好研究一番吧嗅义,反正目前只需要了解到不同的classloader會(huì)給自己加載的類添加上自己的標(biāo)簽屏歹,這個(gè)是jvm的硬性規(guī)定就可以了。
Android相關(guān)內(nèi)容:
本以為這部分應(yīng)該是重點(diǎn)考察的部分之碗,沒(méi)想到其實(shí)上面的兩部分都被問(wèn)了很多次蝙眶,幾乎每次面試都會(huì)涉及,這部分先挑重點(diǎn)的說(shuō):序列化問(wèn)題褪那,serializable和parcelable幽纷。
parcelable是Android自己定義的一個(gè)序列化方式式塌,相比于serializable它的效率更高,原因在于它直接對(duì)內(nèi)存進(jìn)行操作友浸,而serializable在序列化過(guò)程中會(huì)通過(guò)反射的方式創(chuàng)建臨時(shí)對(duì)象珊搀,所以相對(duì)來(lái)說(shuō)效率低一些。其實(shí)很多同學(xué)都知道這個(gè)點(diǎn)尾菇,但是我當(dāng)時(shí)的面試官問(wèn)了我一個(gè)問(wèn)題,它倆其實(shí)還有一個(gè)區(qū)別囚枪,我沒(méi)答上來(lái)派诬,或者說(shuō)沒(méi)往那個(gè)方向想吧,答案是serializable序列化之后的流數(shù)據(jù)是可以用于網(wǎng)絡(luò)傳輸?shù)牧凑樱鴓arcelable只是用于在Android內(nèi)部進(jìn)行IPC使用的默赂,比如intent就是parcelable。
再就是relativeLayout和LinearLayout的效率問(wèn)題括勺,本質(zhì)上是measure次數(shù)的問(wèn)題缆八,relativeLayout在measure的時(shí)候要分別計(jì)算水平方向和垂直方向的空間,而LinearLayout在計(jì)算之前已經(jīng)指定了水平或者垂直方向疾捍,所以計(jì)算量少了一半奈辰。
還有view的事件派發(fā)問(wèn)題,view的繪制流程 onMeasure乱豆, onLayout奖恰, onDraw等等吧
設(shè)計(jì)模式相關(guān):懶漢單例(大多數(shù)都要求手寫了)。尤其記得一個(gè)阿里的面試官大牛問(wèn)我一個(gè)設(shè)計(jì)模式相關(guān)的問(wèn)題宛裕,大家都知道Activity繼承自ContextThemeWrapper, ContextThemeWrapper繼承自ContextWrapper瑟啃,ContextWrapper繼承自Context,而service又繼承自Context,問(wèn)我這個(gè)是什么設(shè)計(jì)模式揩尸,幸虧我之前復(fù)習(xí)了一下設(shè)計(jì)模式相關(guān)的內(nèi)容蛹屿,答上來(lái)了,這個(gè)問(wèn)題留給大家思考吧~
還有程序設(shè)計(jì)框架類的東西 MVC,MVP,MVVM之間的區(qū)別岩榆,這個(gè)大家自己查資料吧错负,重點(diǎn)說(shuō)一下MVVM,這個(gè)東西主要是因?yàn)橛辛藬?shù)據(jù)綁定才出現(xiàn)的勇边,而且15年Google io大會(huì)也提出了他們自己的數(shù)據(jù)綁定框架湿颅,有興趣可以了解一下,MVP和MVVM各有特點(diǎn)粥诫,具體選擇什么看當(dāng)時(shí)的需求油航。
目前就回憶起來(lái)這么多東西,哪天再想起來(lái)了再添加和修改~