如夢朦朧 九月份的時候有了換工作的躁動,然后投了某度的 Android 崗位,本以為像我這種非 211,、985 沒工作經(jīng)驗的渣渣只能被直接 pass,結(jié)果卻意外的收到了電話,真是受寵若驚.經(jīng)過電面,技術(shù)三面,然后就是等通知到最后拿到了 OFFER,如夢一般,真是挺激動的. 面試準備 當收到HR的面試的通知還是很懵逼的,因為感覺自己突然啥都不會了,迅速鎮(zhèn)定下來,去網(wǎng)上找了一下某度的面試題,但是發(fā)現(xiàn)都只有提問了什么并沒有對所提問題的解答,那只能自力更生,像做試卷一樣,一遍總結(jié)一遍溫顧.其實大多都是平時開發(fā)中用到的,只是我們沒有總結(jié)過,被問起來的時候回答的難免會有點捉襟見肘,不能回答的很全面.下面為我個人總(bai)結(jié)(du)的,希望對你能有所幫助,但畢竟能力有限,有寫的不對的地方,還望輕噴.雖然噴我我也不會改的. 因為本文篇幅較長建議收藏,在用到時候找出來看一眼.有一些知識點可能沒涉及到,以后會加以補足.因為面試無非是考察你對技術(shù)的理解和總結(jié),所以本篇的每個點總結(jié)的比較精簡,只是讓你大概的說出來,有的部分是需要能夠畫出原理圖并進行解釋說明,這個要在工作中多積累. JAVA 一. 類的加載過程 Person person = new Person();為例進行說明 1).因為new用到了Person.class,,所以會先找到Person.class文件,并加載到內(nèi)存中; 2).執(zhí)行該類中的static代碼塊,,如果有的話,,給Person.class類進行初始化; 3).在堆內(nèi)存中開辟空間分配內(nèi)存地址; 4).在堆內(nèi)存中建立對象的特有屬性,并進行默認初始化; 5).對屬性進行顯示初始化; 6).對對象進行構(gòu)造代碼塊初始化; 7).對對象進行與之對應(yīng)的構(gòu)造函數(shù)進行初始化; 8).將內(nèi)存地址付給棧內(nèi)存中的p變量 二. JVM相關(guān)知識,,GC機制 JVM基本構(gòu)成 從上圖可知,,JVM主要包括四個部分: ????1.類加載器(ClassLoader):在JVM啟動時或者在類運行時將需要的class加載到JVM中。(下圖表示了從 java 源文件到 JVM 的整個過程,,可配合理解,。 2.執(zhí)行引擎:負責(zé)執(zhí)行class文件中包含的字節(jié)碼指令; 3.內(nèi)存區(qū)(也叫運行時數(shù)據(jù)區(qū)):是在JVM運行的時候操作所分配的內(nèi)存區(qū),。運行時內(nèi)存區(qū)主要可以劃分為5個區(qū)域,,如圖:
4.本地方法接口:主要是調(diào)用C或C++實現(xiàn)的本地方法及返回結(jié)果。 GC機制 垃圾收集器一般必須完成兩件事:檢測出垃圾,;回收垃圾,。怎么檢測出垃圾?一般有以下幾種方法: 引用計數(shù)法: 給一個對象添加引用計數(shù)器,,每當有個地方引用它,,計數(shù)器就加1;引用失效就減1,。好了,,問題來了,如果我有兩個對象A和B,,互相引用,,除此之外,沒有其他任何對象引用它們,,實際上這兩個對象已經(jīng)無法訪問,,即是我們說的垃圾對象。但是互相引用,,計數(shù)不為0,,導(dǎo)致無法回收,,所以還有另一種方法: 可達性分析算法: 以根集對象為起始點進行搜索,如果有對象不可達的話,,即是垃圾對象,。這里的根集一般包括java棧中引用的對象、方法區(qū)常良池中引用的對象,、本地方法中引用的對象等,。 總之,JVM在做垃圾回收的時候,,會檢查堆中的所有對象是否會被這些根集對象引用,,不能夠被引用的對象就會被垃圾收集器回收。一般回收算法也有如下幾種: 1).標記-清除(Mark-sweep) 2).復(fù)制(Copying 3).標記-整理(Mark-Compact) 4).分代收集算法 三. 類的加載器,,雙親機制,,Android的類加載器。 類的加載器 大家都知道,,當我們寫好一個 Java 程序之后,,不是管是 CS 還是 BS應(yīng)用,都是由若干個 .class 文件組織而成的一個完整的 Java 應(yīng)用程序,,當程序在運行時,,即會調(diào)用該程序的一個入口函數(shù)來調(diào)用系統(tǒng)的相關(guān)功能,而這些功能都被封裝在不同的 class 文件當中,,所以經(jīng)常要從這個 class 文件中要調(diào)用另外一個 class 文件中的方法,,如果另外一個文件不存在的,則會引發(fā)系統(tǒng)異常,。而程序在啟動的時候,,并不會一次性加載程序所要用的所有 class 文件,而是根據(jù)程序的需要,,通過 Java 的類加載機制(ClassLoader)來動態(tài)加載某個 class 文件到內(nèi)存當中的,,從而只有 class 文件被載入到了內(nèi)存之后,才能被其它 class 所引用,。所以 ClassLoader 就是用來動態(tài)加載 class 文件到內(nèi)存當中用的,。 雙親機制 1、原理介紹 ClassLoader 使用的是雙親委托模型來搜索類的,,每個 ClassLoader實例都有一個父類加載器的引用(不是繼承的關(guān)系,,是一個包含的關(guān)系),虛擬機內(nèi)置的類加載器(Bootstrap ClassLoader)本身沒有父類加載器,,但可以用作其它ClassLoader實例的的父類加載器,。當一個ClassLoader實例需要加載某個類時,它會試圖親自搜索某個類之前,,先把這個任務(wù)委托給它的父類加載器,,這個過程是由上至下依次檢查的,,首先由最頂層的類加載器Bootstrap ClassLoader試圖加載,如果沒加載到,,則把任務(wù)轉(zhuǎn)交給Extension ClassLoader試圖加載,,如果也沒加載到,則轉(zhuǎn)交給App ClassLoader 進行加載,,如果它也沒有加載得到的話,,則返回給委托的發(fā)起者,由它到指定的文件系統(tǒng)或網(wǎng)絡(luò)等URL中加載該類,。如果它們都沒有加載到這個類時,,則拋出ClassNotFoundException 異常。否則將這個找到的類生成一個類的定義,,并將它加載到內(nèi)存當中,,最后返回這個類在內(nèi)存中的 Class實例對象。 2,、為什么要使用雙親委托這種模型呢,? 因為這樣可以避免重復(fù)加載,當父親已經(jīng)加載了該類的時候,,就沒有必要子ClassLoader再加載一次,。考慮到安全因素,,我們試想一下,,如果不使用這種委托模式,,那我們就可以隨時使用自定義的String來動態(tài)替代java核心api中定義的類型,,這樣會存在非常大的安全隱患,而雙親委托的方式,,就可以避免這種情況,,因為String已經(jīng)在啟動時就被引導(dǎo)類加載器(Bootstrcp ClassLoader)加載,所以用戶自定義的ClassLoader永遠也無法加載一個自己寫的 String,,除非你改變 JDK 中 ClassLoader 搜索類的默認算法,。 3、但是 JVM 在搜索類的時候,,又是如何判定兩個 class 是相同的呢,? JVM 在判定兩個 class 是否相同時,不僅要判斷兩個類名是否相同,,而且要判斷是否由同一個類加載器實例加載的,。只有兩者同時滿足的情況下,JVM 才認為這兩個 class 是相同的,。就算兩個 class 是同一份 class 字節(jié)碼,,如果被兩個不同的 ClassLoader 實例所加載,,JVM 也會認為它們是兩個不同 class。比如網(wǎng)絡(luò)上的一個 Java 類org.classloader.simple.NetClassLoaderSimple,,javac 編譯之后生成字節(jié)碼文件 NetClassLoaderSimple.class,,ClassLoaderA 和ClassLoaderB 這兩個類加載器并讀取了 NetClassLoaderSimple.class文件,并分別定義出了 java.lang.Class 實例來表示這個類,,對于 JVM 來說,,它們是兩個不同的實例對象,但它們確實是同一份字節(jié)碼文件,,如果試圖將這個 Class 實例生成具體的對象進行轉(zhuǎn)換時,,就會拋運行時異常 java.lang.ClassCaseException,提示這是兩個不同的類型,。 Android類加載器 對于 Android 而言,,最終的 apk 文件包含的是 dex 類型的文件,dex文件是將 class 文件重新打包,,打包的規(guī)則又不是簡單地壓縮,,而是完全對class 文件內(nèi)部的各種函數(shù)表,變量表進行優(yōu)化,,產(chǎn)生一個新的文件,,即dex文件。因此加載這種特殊的 Class 文件就需要特殊的類加載器DexClassLoader,。 四. 集合框架,,list,map,,set都有哪些具體的實現(xiàn)類,,區(qū)別都是什么? 1.List,Set都是繼承自 Collection 接口,Map 則不是; 2.List特點:元素有放入順序,,元素可重復(fù); Set特點:元素?zé)o放入順序,,元素不可重復(fù),重復(fù)元素會覆蓋掉,,(注 意:元素雖然無放入順序,,但是元素在set中的位置是有該元素的 HashCode決定的,其位置其實是固定的,,加入Set 的Object必須定 義equals()方法; 另外list支持for循環(huán),,也就是通過下標來遍歷,也可以用迭代器,,但是 set只能用迭代,,因為他無序,無法用下標來取得想要的值)。 3.Set和List對比: Set:檢索元素效率低下,,刪除和插入效率高,,插入和刪除不會引起元 素位置改變。 List:和數(shù)組類似,,List可以動態(tài)增長,,查找元素效率高,插入刪除元 素效率低,,因為會引起其他元素位置改變,。 4.Map適合儲存鍵值對的數(shù)據(jù)。 5.線程安全集合類與非線程安全集合類 LinkedList,、ArrayList,、HashSet是非線程安全的,Vector是線程安全的; HashMap是非線程安全的,,HashTable是線程安全的; StringBuilder是非線程安全的,,StringBuffer是線程安全的。 下面是這些類具體的使用介紹: ArrayList與LinkedList的區(qū)別和適用場景 Arraylist: 優(yōu)點:ArrayList是實現(xiàn)了基于動態(tài)數(shù)組的數(shù)據(jù)結(jié)構(gòu),因為地址連續(xù),,一旦數(shù)據(jù)存儲好了,,查詢操作效率會比較高(在內(nèi)存里是連著放的)。 缺點:因為地址連續(xù),, ArrayList要移動數(shù)據(jù),所以插入和刪除操作效率比較低,。 LinkedList: 優(yōu)點:LinkedList基于鏈表的數(shù)據(jù)結(jié)構(gòu),地址是任意的,所以在開辟內(nèi)存空間的時候不需要等一個連續(xù)的地址,,對于新增和刪除操作add和remove,,LinedList比較占優(yōu)勢。LinkedList 適用于要頭尾操作或插入指定位置的場景,。 缺點:因為LinkedList要移動指針,所以查詢操作性能比較低,。 適用場景分析: 當需要對數(shù)據(jù)進行對此訪問的情況下選用 ArrayList,當需要對數(shù)據(jù)進行多次增加刪除修改時采用 LinkedList,。 ArrayList 與 Vector 的區(qū)別和適用場景 ArrayList有三個構(gòu)造方法: public ArrayList(int initialCapacity)//構(gòu)造一個具有指定初始容量的空列表,。 Vector有四個構(gòu)造方法: public Vector()//使用指定的初始容量和等于零的容量增量構(gòu)造一個空向量,。 ArrayList 和 Vector 都是用數(shù)組實現(xiàn)的,主要有這么三個區(qū)別: 1).Vector是多線程安全的,,線程安全就是說多線程訪問同一代碼,,不會產(chǎn)生不確定的結(jié)果。而ArrayList不是,這個可以從源碼中看出,,Vector類中的方法很多有synchronized進行修飾,,這樣就導(dǎo)致了Vector在效率上無法與ArrayList相比; 2).兩個都是采用的線性連續(xù)空間存儲元素,,但是當空間不足的時候,,兩個類的增加方式是不同。 3).Vector可以設(shè)置增長因子,,而ArrayList不可以,。 4).Vector是一種老的動態(tài)數(shù)組,是線程同步的,,效率很低,,一般不贊成使用。 適用場景: 1.Vector是線程同步的,,所以它也是線程安全的,,而ArrayList是線程異步的,是不安全的,。如果不考慮到線程的安全因素,,一般用ArrayList效率比較高。 2.如果集合中的元素的數(shù)目大于目前集合數(shù)組的長度時,,在集合中使用數(shù)據(jù)量比較大的數(shù)據(jù),,用Vector有一定的優(yōu)勢。 HashSet與Treeset的適用場景 1.TreeSet 是二叉樹(紅黑樹的樹據(jù)結(jié)構(gòu))實現(xiàn)的,Treeset中的數(shù)據(jù)是自動排好序的,,不允許放入null值,。 2.HashSet 是哈希表實現(xiàn)的,HashSet中的數(shù)據(jù)是無序的,可以放入null,,但只能放入一個null,,兩者中的值都不能重復(fù),就如數(shù)據(jù)庫中唯一約束,。 3.HashSet要求放入的對象必須實現(xiàn)HashCode()方法,,放入的對象,是以hashcode碼作為標識的,,而具有相同內(nèi)容的String對象,,hashcode是一樣,所以放入的內(nèi)容不能重復(fù),。但是同一個類的對象可以放入不同的實例,。 適用場景分析: HashSet是基于Hash算法實現(xiàn)的,其性能通常都優(yōu)于TreeSet,。為快速查找而設(shè)計的Set,,我們通常都應(yīng)該使用HashSet,,在我們需要排序的功能時,我們才使用TreeSet,。 HashMap與TreeMap,、HashTable的區(qū)別及適用場景 HashMap 非線程安全 HashMap:基于哈希表(散列表)實現(xiàn)。使用 HashMap 要求添加的鍵類明確定義了 hashCode() 和 equals()[可以重寫 hashCode() 和equals()],,為了優(yōu)化 HashMap 空間的使用,,您可以調(diào)優(yōu)初始容量和負載因子。其中散列表的沖突處理主要分兩種,,一種是開放定址法,,另一種是鏈表法。HashMap 的實現(xiàn)中采用的是鏈表法,。 TreeMap:非線程安全基于紅黑樹實現(xiàn),。TreeMap 沒有調(diào)優(yōu)選項,因為該樹總處于平衡狀態(tài),。 適用場景分析: HashMap和HashTable:HashMap去掉了HashTable的contains方法,,但是加上了containsValue()和containsKey()方法。HashTable同步的,,而HashMap是非同步的,,效率上比HashTable要高。HashMap允許空鍵值,,而HashTable不允許,。 HashMap:適用于Map中插入、刪除和定位元素,。 Treemap:適用于按自然順序或自定義順序遍歷鍵(key),。 (ps:其實我們工作的過程中對集合的使用是很頻繁的,稍加注意并總結(jié)積累一下,在面試的時候應(yīng)該會回答的很輕松) 五. concurrentHashmap原理,原子類,。 ConcurrentHashMap 作為一種線程安全且高效的哈希表的解決方案,,尤其其中的'分段鎖'的方案,相比 HashTable 的全表鎖在性能上的提升非常之大. 六. volatile原理 在《Java并發(fā)編程:核心理論》一文中,,我們已經(jīng)提到過可見性,、有序性及原子性問題,通常情況下我們可以通過Synchronized關(guān)鍵字來解決這些個問題,,不過如果對Synchronized原理有了解的話,,應(yīng)該知道Synchronized是一個比較重量級的操作,對系統(tǒng)的性能有比較大的影響,,所以,,如果有其他解決方案,我們通常都避免使用Synchronized來解決問題,。而volatile關(guān)鍵字就是Java中提供的另一種解決可見性和有序性問題的方案。對于原子性,需要強調(diào)一點,,也是大家容易誤解的一點:對volatile變量的單次讀/寫操作可以保證原子性的,,如long和double類型變量,但是并不能保證i++這種操作的原子性,,因為本質(zhì)上i++是讀,、寫兩次操作。 參考文章 https://link.jianshu.com/?t=https://www.cnblogs.com/paddix/p/5428507.html 七. 多線程的使用場景 使用多線程就一定效率高嗎,? 有時候使用多線程并不是為了提高效率,,而是使得CPU能夠同時處理多個事件。 1).為了不阻塞主線程,啟動其他線程來做好事的事情,比如APP中耗時操作都不在UI中做. 2).實現(xiàn)更快的應(yīng)用程序,即主線程專門監(jiān)聽用戶請求,子線程用來處理用戶請求,以獲得大的吞吐量.感覺這種情況下,,多線程的效率未必高,。 這種情況下的多線程是為了不必等待, 可以并行處理多條數(shù)據(jù),。 比如JavaWeb的就是主線程專門監(jiān)聽用戶的HTTP請求,,然后啟動子線程去處理用戶的HTTP請求。 3).某種雖然優(yōu)先級很低的服務(wù),,但是卻要不定時去做,。 比如Jvm的垃圾回收。 4.)某種任務(wù),,雖然耗時,,但是不耗CPU的操作時,開啟多個線程,,效率會有顯著提高,。 比如讀取文件,然后處理,。 磁盤IO是個很耗費時間,,但是不耗CPU計算的工作。 所以可以一個線程讀取數(shù)據(jù),,一個線程處理數(shù)據(jù),。肯定比一個線程讀取數(shù)據(jù),,然后處理效率高,。 因為兩個線程的時候充分利用了CPU等待磁盤IO的空閑時間。 八. JAVA常量池 Interger 中的128(-128~127) a.當數(shù)值范圍為-128~127時:如果兩個 new 出來 Integer 對象,,即使值相同,,通過“==”比較結(jié)果為false,但兩個對象直接賦值,,則通過“==”比較結(jié)果為“true,,這一點與String非常相似,。 b.當數(shù)值不在-128~127時,無論通過哪種方式,,即使兩個對象的值相等,,通過“==”比較,其結(jié)果為false,; c.當一個Integer對象直接與一個int基本數(shù)據(jù)類型通過“==”比較,,其結(jié)果與第一點相同; d.Integer對象的hash值為數(shù)值本身,; 為什么是-128-127? 在 Integer 類中有一個靜態(tài)內(nèi)部類 IntegerCache,,在 IntegerCache類中有一個 Integer 數(shù)組,用以緩存當數(shù)值范圍為-128~127時的Integer 對象,。 九. 簡單介紹一下java中的泛型,,泛型擦除以及相關(guān)的概念。 泛型是 Java SE 1.5的新特性,,泛型的本質(zhì)是參數(shù)化類型,,也就是說所操作的數(shù)據(jù)類型被指定為一個參數(shù)。這種參數(shù)類型可以用在類,、接口和方法的創(chuàng)建中,,分別稱為泛型類、泛型接口,、泛型方法,。 Java語言引入泛型的好處是安全簡單。 在 Java SE 1.5 之前,,沒有泛型的情況的下,,通過對類型 Object 的引用來實現(xiàn)參數(shù)的“任意化”,“任意化”帶來的缺點是要做顯式的強制類型轉(zhuǎn)換,,而這種轉(zhuǎn)換是要求開發(fā)者對實際參數(shù)類型可以預(yù)知的情況下進行的,。對于強制類型轉(zhuǎn)換錯誤的情況,編譯器可能不提示錯誤,,在運行的時候才出現(xiàn)異常,,這是一個安全隱患。 泛型的好處是在編譯的時候檢查類型安全,并且所有的強制轉(zhuǎn)換都是自動和隱式的,提高代碼的重用率,。 1、泛型的類型參數(shù)只能是類類型(包括自定義類),,不能是簡單類型。 2、同一種泛型可以對應(yīng)多個版本(因為參數(shù)類型是不確定的),,不同版本的泛型類實例是不兼容的,。 3,、泛型的類型參數(shù)可以有多個。 4,、泛型的參數(shù)類型可以使用extends語句,,例如 5、泛型的參數(shù)類型還可以是通配符類型,。例如Class classType = Class.forName('java.lang.String'); 泛型擦除以及相關(guān)的概念 Java 中的泛型基本上都是在編譯器這個層次來實現(xiàn)的,。在生成的 Java字節(jié)碼中是不包含泛型中的類型信息的。使用泛型的時候加上的類型參數(shù),,會在編譯器在編譯的時候去掉,。這個過程就稱為類型擦除。 類型擦除引起的問題及解決方法 1,、先檢查,,在編譯,以及檢查編譯的對象和引用傳遞的問題 2,、自動類型轉(zhuǎn)換 3,、類型擦除與多態(tài)的沖突和解決方法 4、泛型類型變量不能是基本數(shù)據(jù)類型 5,、運行時類型查詢 6,、異常中使用泛型的問題 7、數(shù)組(這個不屬于類型擦除引起的問題) 9,、類型擦除后的沖突 10,、泛型在靜態(tài)方法和靜態(tài)類中的問題 Android 一. Handler機制 Android 的消息機制也就是 handler 機制,創(chuàng)建 handler 的時候會創(chuàng)建一個 looper ( 通過 looper.prepare() 來創(chuàng)建 ),looper 一般為主線程 looper. handler 通過 send 發(fā)送消息 (sendMessage) ,當然 post 一系列方法最終也是通過 send 來實現(xiàn)的,在 send 方法中handler 會通過 enqueueMessage() 方法中的 enqueueMessage(msg,millis )向消息隊列 MessageQueue 插入一條消息,同時會把本身的 handler 通過 msg.target = this 傳入. Looper 是一個死循環(huán),不斷的讀取MessageQueue中的消息,loop 方法會調(diào)用 MessageQueue 的 next 方法來獲取新的消息,next 操作是一個阻塞操作,當沒有消息的時候 next 方法會一直阻塞,進而導(dǎo)致 loop 一直阻塞,當有消息的時候,Looper 就會處理消息 Looper 收到消息之后就開始處理消息: msg.target.dispatchMessage(msg),當然這里的 msg.target 就是上面?zhèn)鬟^來的發(fā)送這條消息的 handler 對象,這樣 handler 發(fā)送的消息最終又交給他的dispatchMessage方法來處理了,這里不同的是,handler 的 dispatchMessage 方法是在創(chuàng)建 Handler時所使用的 Looper 中執(zhí)行的,這樣就成功的將代碼邏輯切換到了主線程了. Handler 處理消息的過程是:首先,檢查Message 的 callback 是否為 null,不為 null 就通過 handleCallBack 來處理消息,Message 的 callback 是一個 Runnable 對象,實際上是 handler 的 post 方法所傳遞的 Runnable 參數(shù).其次是檢查 mCallback 是 否為 null,不為 null 就調(diào)用 mCallback 的handleMessage 方法來處理消息. 二. View的繪制流程 View 的繪制流程:OnMeasure()——>OnLayout()——>OnDraw() 各步驟的主要工作: OnMeasure(): 測量視圖大小。從頂層父View到子View遞歸調(diào)用measure方法,,measure方法又回調(diào)OnMeasure,。 OnLayout(): 確定View位置,進行頁面布局,。從頂層父View向子View的遞歸調(diào)用view.layout方法的過程,,即父View根據(jù)上一步measure子View所得到的布局大小和布局參數(shù),將子View放在合適的位置上,。 OnDraw(): 繪制視圖:ViewRoot創(chuàng)建一個Canvas對象,,然后調(diào)用OnDraw()。六個步驟:①,、繪制視圖的背景,;②、保存畫布的圖層(Layer),;③,、繪制View的內(nèi)容,;④、繪制View子視圖,,如果沒有就不用,;⑤、還原圖層(Layer),;⑥,、繪制滾動條。 三. 事件傳遞機制 1).Android事件分發(fā)機制的本質(zhì)是要解決:點擊事件由哪個對象發(fā)出,,經(jīng)過哪些對象,,最終達到哪個對象并最終得到處理。這里的對象是指Activity,、ViewGroup,、View. 2).Android中 事件分發(fā)順序:Activity(Window) -> ViewGroup -> View. 3).事件分發(fā)過程由 dispatchTouchEvent() 、onInterceptTouchEvent() 和 onTouchEvent() 三個方法協(xié)助完成 設(shè)置 Button 按鈕來響應(yīng)點擊事件事件傳遞情況:(如下圖) 布局如下: 最外層:Activiy A,,包含兩個子View:ViewGroup B,、View C 中間層:ViewGroup B,包含一個子View:View C 最內(nèi)層:View C 假設(shè)用戶首先觸摸到屏幕上 View C 上的某個點(如圖中黃色區(qū)域),,那么 Action_DOWN 事件就在該點產(chǎn)生,,然后用戶移動手指并最后離開屏幕。 按鈕點擊事件: DOWN 事件被傳遞給 C 的 onTouchEvent 方法,,該方法返回 true,,表示處理這個事件;因為 C 正在處理這個事件,那么 DOWN事件將不再往上傳遞給 B 和 A 的onTouchEvent(),;該事件列的其他事件(Move,、Up)也將傳遞給 C 的 onTouchEvent(); (記住這個圖的傳遞順序,面試的時候能夠畫出來,就很詳細了) 四. Binder機制 1.了解Binder 在 Android 系統(tǒng)中,每一個應(yīng)用程序都運行在獨立的進程中,,這也保證了當其中一個程序出現(xiàn)異常而不會影響另一個應(yīng)用程序的正常運轉(zhuǎn),。在許多情況下,我們activity都會與各種系統(tǒng)的service打交道,,很顯然,,我們寫的程序中activity與系統(tǒng)service肯定不是同一個進程,但是它們之間是怎樣實現(xiàn)通信的呢,?所以Binder是android中一種實現(xiàn)進程間通信(IPC)的方式之一,。 1).首先,Binder分為Client和Server兩個進程,。 注意,,Client和Server是相對的。誰發(fā)消息,誰就是Client,,誰接收消息,,誰就是Server。 舉個例子,,兩個進程A和B之間使用Binder通信,,進程A發(fā)消息給進程B,那么這時候A是Binder Client,,B是Binder Server,;進程B發(fā)消息給進程A,那么這時候B是Binder Client,,A是Binder Server——其實這么說雖然簡單了,,但還是不太嚴謹,,我們先這么理解著,。 2).其次,我們看下面這個圖(摘自田維術(shù)的博客),,基本說明白了Binder的組成解構(gòu): 圖中的IPC就是進程間通信的意思,。 圖中的 ServiceManager,負責(zé)把 Binder Server注冊到一個容器中,。 有人把 ServiceManager 比喻成電話局,,存儲著每個住宅的座機電話,還是很恰當?shù)?。張三給李四打電話,,撥打電話號碼,會先轉(zhuǎn)接到電話局,,電話局的接線員查到這個電話號碼的地址,,因為李四的電話號碼之前在電話局注冊過,所以就能撥通,;沒注冊,,就會提示該號碼不存在。 對照著 Android Binder 機制,,對著上面這圖,,張三就是 Binder Client,李四就是Binder Server,,電話局就是ServiceManager,,電話局的接線員在這個過程中做了很多事情,對應(yīng)著圖中的Binder驅(qū)動. 3).接下來我們看 Binder 通信的過程,,還是摘自田維術(shù)博客的一張圖: 注:圖中的SM也就是ServiceManager,。 我們看到,Client想要直接調(diào)用Server的add方法,,是不可以的,,因為它們在不同的進程中,,這時候就需要Binder來幫忙了。 首先是Server在SM這個容器中注冊,。其次,,Client想要調(diào)用Server的add方法,就需要先獲取Server對象,, 但是SM不會把真正的Server對象返回給Client,,而是把Server的一個代理對象返回給Client,也就是Proxy,。然后,,Client調(diào)用Proxy的add方法,SM會幫他去調(diào)用Server的add方法,,并把結(jié)果返回給Client,。 以上這3步,Binder驅(qū)動出了很多力,,但我們不需要知道Binder驅(qū)動的底層實現(xiàn),,涉及到C++的代碼了——把有限的時間去做更有意義的事情。 (ps:以上節(jié)選自包建強老師的文章點我點我). 2.為什么 android 選用 Binder 來實現(xiàn)進程間通信,? 1).可靠性,。在移動設(shè)備上,通常采用基于 Client-Server 的通信方式來實現(xiàn)互聯(lián)網(wǎng)與設(shè)備間的內(nèi)部通信,。目前 linux 支持 IPC 包括傳統(tǒng)的管道,,System V IPC,即消息隊列/共享內(nèi)存/信號量,,以及 socket 中只有 socket 支持 Client-Server 的通信方式,。Android 系統(tǒng)為開發(fā)者提供了豐富進程間通信的功能接口,媒體播放,,傳感器,,無線傳輸。這些功能都由不同的 server 來管理,。開發(fā)都只關(guān)心將自己應(yīng)用程序的client與 server 的通信建立起來便可以使用這個服務(wù),。毫無疑問,如若在底層架設(shè)一套協(xié)議來實現(xiàn) Client-Server 通信,,增加了系統(tǒng)的復(fù)雜性,。在資源有限的手機 上來實現(xiàn)這種復(fù)雜的環(huán)境,可靠性難以保證,。 2).傳輸性能,。socket主要用于跨網(wǎng)絡(luò)的進程間通信和本機上進程間的通信,但傳輸效率低,開銷大,。消息隊列和管道采用存儲-轉(zhuǎn)發(fā)方式,,即數(shù)據(jù)先從發(fā)送方緩存區(qū)拷貝到內(nèi)核開辟的一塊緩存區(qū)中,然后從內(nèi)核緩存區(qū)拷貝到接收方緩存區(qū),,其過程至少有兩次拷貝,。雖然共享內(nèi)存無需拷貝,但控制復(fù)雜,。比較各種IPC方式的數(shù)據(jù)拷貝次數(shù),。共享內(nèi)存:0次。Binder:1次,。Socket/管道/消息隊列:2次,。 3).安全性。Android是一個開放式的平臺,,所以確保應(yīng)用程序安全是很重要的,。Android對每一個安裝應(yīng)用都分配了UID/PID,其中進程的UID是可用來鑒別進程身份。傳統(tǒng)的只能由用戶在數(shù)據(jù)包里填寫UID/PID,,這樣不可靠,,容易被惡意程序利用。而我們要求由內(nèi)核來添加可靠的UID,。 所以,出于可靠性,、傳輸性,、安全性。android建立了一套新的進程間通信方式,。 五. Android中進程的級別,,以及各自的區(qū)別。 1,、前臺進程 用戶當前正在做的事情需要這個進程,。如果滿足下面的條件之一,一個進程就被認為是前臺進程: 1).這個進程擁有一個正在與用戶交互的Activity(這個Activity的onResume()方法被調(diào)用),。 2).這個進程擁有一個綁定到正在與用戶交互的activity上的Service,。 3).這個進程擁有一個前臺運行的Service(service調(diào)用了方法startForeground()). 4).這個進程擁有一個正在執(zhí)行其任何一個生命周期回調(diào)方法(onCreate(),onStart(),或onDestroy())的Service。 5).這個進程擁有正在執(zhí)行其onReceive()方法的BroadcastReceiver,。 通常,,在任何時間點,只有很少的前臺進程存在,。它們只有在達到無法調(diào)合的矛盾時才會被殺--如內(nèi)存太小而不能繼續(xù)運行時,。通常,到了這時,設(shè)備就達到了一個內(nèi)存分頁調(diào)度狀態(tài),,所以需要殺一些前臺進程來保證用戶界面的反應(yīng). 2,、可見進程 一個進程不擁有運行于前臺的組件,但是依然能影響用戶所見,。滿足下列條件時,,進程即為可見: 這個進程擁有一個不在前臺但仍可見的Activity(它的onPause()方法被調(diào)用)。當一個前臺activity啟動一個對話框時,,就出了這種情況,。 3、服務(wù)進程 一個可見進程被認為是極其重要的,。并且,,除非只有殺掉它才可以保證所有前臺進程的運行,否則是不能動它的,。這個進程擁有一個綁定到可見activity的Service,。一個進程不在上述兩種之內(nèi),但它運行著一個被startService()所啟動的service,。 盡管一個服務(wù)進程不直接影響用戶所見,,但是它們通常做一些用戶關(guān)心的事情(比如播放音樂或下載數(shù)據(jù)),所以系統(tǒng)不到前臺進程和可見進程活不下去時不會殺它,。 4,、后臺進程 一個進程擁有一個當前不可見的activity(activity的onStop()方法被調(diào)用)。 這樣的進程們不會直接影響到用戶體驗,,所以系統(tǒng)可以在任意時刻殺了它們從而為前臺,、可見、以及服務(wù)進程們提供存儲空間,。通常有很多后臺進程在運行,。它們被保存在一個LRU(最近最少使用)列表中來確保擁有最近剛被看到的activity的進程最后被殺。如果一個activity正確的實現(xiàn)了它的生命周期方法,,并保存了它的當前狀態(tài),,那么殺死它的進程將不會對用戶的可視化體驗造成影響。因為當用戶返回到這個activity時,,這個activity會恢復(fù)它所有的可見狀態(tài),。 5、空進程 一個進程不擁有入何active組件,。保留這類進程的唯一理由是高速緩存,,這樣可以提高下一次一個組件要運行它時的啟動速度。系統(tǒng)經(jīng)常為了平衡在進程高速緩存和底層的內(nèi)核高速緩存之間的整體系統(tǒng)資源而殺死它們,。 六. 線程池的相關(guān)知識,。 Android中的線程池都是之間或間接通過配置ThreadPoolExecutor來實現(xiàn)不同特性的線程池.Android中最常見的四類具有不同特性的線程池分別為FixThreadPool,、CachedThreadPool、SingleThreadPool,、ScheduleThreadExecutor. 1).FixThreadPool 只有核心線程,并且數(shù)量固定的,也不會被回收,所有線程都活動時,因為隊列沒有限制大小,新任務(wù)會等待執(zhí)行. 優(yōu)點:更快的響應(yīng)外界請求. 2).SingleThreadPool 只有一個核心線程,確保所有的任務(wù)都在同一線程中按順序完成.因此不需要處理線程同步的問題. 3).CachedThreadPool 只有非核心線程,最大線程數(shù)非常大,所有線程都活動時,會為新任務(wù)創(chuàng)建新線程,否則會利用空閑線程(60s空閑時間,過了就會被回收,所以線程池中有0個線程的可能)處理任務(wù). 優(yōu)點:任何任務(wù)都會被立即執(zhí)行(任務(wù)隊列SynchronousQueue相當于一個空集合);比較適合執(zhí)行大量的耗時較少的任務(wù). 4).ScheduledThreadPool 核心線程數(shù)固定,非核心線程(閑著沒活干會被立即回收)數(shù)沒有限制. 優(yōu)點:執(zhí)行定時任務(wù)以及有固定周期的重復(fù)任務(wù) 參考Android開發(fā)——Android中常見的4種線程池(保證你能看懂并理解) 七. 內(nèi)存泄露,,怎樣查找,怎么產(chǎn)生的內(nèi)存泄露,。 產(chǎn)生的內(nèi)存泄露 1).資源對象沒關(guān)閉造成的內(nèi)存泄漏 2).構(gòu)造Adapter時,,沒有使用緩存的convertView 3).Bitmap對象不在使用時調(diào)用recycle()釋放內(nèi)存 4).試著使用關(guān)于application的context來替代和activity相關(guān)的context 5).注冊沒取消造成的內(nèi)存泄漏 6).集合中對象沒清理造成的內(nèi)存泄漏 查找內(nèi)存泄漏 查找內(nèi)存泄漏可以使用Android Stdio 自帶的Android Profiler工具,也可以使用Square產(chǎn)品的LeadCanary. 八. Android優(yōu)化 性能優(yōu)化 ????1).節(jié)制的使用Service 如果應(yīng)用程序需要使用Service來執(zhí)行后臺任務(wù)的話,只有當任務(wù)正在執(zhí)行的時候才應(yīng)該讓Service運行起來,。當啟動一個Service時,,系統(tǒng)會傾向于將這個Service所依賴的進程進行保留,系統(tǒng)可以在LRUcache當中緩存的進程數(shù)量也會減少,,導(dǎo)致切換程序的時候耗費更多性能,。我們可以使用IntentService,當后臺任務(wù)執(zhí)行結(jié)束后會自動停止,,避免了Service的內(nèi)存泄漏,。 ????2).當界面不可見時釋放內(nèi)存 當用戶打開了另外一個程序,我們的程序界面已經(jīng)不可見的時候,,我們應(yīng)當將所有和界面相關(guān)的資源進行釋放,。重寫Activity的onTrimMemory()方法,然后在這個方法中監(jiān)聽TRIM_MEMORY_UI_HIDDEN這個級別,,一旦觸發(fā)說明用戶離開了程序,,此時就可以進行資源釋放操作了。 ????3).當內(nèi)存緊張時釋放內(nèi)存 onTrimMemory()方法還有很多種其他類型的回調(diào),,可以在手機內(nèi)存降低的時候及時通知我們,,我們應(yīng)該根據(jù)回調(diào)中傳入的級別來去決定如何釋放應(yīng)用程序的資源。 ????4).避免在Bitmap上浪費內(nèi)存 讀取一個Bitmap圖片的時候,,千萬不要去加載不需要的分辨率??梢詨嚎s圖片等操作,。 ????5).使用優(yōu)化過的數(shù)據(jù)集合 Android提供了一系列優(yōu)化過后的數(shù)據(jù)集合工具類,如SparseArray,、SparseBooleanArray,、LongSparseArray,使用這些API可以讓我們的程序更加高效,。HashMap工具類會相對比較低效,,因為它需要為每一個鍵值對都提供一個對象入口,而SparseArray就避免掉了基本數(shù)據(jù)類型轉(zhuǎn)換成對象數(shù)據(jù)類型的時間,。 布局優(yōu)化 1).重用布局文件 標簽可以允許在一個布局當中引入另一個布局,,那么比如說我們程序的所有界面都有一個公共的部分,,這個時候最好的做法就是將這個公共的部分提取到一個獨立的布局中,然后每個界面的布局文件當中來引用這個公共的布局,。 Tips:如果我們要在標簽中覆寫layout屬性,,必須要將layout_width和layout_height這兩個屬性也進行覆寫,否則覆寫效果將不會生效,。 標簽是作為標簽的一種輔助擴展來使用的,,它的主要作用是為了防止在引用布局文件時引用文件時產(chǎn)生多余的布局嵌套。布局嵌套越多,,解析起來就越耗時,,性能就越差。因此編寫布局文件時應(yīng)該讓嵌套的層數(shù)越少越好,。 舉例:比如在LinearLayout里邊使用一個布局,。里邊又有一個LinearLayout,那么其實就存在了多余的布局嵌套,,使用merge可以解決這個問題,。 2).僅在需要時才加載布局 某個布局當中的元素不是一起顯示出來的,普通情況下只顯示部分常用的元素,,而那些不常用的元素只有在用戶進行特定操作時才會顯示出來,。 舉例:填信息時不是需要全部填的,有一個添加更多字段的選項,,當用戶需要添加其他信息的時候,,才將另外的元素顯示到界面上。用VISIBLE 性能表現(xiàn)一般,,可以用 ViewStub,。ViewStub 也是 View 的一種,但是沒有大小,,沒有繪制功能,,也不參與布局,資源消耗非常低,,可以認為完全不影響性能,。 tips:ViewStub 所加載的布局是不可以使用標簽的,因此這有可能導(dǎo)致加載出來出來的布局存在著多余的嵌套結(jié)構(gòu),。 高性能編碼優(yōu)化 都是一些微優(yōu)化,,在性能方面看不出有什么顯著的提升的。使用合適的算法和數(shù)據(jù)結(jié)構(gòu)是優(yōu)化程序性能的最主要手段,。 1).避免創(chuàng)建不必要的對象 不必要的對象我們應(yīng)該避免創(chuàng)建: 如果有需要拼接的字符串,,那么可以優(yōu)先考慮使用StringBuffer或者StringBuilder來進行拼接,而不是加號連接符,,因為使用加號連接符會創(chuàng)建多余的對象,,拼接的字符串越長,,加號連接符的性能越低。 當一個方法的返回值是String的時候,,通常需要去判斷一下這個String的作用是什么,,如果明確知道調(diào)用方會將返回的String再進行拼接操作的話,可以考慮返回一個StringBuffer對象來代替,,因為這樣可以將一個對象的引用進行返回,,而返回String的話就是創(chuàng)建了一個短生命周期的臨時對象。 盡可能地少創(chuàng)建臨時對象,,越少的對象意味著越少的GC操作,。 2).在沒有特殊原因的情況下,盡量使用基本數(shù)據(jù)類型來代替封裝數(shù)據(jù)類型,,int比Integer要更加有效,,其它數(shù)據(jù)類型也是一樣。 基本數(shù)據(jù)類型的數(shù)組也要優(yōu)于對象數(shù)據(jù)類型的數(shù)組,。另外兩個平行的數(shù)組要比一個封裝好的對象數(shù)組更加高效,,舉個例子,F(xiàn)oo[]和Bar[]這樣的數(shù)組,,使用起來要比Custom(Foo,Bar)[]這樣的一個數(shù)組高效的多,。 3).靜態(tài)優(yōu)于抽象 如果你并不需要訪問一個對系那個中的某些字段,只是想調(diào)用它的某些方法來去完成一項通用的功能,,那么可以將這個方法設(shè)置成靜態(tài)方法,,調(diào)用速度提升15%-20%,同時也不用為了調(diào)用這個方法去專門創(chuàng)建對象了,,也不用擔心調(diào)用這個方法后是否會改變對象的狀態(tài)(靜態(tài)方法無法訪問非靜態(tài)字段),。 4).對常量使用static final修飾符 static int intVal = 42; 編譯器會為上面的代碼生成一個初始方法,稱為方法,,該方法會在定義類第一次被使用的時候調(diào)用,。這個方法會將42的值賦值到intVal當中,從字符串常量表中提取一個引用賦值到strVal上,。當賦值完成后,,我們就可以通過字段搜尋的方式去訪問具體的值了。 final進行優(yōu)化: static final int intVal = 42; 這樣,,定義類就不需要方法了,因為所有的常量都會在dex文件的初始化器當中進行初始化,。當我們調(diào)用intVal時可以直接指向42的值,,而調(diào)用strVal會用一種相對輕量級的字符串常量方式,而不是字段搜尋的方式,。 這種優(yōu)化方式只對基本數(shù)據(jù)類型以及String類型的常量有效,,對于其他數(shù)據(jù)類型的常量是無效的,。 5).使用增強型for循環(huán)語法 static class Counter { zero()最慢,每次都要計算mArray的長度,,one()相對快得多,,two()fangfa在沒有JIT(Just In Time Compiler)的設(shè)備上是運行最快的,而在有JIT的設(shè)備上運行效率和one()方法不相上下,,需要注意這種寫法需要JDK1.5之后才支持,。 Tips:ArrayList手寫的循環(huán)比增強型for循環(huán)更快,其他的集合沒有這種情況,。因此默認情況下使用增強型for循環(huán),,而遍歷ArrayList使用傳統(tǒng)的循環(huán)方式。 6).多使用系統(tǒng)封裝好的API 系統(tǒng)提供不了的Api完成不了我們需要的功能才應(yīng)該自己去寫,,因為使用系統(tǒng)的Api很多時候比我們自己寫的代碼要快得多,,它們的很多功能都是通過底層的匯編模式執(zhí)行的。 舉個例子,,實現(xiàn)數(shù)組拷貝的功能,,使用循環(huán)的方式來對數(shù)組中的每一個元素一一進行賦值當然可行,但是直接使用系統(tǒng)中提供的System.arraycopy()方法會讓執(zhí)行效率快9倍以上,。 7).避免在內(nèi)部調(diào)用Getters/Setters方法 面向?qū)ο笾蟹庋b的思想是不要把類內(nèi)部的字段暴露給外部,,而是提供特定的方法來允許外部操作相應(yīng)類的內(nèi)部字段。但在Android中,,字段搜尋比方法調(diào)用效率高得多,,我們直接訪問某個字段可能要比通過getters方法來去訪問這個字段快3到7倍。但是編寫代碼還是要按照面向?qū)ο笏季S的,,我們應(yīng)該在能優(yōu)化的地方進行優(yōu)化,,比如避免在內(nèi)部調(diào)用getters/setters方法。 九. 插件化相關(guān)技術(shù),,熱修補技術(shù)是怎樣實現(xiàn)的,,和插件化有什么區(qū)別 相同點: 都使用ClassLoader來實現(xiàn)的加載的新的功能類,都可以使用PathClassLoader與DexClassLoader 不同點: 熱修復(fù)因為是為了修復(fù)Bug的,,所以要將新的同名類替代同名的Bug類,,要搶先加載新的類而不是Bug類,所以多做兩件事:在原先的app打包的時候,,阻止相關(guān)類去打上CLASS_ISPREVERIFIED標志,,還有在熱修復(fù)時動態(tài)改變BaseDexClassLoader對象間接引用的dexElements,這樣才能搶先代替Bug類,,完成系統(tǒng)不加載舊的Bug類. 而插件化只是增肌新的功能類或者是資源文件,,所以不涉及搶先加載舊的類這樣的使命,就避過了阻止相關(guān)類去打上CLASS_ISPREVERIFIED標志和還有在熱修復(fù)時動態(tài)改變BaseDexClassLoader對象間接引用的dexElements. 所以插件化比熱修復(fù)簡單,,熱修復(fù)是在插件化的基礎(chǔ)上在進行替舊的Bug類 十. 怎樣計算一張圖片的大小,,加載bitmap過程(怎樣保證不產(chǎn)生內(nèi)存溢出),,二級緩存,LRUCache算法,。 計算一張圖片的大小 圖片占用內(nèi)存的計算公式:圖片高度 * 圖片寬度 * 一個像素占用的內(nèi)存大小.所以,,計算圖片占用內(nèi)存大小的時候,要考慮圖片所在的目錄跟設(shè)備密度,,這兩個因素其實影響的是圖片的高寬,,android會對圖片進行拉升跟壓縮。 加載bitmap過程(怎樣保證不產(chǎn)生內(nèi)存溢出) 由于Android對圖片使用內(nèi)存有限制,,若是加載幾兆的大圖片便內(nèi)存溢出,。Bitmap會將圖片的所有像素(即長x寬)加載到內(nèi)存中,如果圖片分辨率過大,,會直接導(dǎo)致內(nèi)存OOM,,只有在BitmapFactory加載圖片時使用BitmapFactory.Options對相關(guān)參數(shù)進行配置來減少加載的像素。 BitmapFactory.Options相關(guān)參數(shù)詳解 (1).Options.inPreferredConfig值來降低內(nèi)存消耗,。 比如:默認值A(chǔ)RGB_8888改為RGB_565,節(jié)約一半內(nèi)存,。 (2).設(shè)置Options.inSampleSize 縮放比例,對大圖片進行壓縮 ,。 (3).設(shè)置Options.inPurgeable和inInputShareable:讓系統(tǒng)能及時回 收內(nèi)存,。 A:inPurgeable:設(shè)置為True時,表示系統(tǒng)內(nèi)存不足時可以被回 收,,設(shè)置為False時,,表示不能被回收。 B:inInputShareable:設(shè)置是否深拷貝,,與inPurgeable結(jié)合使用,,inPurgeable為false時,該參數(shù)無意義,。 (4).使用decodeStream代替其他方法,。 decodeResource,setImageResource,setImageBitmap等方法 十一. LRUCache算法是怎樣實現(xiàn)的。 內(nèi)部存在一個LinkedHashMap和maxSize,,把最近使用的對象用強引用存儲在 LinkedHashMap中,,給出來put和get方法,每次put圖片時計算緩存中所有圖片總大小,,跟maxSize進行比較,,大于maxSize,就將最久添加的圖片移除,;反之小于maxSize就添加進來,。 之前,我們會使用內(nèi)存緩存技術(shù)實現(xiàn),也就是軟引用或弱引用,,在Android 2.3(APILevel 9)開始,垃圾回收器會更傾向于回收持有軟引用或弱引用的對象,,這讓軟引用和弱引用變得不再可靠,。 算法 m * n的矩陣,能形成幾個正方形(2 * 2能形成1個正方形,,2 * 3 2個,,3 * 3 6個) 計數(shù)的關(guān)鍵是要觀察到任意一個傾斜的正方形必然唯一內(nèi)接于一個非傾斜的正方形,而一個非傾斜的邊長為k的非傾斜正方形,,一條邊上有k-1個內(nèi)點,,每個內(nèi)點恰好確定一個內(nèi)接于其中的傾斜正方形,加上非傾斜正方形本身,,可知,,將邊長為k的非傾斜正方形數(shù)目乘以k,再按k求和即可得到所有正方形的數(shù)目,。 設(shè)2≤n≤m,,k≤n-1,則邊長為k的非傾斜有 (n-k)(m-k)個,,故所有正方形有 ∑(m-k)(n-k)k個 例如m=n=4 正方形有331+222+113=20個 下面是面試過程中遇到的題目 大多數(shù)題目都可以在上面找到答案. 電話面試題 1.ArrayList 和 Hashmap 簡單說一些,區(qū)別,底層的數(shù)據(jù)結(jié)構(gòu). 2.Handler 消息機制 3.引起內(nèi)存泄漏的場景 4.多線程的使用場景? 5.常用的線程池有哪幾種? 6.在公司做了什么?團隊規(guī)模?為什么離職? 面試中實際涉及到的問題 第一輪 1.知道哪些單例模式,寫一個線程安全的單例,并分析為什么是線程安全的? 2.Java中的集合有哪些?解釋一下HashMap?底部的數(shù)據(jù)結(jié)構(gòu)?散列表沖突的處理方法,散列表是一個什么樣的數(shù)據(jù)結(jié)構(gòu)?HashMap是采用什么方法處理沖突的? 3.解釋一下什么是MVP架構(gòu),畫出圖解,一句話解釋MVP和MVC的區(qū)別? 4.Handle消息機制?在使用Handler的時候要注意哪些東西,是否會引起內(nèi)存泄漏?畫一下Handler機制的圖解? 5.是否做過性能優(yōu)化?已經(jīng)采取了哪些措施進行優(yōu)化? 6.引起內(nèi)存泄漏的原因是什么?以及你是怎么解決的? 這些問題應(yīng)該都是比較基礎(chǔ)的問題,每個開發(fā)者都應(yīng)該是非常熟悉并能詳細敘述的.這一輪的面試官問的技術(shù)都是平時用到的. 第二輪 1.關(guān)于并發(fā)理解多少?說幾個并發(fā)的集合? 2.Handler 消息機制圖解? 3.在項目中做了哪些東西? 4.畫圖說明View 事件傳遞機制?并舉一個例子闡述 5.類加載機制,如何換膚,換膚插件中存在的問題?hotfix是否用過,原理是否了解? 6.說說項目中用到了哪些設(shè)計模式,說了一下策略模式和觀察者模式? 7.會JS么?有Hybid開發(fā)經(jīng)驗么? 8.說一下快排的思想?手寫代碼 9.堆有哪些數(shù)據(jù)結(jié)構(gòu)? 對于這輪米那是明顯感覺到壓力,知識的縱向了解也比較深,應(yīng)該是個leader. 第三輪 1.介紹一下在項目中的角色? 2.遇到困難是怎么解決的? 3.如何與人相處,與別人意見相左的時候是怎么解決的,并舉生活中的一個例子. 4.有沒有壓力特別大的時候? 這個應(yīng)該是項目經(jīng)理了,問的問題偏向于生活性格方面. 以上面試中問到的題目基本上都可以在上面找到答案,所以做準備是很重要的,但技術(shù)是一點點積累的,就算你全會背了,面試過了,真正等到工作的時候還是會捉襟見肘的,所以踏實點吧騷年.
|
|
來自: 過河卒沖 > 《技術(shù)管理》