面試總是碰到各種難題,相信很多碼農(nóng)跟我一樣總是重復(fù)于業(yè)務(wù)層的開發(fā),對底層的了解完全是因?yàn)槊嬖囆枰?,這里將部分面試題進(jìn)行整理,可以方便學(xué)習(xí),希望也能方便工友們一起學(xué)習(xí)。
基礎(chǔ)部分:
1、內(nèi)存泄漏:
? ??????內(nèi)存泄露的根本原因:長生命周期的對象持有短生命周期的對象。短周期對象就無法及時釋放。
1.靜態(tài)集合類引起內(nèi)存泄露
主要是hashmap,Vector等,如果是靜態(tài)集合 這些集合沒有及時setnull的話,就會一直持有這些對象。
2.remove 方法無法刪除set集 Objects.hash(firstName, lastName);
經(jīng)過測試,hashcode修改后,就沒有辦法remove了。
3.observer 我們在使用監(jiān)聽器的時候,往往是addxxxlistener,但是當(dāng)我們不需要的時候,忘記removexxxlistener,就容易內(nèi)存leak。
廣播沒有unregisterrecevier
4.各種數(shù)據(jù)鏈接沒有關(guān)閉,數(shù)據(jù)庫contentprovider,io,sokect等。cursor
5.內(nèi)部類:
java中的內(nèi)部類(匿名內(nèi)部類),會持有宿主類的強(qiáng)引用this。
所以如果是new Thread這種,后臺線程的操作,當(dāng)線程沒有執(zhí)行結(jié)束時,activity不會被回收。
Context的引用,當(dāng)TextView 等等都會持有上下文的引用。如果有static drawable,就會導(dǎo)致該內(nèi)存無法釋放。
6.單例
單例 是一個全局的靜態(tài)對象,當(dāng)持有某個復(fù)制的類A是,A無法被釋放,內(nèi)存leak。
2、內(nèi)存溢出
當(dāng)程序需要申請一段“大”內(nèi)存,但是虛擬機(jī)沒有辦法及時的給到,即使做了GC操作以后
這就會拋出 OutOfMemoryException 也就是OOM。避免如下:
1、減少內(nèi)存對象的占用,比如
ArrayMap/SparseArray代替hashmap
避免在android里面使用Enum
減少bitmap的內(nèi)存占用
inSampleSize:縮放比例,在把圖片載入內(nèi)存之前,我們需要先計(jì)算出一個合適的縮放比例,避免不必要的大圖載入。
decode format:解碼格式,選擇ARGB_8888/RBG_565/ARGB_4444/ALPHA_8,存在很大差異。
減少資源圖片的大小,過大的圖片可以考慮分段加載
2、內(nèi)存對象的重復(fù)利用
大多數(shù)對象的復(fù)用,都是利用對象池的技術(shù)。
listview/gridview/recycleview contentview的復(fù)用
inBitmap 屬性對于內(nèi)存對象的復(fù)用ARGB_8888/RBG_565/ARGB_4444/ALPHA_8
這個方法在某些條件下非常有用,比如要加載上千張圖片的時候。
避免在ondraw方法里面 new對象
StringBuilder 代替+
3、ANR 是什么?怎樣避免和解決 ANR
ANR->Application Not Responding
也就是在規(guī)定的時間內(nèi),沒有響應(yīng)。
三種類型:
1). KeyDispatchTimeout(5 seconds) —主要類型按鍵或觸摸事件在特定時間內(nèi)無響應(yīng)
2). BroadcastTimeout(10 seconds) —BroadcastReceiver在特定時間內(nèi)無法處理完成
3). ServiceTimeout(20 seconds) —小概率類型 Service在特定的時間內(nèi)無法處理完成
為什么會超時:事件沒有機(jī)會處理 & 事件處理超時
怎么避免ANR
ANR的關(guān)鍵
是處理超時,所以應(yīng)該避免在UI線程,BroadcastReceiver 還有service主線程中,處理復(fù)雜的邏輯和計(jì)算
而交給work thread操作。
1)避免在activity里面做耗時操作,oncreate & onresume
2)避免在onReceiver里面做過多操作
3)避免在Intent Receiver里啟動一個Activity,因?yàn)樗鼤?chuàng)建一個新的畫面,并從當(dāng)前用戶正在運(yùn)行的程序上搶奪焦點(diǎn)。
4)盡量使用handler來處理UI thread & workthread的交互。
如何解決ANR
首先定位ANR發(fā)生的log:
04-0113:12:11.572I/InputDispatcher(220):Applicationisnot responding:Window{2b263310com.android.email/com.android.email.activity.SplitScreenActivitypaused=false}.5009.8ms sinceevent,5009.5ms since waitstartedCPUusagefrom4361ms to699ms ago----CPU在ANR發(fā)生前的使用情況04-0113:12:15.872E/ActivityManager(220):100%TOTAL:4.8%user+7.6%kernel+87%iowait04-0113:12:15.872E/ActivityManager(220):CPUusagefrom3697ms to4223ms later:--ANR后CPU的使用量
從log可以看出,cpu在做大量的io操作。
所以可以查看io操作的地方。
當(dāng)然,也有可能cpu占用不高,那就是 主線程被block住了。
4、.Framework 工作方式及原理,Activity 是如何生成一個 view 的,機(jī)制是什么
Framework是android 系統(tǒng)對 linux kernel,lib庫等封裝,提供WMS,AMS,bind機(jī)制,handler-message機(jī)制等方式,供app使用。
簡單來說framework就是提供app生存的環(huán)境。
1)Activity在attch方法的時候,會創(chuàng)建一個phonewindow(window的子類)
2)onCreate中的setContentView方法,會創(chuàng)建DecorView
3)DecorView 的addview方法,會把layout中的布局加載進(jìn)來。
5、橫豎屏切換問題
關(guān)于Android橫豎屏切換Activity是否會銷毀重建,這個由Activity的configChanges屬性控制。當(dāng)一個配置改變時Activity默認(rèn)會銷毀重建,但是如果這個屬性聲明了此項(xiàng)配置后,Activit就不會銷毀重建,而是回調(diào)Activity的onConfigurationChanged方法。
橫豎屏切換 - Activity銷毀重建
對于android3.2(API13)及以后的系統(tǒng),以下任意一種配置,橫豎屏切換Activity的生命周期都會重新執(zhí)行一次:
1.不配置configChanges屬性
2.設(shè)置android:configChanges="orientation"
3.設(shè)置android:configChanges="orientation|keyboardHidden"(3.2系統(tǒng)之前的系統(tǒng)不會執(zhí)行生命周期方法了)
配置android:configChanges="orientation|keyboardHidden|screenSize"可以控制Activity在橫豎屏切換時不銷毀重建
6、websocket 和socket區(qū)別
Socket是傳輸控制層協(xié)議,WebSocket是應(yīng)用層協(xié)議。Socket其實(shí)并不是一個協(xié)議,而是為了方便使用TCP或UDP而抽象出來的一層,是位于應(yīng)用層和傳輸控制層之間的一組接口。當(dāng)兩臺主機(jī)通信時,必須通過Socket連接,Socket則利用TCP/IP協(xié)議建立TCP連接。TCP連接則更依靠于底層的IP協(xié)議,IP協(xié)議的連接則依賴于鏈路層等更低層次。WebSocket同HTTP一樣是應(yīng)用層的協(xié)議,但是它是一種雙向通信協(xié)議,是建立在TCP之上的。websocket通信過程如下:
1. 瀏覽器、服務(wù)器建立TCP連接,三次握手。這是通信的基礎(chǔ),傳輸控制層,若失敗后續(xù)都不執(zhí)行。
2. TCP連接成功后,瀏覽器通過HTTP協(xié)議向服務(wù)器傳送WebSocket支持的版本號等信息。(開始前的HTTP握手)
3. 服務(wù)器收到客戶端的握手請求后,同樣采用HTTP協(xié)議回饋數(shù)據(jù)。
4. 當(dāng)收到了連接成功的消息后,通過TCP通道進(jìn)行傳輸通信。
底層原理:
1、虛擬機(jī)內(nèi)存模型

JVM(Java Virtual Machine)的內(nèi)存空間分為5部分:
(1)PC Register 程序計(jì)數(shù)器
(2)JVM Stack java虛擬機(jī)棧
(3)Native Method Stack 本地方法棧
(4)Head 堆
(5)Method Area 方法區(qū)
一、PC Register 程序計(jì)數(shù)器
1、定義
程序計(jì)數(shù)器是一塊較小的內(nèi)存空間,可以把它看作當(dāng)前線程正在執(zhí)行的字節(jié)碼的信號指示器。程序計(jì)數(shù)器里面記錄的是當(dāng)前線程正在執(zhí)行的那一條字節(jié)碼指令的地址。
注:如果當(dāng)前線程正在執(zhí)行的是一個本地方法,那么此時程序計(jì)數(shù)器為空。
2、作用
字節(jié)碼解釋器通過改變程序計(jì)數(shù)器來依次讀取指令,從而實(shí)現(xiàn)代碼的流程控制,如:順序執(zhí)行、選擇、循環(huán)、異常處理。
在多線程的情況下,程序計(jì)數(shù)器用于記錄當(dāng)前線程執(zhí)行的位置,從而當(dāng)線程被切換回來的時候能夠知道該線程上次運(yùn)行到哪兒了。
3、特點(diǎn)
是內(nèi)存較小的一塊空間。
線程私有。每一個線程都有一個程序計(jì)數(shù)器。
是唯一一個不會出現(xiàn)OutOfMemoryError的內(nèi)存區(qū)域。
生命周期隨著線程的創(chuàng)建而創(chuàng)建,隨著線程的結(jié)束而死亡。
二、JVM Stack 虛擬棧
1、定義
Java虛擬機(jī)棧是描述Java方法運(yùn)行過程的內(nèi)存模型。
Java虛擬機(jī)棧會為每一個即將運(yùn)行的方法分配“棧幀”空間,用于保存改方法運(yùn)行過程中所需要的一些信息,例如局部變量、操作數(shù)棧、動態(tài)鏈接、方法出口信息等。
當(dāng)這個方法執(zhí)行完后,“棧幀”中的信息出棧,并釋放內(nèi)存空間。
注意:人們常說,Java的內(nèi)存空間分為“棧”和“堆”,棧中存放局部變量,堆中存放對象。 這句話不完全正確!這里的“堆”可以這么理解,但這里的“?!敝淮砹薐ava虛擬機(jī)棧中的局部變量表部分。真正的Java虛擬機(jī)棧是由一個個棧幀組成,而每個棧幀中都擁有:局部變量表、操作數(shù)棧、動態(tài)鏈接、方法出口信息。
2、特點(diǎn)
局部變量表的創(chuàng)建是在方法被執(zhí)行的時候,隨著棧幀的創(chuàng)建而創(chuàng)建。而且,局部變量表的大小在編譯時期就確定下來了,在創(chuàng)建的時候只需分配事先規(guī)定好的大小即可。此外,在方法運(yùn)行的過程中局部變量表的大小是不會發(fā)生改變的。
Java虛擬機(jī)棧會出現(xiàn)兩種異常:
StackOverFlowError
OutOfMemoryError
a) StackOverFlowError:
若Java虛擬機(jī)棧的內(nèi)存大小不允許動態(tài)擴(kuò)展,那么當(dāng)線程請求棧的深度超過當(dāng)前Java虛擬機(jī)棧的最大深度的時候,就拋出StackOverFlowError異常。
b) OutOfMemoryError:
若Java虛擬機(jī)棧的內(nèi)存大小允許動態(tài)擴(kuò)展,且當(dāng)線程請求棧時內(nèi)存用完了,無法再動態(tài)擴(kuò)展了,此時拋出OutOfMemoryError異常。
Java虛擬機(jī)棧是線程私有的,每個線程都有各自的Java虛擬機(jī)棧,而且隨著線程的創(chuàng)建而創(chuàng)建,隨著線程的死亡而死亡。
三、Native Method Stack 本地方法棧
1、定義
本地方法棧和Java虛擬機(jī)棧實(shí)現(xiàn)的功能類似,只不過本地方法區(qū)是本地方法運(yùn)行的內(nèi)存模型。
本地方法被執(zhí)行的時候,在本地方法棧也會創(chuàng)建一個棧幀,用于存放該本地方法的局部變量表、操作數(shù)棧、動態(tài)鏈接、出口信息。
方法執(zhí)行完畢后相應(yīng)的棧幀也會出棧并釋放內(nèi)存空間。
也會拋出StackOverFlowError和OutOfMemoryError異常。
四、Head 堆
1、定義
堆是用來存在對象的內(nèi)存空間。
幾乎所有的對象都存放在堆中。
2、特點(diǎn)
線程共享 :整個Java虛擬機(jī)只有一個堆,所有的線程都訪問同一個堆。
在虛擬機(jī)啟動時創(chuàng)建。
垃圾回收的主要場所。
不同的區(qū)域存放具有不同生命周期的對象。這樣可以根據(jù)不同的區(qū)域使用不同的垃圾回收算法,從而更具有針對性,從而更高效。
堆的大小既可以固定也可以擴(kuò)展,但主流的虛擬機(jī)堆的大小是可擴(kuò)展的,因此當(dāng)線程請求分配內(nèi)存,但堆已滿,且內(nèi)存已滿無法再擴(kuò)展時,就拋出OutOfMemoryError。
五、Method Area 方法區(qū)
1、定義
Java虛擬機(jī)規(guī)范中定義方法區(qū)是堆的一個邏輯部分。
方法區(qū)中存放已經(jīng)被虛擬機(jī)加載的類信息、常量、靜態(tài)變量、即時編譯器編譯后的代碼等。
2、特點(diǎn)
線程共享
方法區(qū)是堆的一個邏輯部分,因此和堆一樣,都是線程共享的。整個虛擬機(jī)中只有一個方法區(qū)。
永久代
方法區(qū)中的信息一般需要長期存在,而且它又是堆的邏輯分區(qū),因此用堆的劃分方法,我們把方法區(qū)稱為老年代。
內(nèi)存回收效率低
方法區(qū)中的信息一般需要長期存在,回收一遍內(nèi)存之后可能只有少量信息無效。
對方法區(qū)的內(nèi)存回收的主要目標(biāo)是:對常量池的回收 和 對類型的卸載。
Java虛擬機(jī)規(guī)范對方法區(qū)的要求比較寬松。
和堆一樣,允許固定大小,也允許可擴(kuò)展的大小,還允許不實(shí)現(xiàn)垃圾回收。
3、常量池
方法區(qū)中存放數(shù)據(jù):類信息、常量、靜態(tài)變量、即時編譯器編譯后的代碼。其中常量存儲在運(yùn)行時常量池中。
我們一般在一個類中通過public static final來聲明一個常量。這個類被編譯后便生成Class文件,這個類的所有信息都存儲在這個class文件中。
當(dāng)這個類被Java虛擬機(jī)加載后,class文件中的常量就存放在方法區(qū)的運(yùn)行時常量池中。而且在運(yùn)行期間,可以向常量池中添加新的常量。如:String類的intern()方法就能在運(yùn)行期間向常量池中添加字符串常量。
當(dāng)運(yùn)行時常量池中的某些常量沒有被對象引用,同時也沒有被變量引用,那么就需要垃圾收集器回收。
六、直接內(nèi)存
直接內(nèi)存是除Java虛擬機(jī)之外的內(nèi)存,但也有可能被Java使用。
在NIO中引入了一種基于通道和緩沖的IO方式。它可以通過調(diào)用本地方法直接分配Java虛擬機(jī)之外的內(nèi)存,然后通過一個存儲在Java堆中的DirectByteBuffer對象直接操作該內(nèi)存,而無需先將外面內(nèi)存中的數(shù)據(jù)復(fù)制到堆中再操作,從而提升了數(shù)據(jù)操作的效率。
直接內(nèi)存的大小不受Java虛擬機(jī)控制,但既然是內(nèi)存,當(dāng)內(nèi)存不足時就會拋出OOM異常。
七、總結(jié)
1、Java虛擬機(jī)的內(nèi)存模型中一共有兩個“?!?,分別是:Java虛擬機(jī)棧和本地方法棧。
兩個“?!钡墓δ茴愃疲际欠椒ㄟ\(yùn)行過程的內(nèi)存模型。并且兩個“?!眱?nèi)部構(gòu)造相同,都是線程私有。
只不過Java虛擬機(jī)棧描述的是Java方法運(yùn)行過程的內(nèi)存模型,而本地方法棧是描述Java本地方法運(yùn)行過程的內(nèi)存模型。
2、Java虛擬機(jī)的內(nèi)存模型中一共有兩個“堆”,一個是原本的堆,一個是方法區(qū)。
方法區(qū)本質(zhì)上是屬于堆的一個邏輯部分。堆中存放對象,方法區(qū)中存放類信息、常量、靜態(tài)變量、即時編譯器編譯的代碼。
3、堆是Java虛擬機(jī)中最大的一塊內(nèi)存區(qū)域,也是垃圾收集器主要的工作區(qū)域。
4、程序計(jì)數(shù)器、Java虛擬機(jī)棧、本地方法棧是線程私有的,即每個線程都擁有各自的程序計(jì)數(shù)器、Java虛擬機(jī)棧、本地方法區(qū)。并且他們的生命周期和所屬的線程一樣。
而堆、方法區(qū)是線程共享的,在Java虛擬機(jī)中只有一個堆、一個方法棧。并在JVM啟動的時候就創(chuàng)建,JVM停止才銷毀。
2、類加載過程
Java 類加載的過程簡介
一般來說,我們把 Java 的類加載過程分為三個主要步驟:加載,連接,初始化,具體行為在 Java 虛擬機(jī)規(guī)范里有非常詳細(xì)的定義。
? ? ? ? 首先是加載過程(Loading),它是 Java 將字節(jié)碼數(shù)據(jù)從不同的數(shù)據(jù)源讀取到 JVM 中,并映射為 JVM 認(rèn)可的數(shù)據(jù)結(jié)構(gòu)(Class 對象),這里的數(shù)據(jù)源可能是各種各樣的形態(tài),比如 jar 文件,class 文件,甚至是網(wǎng)絡(luò)數(shù)據(jù)源等;如果輸入數(shù)據(jù)不是 ClassFile 的結(jié)構(gòu),則會拋出 ClassFormatError。加載階段是用戶參與的階段,我們可以自定義類加載器,去實(shí)現(xiàn)自己的類加載過程。
? ? ? ? 第二階段是連接(Linking),這是核心的步驟,簡單說是把原始的類定義信息平滑地轉(zhuǎn)入 JVM 運(yùn)行的過程中。這里可進(jìn)一步細(xì)分成三個步驟:1,驗(yàn)證(Verification),這是虛擬機(jī)安全的重要保障,JVM 需要核驗(yàn)字節(jié)信息是符合 Java 虛擬機(jī)規(guī)范的,否則就被認(rèn)為是 VerifyError,這樣就防止了惡意信息或者不合規(guī)信息危害 JVM 的運(yùn)行,驗(yàn)證階段有可能觸發(fā)更多 class 的加載。2,準(zhǔn)備(Pereparation),創(chuàng)建類或者接口中的靜態(tài)變量,并初始化靜態(tài)變量的初始值。但這里的“初始化”和下面的顯示初始化階段是有區(qū)別的,側(cè)重點(diǎn)在于分配所需要的內(nèi)存空間,不會去執(zhí)行更進(jìn)一步的 JVM 指令。3,解析(Resolution),在這一步會將常量池中的符號引用(symbolic reference)替換為直接引用。在 Java 虛擬機(jī)規(guī)范中,詳細(xì)介紹了類,接口,方法和字段等各方面的解析。
????????最后是初始化階段(initialization),這一步真正去執(zhí)行類初始化的代碼邏輯,包括靜態(tài)字段賦值的動作,以及執(zhí)行類定義中的靜態(tài)初始化塊內(nèi)的邏輯,編譯器在編譯階段就會把這部分邏輯整理好,父類型的初始化邏輯優(yōu)先于當(dāng)前類型的邏輯。再來談?wù)勲p親委派模型,簡單說就是當(dāng)加載器(Class-Loader)試圖加載某個類型的時候,除非父類加載器找不到相應(yīng)類型,否則盡量將這個任務(wù)代理給當(dāng)前加載器的父加載器去做。使用委派模型的目的是避免重復(fù)加載 Java 類型。
自定義類加載器的常見場景
? ? ? ? 實(shí)現(xiàn)類似進(jìn)程內(nèi)隔離,類加載器實(shí)際上用作不同的命名空間,以及提供類似容器,模塊化的效果。例如:1,兩個模塊依賴于某個類庫的不同版本,如果分別被不同的容器加載,就可以互不干擾。這個方面的集大成者是 Jave EE 和 OSGL,JPMS等框架。2,應(yīng)用需要從不同的數(shù)據(jù)源獲取類定義信息,例如網(wǎng)絡(luò)數(shù)據(jù)源,而不是本地文件系統(tǒng)。3,或者是需要自己操縱字節(jié)碼,動態(tài)修改生成類型。
????????我們可以總體上簡單理解自定義類加載過程:1,通過指定名稱,找到其二進(jìn)制實(shí)現(xiàn),這里往往就是自定義類加載器會“定制”的部分,例如,在特定數(shù)據(jù)源根據(jù)名字獲取字節(jié)碼,或者修改或生成字節(jié)碼。2,然后,創(chuàng)建 Class 對象,并完成類加載過程。二進(jìn)制信息到 class 對象的轉(zhuǎn)換,通常就依賴 defineClass,我們無需自己實(shí)現(xiàn),它是 final 方法。有了 Class 對象,后續(xù)完成加載過程就順利成章了。
如何降低類加載的開銷
? ? ? ? 這么多類加載,有沒有什么通用方法,不需要代碼和其他工作量,就可以降低類加載的開銷?這個可以有。
? ? ? ? 1,比如 AOT,相當(dāng)于直接編譯成機(jī)器碼,降低的其實(shí)主要是解釋和編譯開銷。但是其目前還是個實(shí)驗(yàn)特性,支持的平臺也有限,比如,JDK 9 僅支持 Linux x64,所以局限性太大,先暫且不談。
? ? ? ? 2,還有就是較少人知道的 AppCDS(Application Class-Data Sharing),CDS 在 Java 5 中被引進(jìn),但僅限于 Bootstrap Class-loader,在 8u40 中實(shí)現(xiàn)了 AppCDS,支持其他的類加載器,目前已經(jīng)在 JDK 10 中開源。
? ? ? ? 簡單來說,AppCDS 基本原理和工作過程是:首先,JVM 將類信息加載,解析成元數(shù)據(jù),并根據(jù)是否需要修改,將其分類為 Read-Only 部分和 Read-Write 部分。然后,將這些元數(shù)據(jù)直接存儲在文件系統(tǒng)中,作為所謂的 Shared Archive。第二,在應(yīng)用程序啟動時,指定歸檔文件,并開啟 AppCDS。AppCDS 改善啟動速度非常明顯,傳統(tǒng)的 Java EE 應(yīng)用,一般可以提高 20% ~ 30%以上。與此同時,降低內(nèi)存 footprint,因?yàn)橥画h(huán)境的 Java 進(jìn)程間可以共享部分?jǐn)?shù)據(jù)結(jié)構(gòu)。當(dāng)然,也不是沒有局限性,如果恰好大量使用了運(yùn)行時動態(tài)類加載,它的幫助就有限了。
3、HashMap底層實(shí)現(xiàn)原理
HashMap基于hashing原理,我們通過put()和get()方法儲存和獲取對象。當(dāng)我們將鍵值對傳遞給put()方法時,它調(diào)用鍵對象的hashCode()方法來計(jì)算hashcode,然后找到bucket位置來儲存值對象。當(dāng)獲取對象時,通過鍵對象的equals()方法找到正確的鍵值對,然后返回值對象。HashMap使用鏈表來解決碰撞問題,當(dāng)發(fā)生碰撞了,對象將會儲存在鏈表的下一個節(jié)點(diǎn)中。 HashMap在每個鏈表節(jié)點(diǎn)中儲存鍵值對對象。
當(dāng)兩個不同的鍵對象的hashcode相同時會發(fā)生什么? 它們會儲存在同一個bucket位置的鏈表中。鍵對象的equals()方法用來找到鍵值對。
HashMap實(shí)現(xiàn)了Map接口,Map接口對鍵值對進(jìn)行映射。Map中不允許重復(fù)的鍵。Map接口有兩個基本的實(shí)現(xiàn),HashMap和TreeMap。TreeMap保存了對象的排列次序,而HashMap則不能。HashMap允許鍵和值為null。HashMap是非synchronized的,但collection框架提供方法能保證HashMap synchronized,這樣多個線程同時訪問HashMap時,能保證只有一個線程更改Map。
HashMap和Hashtable的區(qū)別:
HashMap和Hashtable都實(shí)現(xiàn)了Map接口,但決定用哪一個之前先要弄清楚它們之間的分別。主要的區(qū)別有:線程安全性,同步(synchronization),以及速度。
1.HashMap幾乎可以等價于Hashtable,除了HashMap是非synchronized的,并可以接受null(HashMap可以接受為null的鍵值(key)和值(value),而Hashtable則不行)。
2.HashMap是非synchronized,而Hashtable是synchronized,這意味著Hashtable是線程安全的,多個線程可以共享一個Hashtable;而如果沒有正確的同步的話,多個線程是不能共享HashMap的。Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的擴(kuò)展性更好。
3.另一個區(qū)別是HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。所以當(dāng)有其它線程改變了HashMap的結(jié)構(gòu)(增加或者移除元素),將會拋出ConcurrentModificationException,但迭代器本身的remove()方法移除元素則不會拋出ConcurrentModificationException異常。但這并不是一個一定發(fā)生的行為,要看JVM。這條同樣也是Enumeration和Iterator的區(qū)別。
4.由于Hashtable是線程安全的也是synchronized,所以在單線程環(huán)境下它比HashMap要慢。如果你不需要同步,只需要單一線程,那么使用HashMap性能要好過Hashtable。
5.HashMap不能保證隨著時間的推移Map中的元素次序是不變的。
tip:HashMap可以通過下面的語句進(jìn)行同步:Map m = Collections.synchronizeMap(hashMap);
HashMap和HashSet的區(qū)別:
HashMap和HashSet的區(qū)別是Java面試中最常被問到的問題。如果沒有涉及到Collection框架以及多線程的面試,可以說是不完整。而Collection框架的問題不涉及到HashSet和HashMap,也可以說是不完整。HashSet是collection框架的一部分,它們讓我們能夠使用對象的集合。collection框架有自己的接口和實(shí)現(xiàn),主要分為Set接口,List接口和Queue接口。它們有各自的特點(diǎn),Set的集合里不允許對象有重復(fù)的值,List允許有重復(fù),它對集合中的對象進(jìn)行索引,Queue的工作原理是FCFS算法(First Come, First Serve)。
HashSet實(shí)現(xiàn)了Set接口,它不允許集合中有重復(fù)的值,當(dāng)我們提到HashSet時,第一件事情就是在將對象存儲在HashSet之前,要先確保對象重寫equals()和hashCode()方法,這樣才能比較對象的值是否相等,以確保set中沒有儲存相等的對象。如果我們沒有重寫這兩個方法,將會使用這個方法的默認(rèn)實(shí)現(xiàn)。
*HashMap*? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?*HashSet*
HashMap實(shí)現(xiàn)了Map接口? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? HashSet實(shí)現(xiàn)了Set接口
HashMap儲存鍵值對? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?HashSet僅僅存儲對象
使用put()方法將元素放入map中? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 使用add()方法將元素放入set中
HashMap中使用鍵對象來計(jì)算hashcode值? ? ? ? ? ? ? ? HashSet使用成員對象來計(jì)算hashcode值,? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?對于兩個對象來說hashcode可能相同,所以? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? equals()方法用來判斷對象的相等性,如果兩個? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?對象不同的話,那么返回false
HashMap比較快,因?yàn)槭鞘褂梦ㄒ坏逆I來獲取對象? ? HashSet較HashMap來說比較慢
4、多線程,線程池以及synchronize關(guān)鍵字
流程圖如下:

5、Handler原理

6、動畫實(shí)現(xiàn)原理
Frame Animation 幀動畫,通過順序播放一系列圖像從而產(chǎn)生動畫效果,。圖片過多時容易造成OOM(Out Of Memory內(nèi)存用完)異常。
Tween Animation?補(bǔ)間動畫(又叫view動畫),是通過對場景里的對象不斷做圖像變換(透明度、縮放、平移、旋轉(zhuǎn))從而產(chǎn)生動畫效果,是一種漸進(jìn)式動畫,并且View動畫支持自定義。
Accribute Animation 屬性動畫,這也是在android3.0之后引進(jìn)的動畫,在手機(jī)的版本上是android4.0就可以使用這個動 畫,通過動態(tài)的改變對象的屬性從而達(dá)到動畫效果。