Java知識分享網 - 輕松學習從此開始!????

Java知識分享網

Java1234官方群24:java1234官方群24
Java1234官方群24:791563025
     

Mycat實現mysql高可用集群視頻教程免費領取

畢設代做,包查重聯系人QQ:1982956321畢設大神

領取國內優秀就業,加薪,跳槽項目課程源碼-vue2+jwt+springboot+mybaits前后端分離通訊錄系統課程

SpringBoot打造企業級進銷存

Java1234 VIP課程

領取微信掃碼登錄Java實現視頻教程

Java1234至尊VIP(特價活動)

2020年今日頭條面試題 PDF 下載


分享到:
時間:2020-03-29 20:51來源:http://www.luygg.com 作者:小鋒  侵權舉報
2020年今日頭條面試題 PDF 下載
失效鏈接處理
2020年今日頭條面試題 PDF 下載

下載地址:

提取碼:czjf

相關截圖:


主要內容:

今?頭條?試
?我介紹:?試官您好,我是劉世麟,?常榮幸能參加貴公司的?試,下?我簡單介紹?下我的個?
情況:我從實習到現在?直在致學教育?作,從事 Android 開發,憑借良好的?作能?和溝通能?,
連續兩年蟬聯「優秀員?」稱號,在今年初被公司內聘為技術總監助理,協助技術總監開展部?管理
和項?推動?作。在?作之外,我喜歡編寫技術博客和在 GitHub 上貢獻開源代碼,?前在 GitHub
上總共擁有 7k 左右的 Star,數篇技術博客也有數?萬閱讀。我?常地熱愛移動開發,早已久仰貴團
隊對技術的看?,所以希望今天???試有好的表現,未來能有幸與您共事。
你有什么要問我的嗎?
對新?公司的員?有沒有什么培訓項??
?職后參與的項?是怎樣的?
Java 篇
HashMap 的內部結構?內部原理?和 HashTable 的區別,假如發?了 hash 碰
撞,如何設計能讓遍歷效率??
HashMap 基于 Map 實現,允許 null 的鍵值,不保證插?順序,也不保證序列不隨時間?變化。其
內部是使??個默認容量為 16 的數組來存儲數據的,?數組中每?個元素卻?是?個鏈表的頭結
點。所以,更加確切的說,HashMap 內部存儲結構是使?哈希表的拉鏈結構(數組 + 鏈表),這種
存儲數據的?法叫做拉鏈法。?鏈表中的每個結點都是 Entry 類型,? Entry 存儲的內容包含
key、value、hash 和 next。
?作原理:主要是通過 hash 的?法,通過 put 和 get 來存取對象。
存取對象時,我們將 key-value 傳給 put() 時,它通過 hashCode() 計算 hash 從?得到桶
(bucket)的位置,進?步存儲。HashMap 會根據當前桶(bucket)的占?情況來?動調整容
量(超過負載因? Load Factor 則 resize() 為原來的 2 倍);
獲取對象時,我們將 key 傳遞給 get() ,它調? hashCode() 計算 hash 從?得到桶
(bucket)的位置,并進?步通過 equals() 確認鍵值對。如果發?碰撞的時候,HashMap
將會通過鏈表把產?碰撞沖突的元素組織起來。在 Java 8 中,如果?個桶(bucket)中碰撞沖
突的元素超過某個限制(默認是 8),則使?紅?樹來替換鏈表,從?提?速度。
什么是紅?樹?
紅?樹本質上就是?種?叉查找樹(?叉查找樹的插?、刪除、查找最好情況為 O(logn),但極
端的斜樹為 O(n)),但它在?叉查找樹的基礎上額外添加了?個顏?做標記,同時具有?定的規
則,這些規則讓紅?樹保證了?種平衡,插?、刪除、查找的最壞時間復雜度都是 O(logn)。
性質:
任何?個結點都有顏?,??或者紅?;
根結點是??的;
??結點之間不能出現兩個連續的紅結點;
任何?個結點向下遍歷到其?孫的葉?結點,所經歷的?結點個數必須相等;
空節點被認為是??的;
HashMap 和 HashTable 雖然都實現了 Map 接?,但 HashTable 的實現是基于 Dictionary 抽
象類。?且 HashMap 中可以把 null 作為鍵值,所以 HashMap 判斷是否含有某個鍵是?
containsKey() ?不是 get() 。 get() ?法返回 null 的時候,并不能判斷是沒有鍵,也可能是
這個鍵對應的值為 null。但 HashTable 是不允許的。還有?個區別就是 HashMap 是?同步的,在
多線程中需要?動同步,? HashTable 是同步的,可以直接?在多線程中。
但實際上,我們在多線程的時候,更加?睞于使? ConcurrentHashMap ?不是 HashTable 。因
為 HashTable 使? synchronized 來做線程安全,全局只有?把鎖,直接鎖住整個 Hash 表,?
ConcurrentHashMap 是?次鎖?個桶。在線程競爭激烈的情況下 HashTable 效率是?常低下
的。但即便如此,我們也不能說 ConcurrentHashMap 就可以完全替代 HashTable 。根本在于
HashTable 的迭代器是強?致性的,? ConcurrentHashMap 是弱?致性的。
ConcurrentHashMap 不允許 key 或者 value 為 null。
對于「強?致性」和「弱?致性」的理解:?如我們往 ConcurrentHashMap 底層數據結構加
??個元素后,get 可能在某段時間內還看不到這個元素。
講講 ConcurrentHashMap。
由于 HashMap 是?個線程不安全的容器,主要體現在容量?于 總量*負載因? 發?擴容時會出現環
形鏈表從?導致死循環。
因此需要?持線程安全的并發容器 ConcurrentHashMap 。 在 JDK 1.7 中,ConcurrentHashMap 仍然是數組加鏈表,和 HashMap 不?樣的是,
ConcurrentHashMap 最外層并不是?個?的數組,?是?個 Segment 的數組,每?個 Segment 包
含?個和 HashMap 數據結構差不多的鏈表數組。
ConcurrentHashMap 采?了分段鎖的技術,Segment 繼承于 ReentrantLock,所以我們可以很?便
的對每?個 Segment 上鎖。不會像 HashTable 那樣不管是 put 還是 get 操作都需要做同步處理,?
個線程占?鎖訪問?個 Segment 時,根本不會影響到其他的 Segment。 在 ConcurrentHashMap 的 get ?法中,?常?效,因為全程不需要加鎖。只需要將 key 通過 hash
之后定位到具體的 Segment,再通過?次 hash 定位到具體的元素上。由于 HashEntry 中的 value
屬性是? volatile 關鍵字修飾的,保證了內存可?性,所以每次獲取到的值都是最新值。
class Node<T>{
 public T value;
 public Node<T> parent;
 public boolean isRed;
 public Node<T> left;
 public Node<T> right; }
雖然對 HashEntry 的 value 采?了volatile 關鍵字修飾,但并不能保證并發的原?性,所以 put 操作
時仍然需要加鎖處理。?先是通過 key 的 hash 定位到具體的 Segment,在 put 之前會進??次擴容
校驗。這?? HashMap 要好的?點是:HashMap 是插?元素之后在看是否需要擴容,有可能擴容
之后后續就沒有插?就浪費了本次擴容,? HashMap 的擴容是?常消耗性能的。?
ConcurrentHashMap 不?樣,它是先將數據插?之后再檢查是否需要擴容,之后再做插?。
?在 JDK 1.8 中,拋棄了原有的 Segment 分段鎖,?采?了 CAS + synchronized 來保證并發安全
性。并把 1.7 中存放數據的 HashEntry 改為了 Node,但作?還是相同的。其中 value 和 next 均?
volatile 保證可?性。
JVM 虛擬機內存結構,以及它們的作?
JVM 內存結構主要由三?塊:堆內存、?法區和棧。
每個線程包含?個棧區,棧中只包含基本數據類型和對象的引?,?且每個棧中的數據都是私有的,
其他棧不允許訪問。此外,還會存放?法的形式參數和引?對象的地址,在使?完后,棧空間會?即
回收,堆空間等待 GC。
堆主要?于存放對象,同時也是垃圾收集器管理的主要區域。每個對象會包含?個與之對應的 class
信息,JVM 只有?個堆區(heap)被所有線程共享,堆區不存放基本數據類型和對象引?,只存放對
象本身。
??法區主要?于存放線程所執?的字節碼指令和常量池,會被所有線程共享,?法區包含所有的
class 和 static 變量。
講講 JVM 的類加載過程 && 雙親委派模型
JVM 的類加載過程分為加載、驗證、準備、解析、初始化 5 個階段。
加載:加載階段由類加載器進?負責,類加載器根據?個類的全限定名讀取該類的?進制字節流
到 JVM 內部,然后轉換為?個對應的 java.lang.Class 對象實例;?個類由類加載器和類本身?
起確定,所以不同類加載器加載同?個類得到的 java.lang.Class 也是不同的。
驗證:驗證階段負責驗證類數據信息是否符合 JVM 規范,是否是?個有效的字節碼?件;
準備:準備階段負責為類中的 static 變量分配空間,并初始化(與程序?關、系統初始化);
解析:解析階段負責將常量池中所有的符號引?轉換為直接引?;
初始化:初始化階段負責將所有的 static 域按照程序指定操作對應執?(賦值 static 變量,執?
static 塊)。
上述階段通常都是交叉混合允許,沒有嚴格的先后執?順序。
雙親委派過程:當?個類加載器收到類加載任務的時候,?即將任務委派給它的?類加載器去執?,
直到委派給最頂層的啟動類加載器為?。如果?類加載器?法加載委派給它的類時,將類加載任務回
退到它的下?級加載器去執?。除了啟動類加載器以外,每個類加載器擁有?個?類加載器,?戶的
?定義類加載器的?類加載器是 AppClassLoader。雙親委派模型可以保證全限名指定的類,只被加
載?次。雙親委派模型不具有強制性約束,是 Java 設計者推薦的類加載器實現?式。
采?雙親委派模型的原因:?如?客定義?個 java.lang.String 類,該 String 類和系統 String 類有?
樣的功能,只是在某個?法?如 equels() 中加?了病毒代碼,并且通過?定義類加載器加? JVM
中,如果沒有雙親委派模型,那么 JVM 就可能誤以為?客編寫的 String 類是系統 String 類,導致
「病毒代碼」最終被執?。?有了雙親委派模型,?客定義的 java.lang.String 類就?于不會被加載
進內存,因為最頂端的類加載器會加載系統的 String 類,最終?定義的類加載器?法加載
java.lang.String 類。
可以通過?寫 loadClass() ?法,打破雙親委派模型。
談談 Java 的 垃圾回收算法
?先我們肯定得需要確定哪些是活著的對象,哪些是可以回收的。
引?計數算法:它是判斷對象是否存活的基本算法:給每個對象添加?個引?計數器,每當?個
地?引?它的時候,計數器就加 1;當引?失效后,計數器值就減 1。但是這種?法有?個致命
的缺陷:當兩個對象相互引?時會導致這兩個對象都?法被回收。
根搜索算法:但?前主流商?語?都采?根搜索算法來判斷對象是否存活。對于程序來說,根對
象總是可以被訪問的,從這些根對象開始,任何可以被觸及的對象都被認為是「活著的」對象,
?法觸及的對象被認為是垃圾,需要被回收。
對于垃圾回收算法,主要包含標記-清除算法、復制回收算法、標記-整理算法和分代回收算法。
標記-清除算法:?先使?根搜索算法標記出所有需要回收的對象,標記完成后統?回收所有被
標記的對象。但有兩個缺點:
效率問題:標記和清除的效率都不?;
空間問題:標記清除后會產??量不連續的內存碎?;
復制回收算法:將可?內存分為??相等的兩份,在同?時刻只能使?其中的?份。當其中?份
內存使?完了,就把還存活的對象復制到另?份內存上,然后將這?份上的內存情況。復制回收
算法能有效地避免內存碎?,但是算法需要把內存?分為?,導致內存使?率??降低。
標記-整理算法:復制算法在對象存活率較?的情況下會復制很多的對象,效率會很低。標記-整
理算法就解決了這樣的問題,同樣采?的是根搜索算法進?存活對象標記,但后續是將所有存活
的對象都移動到內存的?端,然后清理掉端外界的對象。
分代回收算法:在 JVM 中不同的對象擁有不同的?命周期,因此對于不同?命周期的對象也可
以采?不同的垃圾回收?法,以提?效率,這就是分代回收算法的核?思想。
在不進?對象存活時間區分的情況下,每次垃圾回收都是對整個堆空間進?回收,花費的時間相
對會?。同時,因為每次回收都需要遍歷所有存活對象,但實際上,對于?命周期?的對象?
?,這種遍歷是沒有效果的,因為可能進?了很多次遍歷,但是他們依舊存在。因此,分代垃圾
回收采?分治的思想,進?代的劃分,把不同?命周期的對象放在不同代上,不同代上采?最適
合它的垃圾回收?式進?回收。
JVM 中共分為三個代:新?代、?年代和持久代。其中持久代主要存放的是 Java 類的類信息,
與垃圾收集要收集的 Java 對象關系不?。
新?代:所有新?成的對象?先都是放在新?代的,新?代采?復制回收算法。新?代的
?標就是盡可能快速地收集掉那些?命周期短的對象。新?代按照 8:1 的?例分為?個
Eden 區和兩個 Survivor 區。?部分對象在 Eden 區?成,當 Eden 區滿時,還存活的對
象將被復制到其中的?個 Survivor 區,當這個 Survivor 區滿時,此區的存活對象將被復
制到另外?個 Survivor 區,當另?個 Survivor 區也滿了的時候,從第?個 Survivor 區
復制過來的并且此時還存活的對象,將被復制到了「年?區」。需要注意,Survivor 的兩
個區是對稱的,沒有任何的先后關系,所以同?個區中可能同時存在 Eden 復制過來的對
象,和從前?個 Survivor 區復制過來的對象,?復制到年?區的只有從第?個 Survivor 區
過來的對象,?且,Survivor 區總有?個是空的。
?年代:在新?代中經歷了 N 次垃圾回收后仍然存活的對象,就會被放到?年代中,?年
代采?標記整理回收算法。因此,可以認為?年代中存放的都是?些?命周期較?的對
象。
持久代:?于存放靜態?件,如 final 常量、static 常量、常量池等。持久代對垃圾回收沒
有顯著影響,但有些應?可能動態?成或者調??些 class。在這種時候需要設置?個?較
?的持久代空間來存放這些運?過程中新增的類。
談談 Java 垃圾回收的觸發條件
Java 垃圾回收包含兩種類型:Scavenge GC 和 Full GC。
Scavenge Gc:?般情況下,當新對象?成,并且在 Eden 申請空間失敗的時候,就會觸發
Scavenge GC,對 Eden 區進? GC,清除?存活的對象,并且把尚且存活的對象移動到
Survivor 區,然后整理 Survivor 的兩個區。這種?式的 GC 是對新?代的 Eden 區進?,不會
影響到?年代。因為?部分對象都是從 Eden 區開始的,同時 Eden 區不會分配的很?,所以
Eden 區的 GC 會頻繁進?。
Full GC:Full GC 將會對整個堆進?整理,包括新?代、?年代和持久代。Full GC 因為需要對
整個堆進?回收,所以? Scavenge GC 要慢,因此應該盡量減少 Full GC 的次數。在對 JVM 調
優的過程中,很??部分?作就是對 Full GC 的調節,有如下原因可能導致 Full GC:
?年代被寫滿;
持久代被寫滿;
System.gc() 被顯示調?;
synchronized 和 Lock 的區別
1. 使??法的區別:
synchronized:在需要同步的對象中加?此控制, synchronized 可與加在?法上,也可
以加在特定代碼塊中,括號中表示需要加鎖的對象。
Lock:需要顯示指定起始位置和終?位置。?般使? ReentrantLock 類作為鎖,多個線
程中必須要使??個 ReentrantLock 類作為對象才能保證鎖的?效。且在加鎖和解鎖處
需要通過 lock() 和 unlock() 顯式指出。所以?般會在 finally 塊中寫
unlock() 以防死鎖。
2. 性能的區別:
synchronized 是托管給 JVM 執?的,? lock 是 Java 寫的控制鎖的代碼。在 Java 1.5
中, synchronized 是性能低下的。因為這是?個?量級操作,需要調?操作接?,導致有可
能加鎖消耗的系統時間?鎖以外的操作還多。相?下使? Java 提供的 Lock 對象,性能更低?
些。但是到了 Java 1.6,發?了變化。 synchronized 在語義上很清晰,可以進?很多優化,
有適應?旋、鎖消除、鎖粗化、輕量級鎖、偏向鎖等,導致在 Java 1.6 上 synchronized 的性
能并不? Lock 差。
synchronized:采?的是 CPU 悲觀鎖機制,即線程獲得的是獨占鎖。獨占鎖就意味著 其
他線程只能依靠阻塞來等待線程釋放所。?在 CPU 轉換線程阻塞時會引起線程上下?切
換,當有很多線程競爭鎖的時候,會引起 CPU 頻繁的上下?切換導致效率很低。
Lock:采?的是樂觀鎖的?式。所謂樂觀鎖就是:每次不加鎖?是假設沒有沖突?去完成
某項操作,如果因為沖突失敗就重試,直到成功為?。樂觀鎖實現的機制就是 CAS 操作。
我們可以進?步研究 ReentrantLock 的源代碼,會發現其中?較?要的獲得鎖的?個?
法是 compareAndSetState 。這?其實就是調?的 CPU 提供的特殊指令。
3. ReentrantLock :具有更好的可伸縮性:?如時間鎖等候、可中斷鎖等候、?塊結構鎖、多個
條件變量或者鎖投票。
volatile 的作?,為什么會出現變量讀取不?致的情況,與 synchronized 的區
別;
volatile 修飾的變量具有可?性
volatile 是變量修飾符,它修飾的變量具有可?性,Java 中為了加快程序的運?效率,對?些變
量的操作通常是在該線程的寄存器或是 CPU 緩存上進?的,之后才會同步到主存中,?加了
volatile 修飾符的變量則是直接讀取主存,保證了讀取到的數據?定是最新的。
volatile 禁?指令?排
指令?排是指處理器為了提?程序效率,可能對輸?代碼進?優化,它不保證各個語句的執?順
序同代碼中的順序?致,但是它會保證程序最終執?結果和代碼順序執?的結果是?致的。指令
?排序不會影響單個線程的執?,但是會影響到線程并發執?的正確性。
? synchronized 可?作于?段代碼或?法,既可以保證可?性,?能夠保證原?性。
在性能??,synchronized 關鍵字是防?多個線程同時執??段代碼,會影響程序執?效率,?
volatile 關鍵字在某些情況下性能要優于 synchronized。
可?性是指多個線程訪問同?個變量時,?個線程修改了這個變量的值,其他線程能夠?即看
到修改的值。
指令?排序:?般來說,處理器為了提供程序運?效率,可以會對輸?代碼進?優化,它不保
證程序中各個語句的執?先后順序同代碼中的順序?致,但是它會保證程序最終執?結果和代
碼順序執?的結果是?致的。它不會影響到單線程的執?,卻會影響到多線程的并發執?。
++i 在多線程環境下是否存在問題,怎么解決?
雖然遞增操作 ++i 是?種緊湊的語法,使其看上去只是?個操作,但這個操作并?源?的。因為它并
不能作為?個不可分割的操作來執?。實際上,它包含了 3 個獨?的操作:讀取 i 的值,將值加 1,
然后將計算結果返回給 i。這是?個「讀取-修改-寫?」的操作序列,并且其結果狀態依賴于之前的狀
態,所以在多線程環境下存在問題。
要解決?增操作在多線程下線程不安全的問題,可以選擇使? Java 提供的原?類,如 AtomicInteger
或者使? synchronized 同步?法。
原?性:在 java 中,對基本數據類型的變量的讀取和賦值操作是原?性操作,即這些操作是不
可被中斷的,要么執?,要么不執?。也就是說,只有簡單的讀取、賦值(?且必須是將數字
賦值給某個變量)才是原?操作。(變量之間的相互賦值不是原?操作,?如 y = x,實際上是
先讀取 x 的值,再把讀取到的值賦值給 y 寫??作內存)
Thread.sleep() 和 Thread.yield() 區別
sleep() 和 yield() 都會釋放 CPU。
sleep() 使當前線程進?停滯狀態,所以執? sleep() 的線程在指定的時間內肯定不會執
?; yield() 只是使當前線程?新回到可執?狀態,所以執? yield() 的線程有可能在進?到可
執?狀態后?上?被執?。
sleep() 可使優先級低的線程得到執?的機會,當然也可以讓同優先級和?優先級的線程有執?的
機會; yield() 只能使同優先級的線程有執?的機會。
講講常?的容器類
List 接?實現:允許數據?復
1. ArrayList:實現 List 接?,內部由數組實現,可以插? null,元素可?復,線程不安全,
訪問元素快;空間不?的時候增?率為當前?度的 50%。
2. Vector:ArrayList 的線程安全寫法,由于?持多線程,所以性能? ArrayList 較低;空間
不?的時候增?率為當前?度的 100%。它的同步是通過 Iterator ?法加 synchronized 實
現的。
3. Stack:線程同步,繼承? Vector,添加了?個?法來完成棧的功能。
4. LinkList:實現 List 接?,內部實現是雙向鏈表,擅?插?刪除,元素可?復;線程不同
步。
Set 接?實現:不允許數據?復,最多允許?個 null 元素。
1. HashSet:實現 Set 接?,線程不同步,不允許?復元素,基于 HashMap 存儲,遍歷時不
保證順序,并且不保證下次遍歷順序和之前?樣,允許 null 元素;
2. LinkedHashSet:繼承? HashSet,不允許?復元素,可以保持順序的 Set 集合,基于
LinkedHashMap 實現;
3. TreeSet:線程不同步,不允許?復元素,保持元素??次序的集合,??的元素必須實現
Comparable 接?,源碼算法基于 TreeMap;
4. EnumSet:線程不同步,內部使? Enum 數組實現,速度? HashSet 快。只能存儲在構造
函數傳?的枚舉類的枚舉值。
Map
1. HashMap:線程不沖突。根據 key 的 hashcode 進?存儲,內部使?靜態內部類 Node 的
數組進?存儲,默認初始??為 16,每次擴??倍。當發? hash 沖突時,采?拉鏈法存
儲。可以接受 null 的 key 和 value。在 JDK 1.8 中,當單個桶中元素?于等于 8 時,鏈表
實現改為紅?樹實現;當元素個數?于 6 時,變回鏈表實現,由此來防? hashCode 攻
擊。
2. LinkedHashMap:繼承? HashMap,相對 HashMap 來說,遍歷的時候具有順序,可以
保證插?的順序,存儲?式和 HashMap ?樣,采?哈表表?法存儲,不過
LinkedHashMap 多維護了?份上下指針;?多數情況下遍歷速度? HashMap 慢,不過有
種情況例外,當 HashMap 容量很?,實際數據較少的時候,遍歷起來可能會?
LinkedHashMap 慢,因為 LinkedHashMap 的遍歷速度只和實際數據有關,和容量?關,
? HashMap 的遍歷速度和它的容量有關。
3. TreeMap:線程不同步,基于紅?樹的 NavigableMap 實現,能夠把它保存的記錄根據鍵
排序,默認是按鍵值的升序排序,也可以指定排序的?較器,當? Iterator 遍歷
TreeMap 時,得到的記錄是排過序的。
4. HashTable:線程安全,不能存儲 null 的 key 和 value。
如何去除 ArrayList 的重復元素?
直接采? HashSet 即可。作為它的參數,然后再 addAll。但這種?式不能保證原來的順序,如果要
求順序,可以使? LinkedHashSet 即可。
講講 Java 的泛型擦除,泛型主要是為了解決什么問題?如何?泛型做 Json 的解
析的?
泛型信息只存在于代碼編譯階段,在進? JVM 之前,與泛型相關的信息會被擦除掉,這樣的現象就叫
泛型擦除。
泛型的最?作?是提升代碼的安全性和可讀性。如果沒有泛型,我們只能采? Object 來實現參數的
任意化,這樣在使?的時候需要進?類型強轉,?且這樣即使錯誤了,也不會在編譯時就提示錯誤。
?有了泛型就可以很好的解決這個問題,在編譯時就會出現編譯不通過的現象。?泛型提升代碼效率
的優點也顯?易?,如果沒有泛型,解決前?的問題只能通過編寫多個 set 和 get ?法來處理,但有
了泛型便只需要?個 get 和?個 set ?法就可以處理。
既然說到了「泛型擦除」的概念,就不得不提到?個經典的現象,我們假設兩個 ArrayList,?個??
存放 Integer 類型,?個??存放 String 類型,但調?他們的 getClass() ?法卻是相等的。因為
在 JVM 中它們的 Class 都是 List.class。?我們在做 Json 解析的時候,?定會遇到有的數據是
JsonObject,有個數據是 JsonArray 的情況。我們正常使? gson 做解析的時候,需要對象.class 做參
數傳?,所以我們必須要知道 List ??存放的數據類型。這時候我們就可以通過「反射」的機制來獲
取??的泛型信息了。主要?式是采?
((ParameterizedType)getClass().getGenericSuperclass()).getActualTypeArguments(
) 拿到裝載的泛型類的真實類型數組,拿到真實類型后我們便可以采? gson 進? Json 解析了。
談談 Java 的 Error 和 Exception 的區別聯系。
Error 和 Exception 均集成? Throwable,但 Error ?般指的是和虛擬機相關的問題,?如系統崩
潰,虛擬機錯誤,OOM 等,遇到這樣的錯誤,程序應該被終?。? Exception 表示程序可以處理的
異常,可以捕獲并且可能恢復。
軟引?和弱引?的區別?
只具有弱引?的對象擁有更短暫的?命周期,可能隨時被回收(主要發?在每次 GC)。?只具有軟引?
的對象只有在內存吃緊的時候才會被回收(主要在 OOM 之前),在內存?夠的時候,通常不被回
收。
?如我們經常去讀取?些圖?,由于讀取?件需要硬件操作,速度較慢,從?導致性能較低。所以我
們可以考慮把這些?件緩存起來,需要的時候直接從內存讀取。但由于圖?占?內存空間較?,?較
容易發? OOM,所以我們可以考慮軟引?來避免這個問題發?。
成員變量和靜態?法可以被重寫么?重寫的規則是怎樣的?
?類?寫?類的?法,只有實例?法可以被重寫,?寫后的?法必須仍為實例?法。成員變量和靜態
?法都不能被重寫,只能被隱藏。(形式上可以寫,但本質上并不是?寫,?是屬于隱藏)
?法的?寫(override)遵循兩同兩???原則:
?法名必須相同,參數類型必須相同。
?類返回的類型必須?于或者等于?類?法返回的類型。
?類拋出的異常必須?于或者等于?類?法拋出的異常。
?類訪問的權限必須?于或者等于?類?法的訪問權限。
?寫?法可以改變其他的?法修飾符,?如 final,synchronized,native 等。
內部類訪問局部變量的時候,為什么變量必須加上 final 修飾符?
因為?命周期不同。局部變量在?法結束后就會被銷毀,但內部類對象并不?定,這樣就會導致內部
類引?了?個不存在的變量。所以編譯器會在內部類中?成?個局部變量的拷?,這個拷?變量的?
命周期和內部類對象相同,就不會出現上述問題。但這樣就導致了其中?個變量被修改,兩個變量值
可能不同的問題。為了解決這個問題,編譯器就要求局部變量需要被 final 修飾,以保證兩個變量值
相同。
Android 篇
Android 為什么推薦使? ArrayMap 和 SparseArray? 在 Java 中,我們通常會采? HashMap 來存儲 K-V 數據類型,然后在 Android Studio 中這樣使?卻
經常會得到?個警告,提示使? ArrayMap 或者 SparseArray 做替代。
HashMap 基本上就是?個 HashMap.Entry 的數組,更準確地說,Entry 類中包含以下字段:
?個?基本數據類型的 key
?個?基本數據類型的 value
保存對象的哈希值
指向下?個 Entry 的指針
當有鍵值對插?時,HashMap 會發?什么 ?
?先,鍵的哈希值被計算出來,然后這個值會賦給 Entry 類中對應的 hashCode 變量。
然后,使?這個哈希值找到它將要被存?的數組中“桶”的索引。
如果該位置的 “桶” 中已經有?個元素,那么新的元素會被插?到 “桶” 的頭部,next 指向上?個
元素——本質上使“桶”形成鏈表。
現在,當你? key 去查詢值時,時間復雜度是 O(1)。
雖然時間上 HashMap 更快,但同時它也花費了更多的內存空間。
所以它會有嚴?的缺點:
?動裝箱的存在意味著每?次插?都會有額外的對象創建,這跟垃圾回收機制?樣也會影響到內
存的利?;
HashMap.Entry 對象本身是?層額外需要被創建以及被垃圾回收的對象;
「桶」在 HashMap 每次被壓縮或者擴容的時候都會被?新安排,這個操作會隨著對象數量的增
??變得開銷極?。
? ArrayMap 和 SparseArray 更加考慮內存優化,它們內部均采?兩個數組進?數據存儲。
在 ArrayMap 中,內部的數組?個?于記錄 key 的 hash 值,另外?個數組?于記錄 value 值,這樣
既能避免為每個存? map 中的鍵創建額外的對象,還能更積極地控制這些數組?度的增加。因為增
加?度只需拷?數組中的鍵,?不是?新構建?個哈希表。
當插??個鍵值對時:
鍵/值被?動裝箱。
鍵對象被插?到 mArray[] 數組中的下?個空閑位置。
值對象也會被插?到 mArray[] 數組中與鍵對象相鄰的位置。
鍵的哈希值會被計算出來并被插?到 mHashes[] 數組中的下?個空閑位置。
對于查找?個 key :
鍵的哈希值先被計算出來
在 mHashes[] 數組中?分查找此哈希值。這表明查找的時間復雜度增加到了 O(logN)。
?旦得到了哈希值所對應的索引 index,鍵值對中的鍵就存儲在 mArray[2index] ,值存儲在
mArray[2index+1]。
這?的時間復雜度從 O(1) 上升到 O(logN),但是內存效率提升了。當我們在 100 左右的數據量
范圍內嘗試時,沒有耗時的問題,察覺不到時間上的差異,但我們應?的內存效率獲得了提?。
需要注意的是,ArrayMap 并不適?于可能含有?量條?的數據類型。它通常? HashMap 要慢,因
為在查找時需要進??分查找,增加或刪除時,需要在數組中插?或刪除鍵。對于?個最多含有?百
條?的容器來說,它們的性能差異并不巨?,相差不到 50%。
Bitmap 加載發? OOM 怎么辦?怎么處理。
在 Android 開發中,Bitmap 的加載通常采? BitmapFactory.decodeXXX() ,但這些?法在構造
Bitmap 的時候就開始分配內存,所以很容易造成 OOM,解決?案也很簡單,只需要對
BitmapFactory.Options 定義屬性后進?壓縮即可。需要設置 inJustDecodeBounds 屬性為
true,來阻?解析時分配內存,此時解析返回 null,但是卻可以拿到圖?的寬?等信息。這個時候可
以根據??的需求通過計算 inSampleSize 的值,之后把 inJustDecodeBounds 屬性設置為
false 后再解析?次即可。
有了解過 IntentService 么?
IntentService 作為 Service 的?類,默認給我們開啟了?個?作線程執?耗時任務,并且執?
完任務后,會?動停?服務。拓展 IntentService 也?較簡單,提供?個構造?法和實現
onHandleIntent() ?法就是了,不需要?寫?類的其他?法。但如果要綁定服務的話,還是要?
寫 onBind() 返回?個 IBinder 的。使? Service 可以同時執?多個請求,?使?
IntentService 只能同時執??個請求。
詳情可點擊:?試:Android Service,你真的了解了么?
Activity 的?種啟動模式有了解么?各?的使?場景?
詳情可點擊:?試:說說 Activity 的啟動模式
standard
Android 默認的啟動模式,每次 start 都會新建實例,適?于?多數場景。
singleTop
如果 start 的 Activity 剛好在棧頂,則不會新建實例,直接調? onNewIntent() 。使?場景:
資訊類內容??。
singleTask
調? start 啟動的 Activity 只要在當前 Activity 棧??存在,則直接調? onNewIntent() ,并
把上?的所有實例全部移除。使?場景:APP 的主??。
singleInstance
Activity 棧??只會存在?個實例,每次啟動都會直接調? onNewIntent() 。適?于?如接電
話,鬧鐘。
Looper.prepare() 和 Looper.loop() ?法分別做了什么?
Looper.prepare():?先從 ThreadLocal 中獲取?個 Looper ,如果沒有則向
ThreadLocal 中添加?個新的 Looper ,同時新建?個 MessageQueue 。主線程的
Looper 在 ActivityThread 中創建。
ThreadLoacl 是 Java 提供的?于保存同?進程中不同線程數據的?種機制。每個線程中
都保有?個 ThreadLocalMap 的成員變量, ThreadLoaclMap 內部采?
WeakRefrence 數組保存,數組的 key 即為 ThreadLocal 內部的 hash 值。通常情況
下,我們創建的變量是可以被任何?個線程訪問并修改的,但 ThreadLocal 創建的變量
只能被當前線程訪問,其他線程?法訪問和修改。Handler 正是利?它的這?特性,來做
到每個線程都有??獨有的 Looper。
ActivityThread 是 Android 應?的主線程,在 Application 進程中管理執?主線程,
調度和執?活動和?播,和活動管理請求的其他操作。
Looper.loop():循環使? MessageQueue.next() 來獲取消息,該函數在 MessageQueue 中
沒有消息的時候會阻塞,這?采?了 epoll 的 I/O 多路復?機制,當獲取到?個消息的時候會
返回。
講講 LruCache,除此之外,你還知道哪些緩存算法?
LruCache 中維護了?個集合 LinkedHashMap ,該 LinkedHashMap 是以訪問順序排序的。當調?
put() ?法時,就會在結合中添加元素,并調? trimToSize() 判斷緩存是否已滿,如果滿了就
? LinkedHashMap 的迭代器刪除隊尾元素,即近期最少訪問的元素。當調? get() ?法訪問緩
存對象時,就會調? LinkedHashMap 的 get() ?法獲得對應集合元素,同時會更新該元素到隊
頭。
除了我們常?的 LRU 緩存,實際上我們在近來停更的 ImageLoader 庫??可以看到其他的緩存算
法,?如根據緩存對象被使?的頻率來處理的 LFU 算法;?如根據給對象設置失效期的 Simle time?based 算法;?如超過指定緩存,就移除棧內最?內存的緩存對象的 LargestLimitedMemoryCache
算法。
你是如何進? APK 瘦身的?
?先我們得知道 APK ?件??都是由哪些?件構成的,通過 APK Analyzer 我們可以得知,APK 占?
最?的兩塊是 lib 和 res。
在滿?要求的情況下,我們可以指定更少的 so 庫進?優化,通常情況下直接指定 armeabi 或者
armeabi-v7a 就可以了。
對于圖?,我們可以通過 ImageOptim 進?圖?壓縮,雖然官?提供了 shrinkResources 設置
項,但由于該項設置有?險就沒有處理。
對于 dex ?件,刪除了不少??庫(這些庫都是早期為了兼容低版本?機)
簡述 Android 事件傳遞機制,什么時候會觸發 ACTION_CANCLE。
詳情可點擊:?試:從源碼的?度談談 Android 的事件傳遞機制
我們先說 ACTION_DOWN 事件。當?戶按下屏幕的時候,事件產?并傳遞給了 Activity 并調?
Activity 的 dispatchTouchEvent() ?法,如果 Activity 沒有調? onInterceptTouchEvent()
進?事件攔截,則會傳遞給 DecorView 。由于 DecorView 繼承? FrameLayout ,?
FrameLayout 是 ViewGroup 的?類,所以直接會調? ViewGroup 的
dispatchTouchEvent() ?法。在 ViewGroup 中同樣要調? onInterceptTouchEvent() 查看
是否要攔截,默認返回 false,不過我們可以直接改寫覆蓋它。如果 ViewGroup 沒有做事件攔截,
則會通過?個 for 循環倒序遍歷該 ViewGroup 下的所有? View 的 dispatchTouchEvent() ,在
該?法中,會查看該 View 是否 enable,再查看是否?寫了 OnTouchListener 的 onTouch() ?
法,這也是 onTouch() 優先于 onTouchEvent() 的原因。在 View 的 onTouchEvent() ?法
中,只要 View 的 CLICKABLE 或者 LONG_CLICKABLE 有?個為 true,那么 onTouchEvent() 就
會消耗這個事件,接著就會在 ACTION_UP 事件中調? performClick() ?法。如果? View 沒有
消耗這個事件,則會傳遞回給 ViewGroup 。如果 ViewGroup 也沒有消耗這個事件,則向上傳遞給
Activity。
?對 ACTION_UP 和 ACTION_MOVE 事件就不?樣了。不管 ACTION_DOWN 事件在哪個控件消費了
(return true),那么 ACTION_UP 和 ACTION_MOVE 就會從上往下(通過
dispatchTouchEvent() ?法)做事件分發向下傳,就只會傳遞到這個控件,?不會選擇繼續往下
傳遞。如果 ACTION_DOWN 事件是在 dispatchTouchEvent() 消費,那么事件到此停?傳遞;如
果 ACTION_DOWN 事件是在 onTouchEvent() 消費,那么就會把 ACTION_UP 和 ACTION_MOVE
傳遞給該控件的 onTouchEvent() 處理并結束傳遞。
處理滑動沖突:主要分為外部攔截法和內部攔截法。
外部攔截法:通過?寫? View 的 onInterceptTouchEvent() ,根據業務邏輯需要在
ACTION_MOVE ??進?處理。
內部攔截法:通過?寫? View 的 dispatchTouchEvent() ,根據業務邏輯決定??消
費還是? View 處理,主要通過 View 的 requestDisallowInterceptTouchEvent()
來處理。
對于 ACTION_CANCLE 的調?時機,我之前看過系統?檔的解釋是這樣的:在設計設置??的滑動開
關的時候,如果不監聽 ACTION_CANCEL ,在滑動到中間時,如果你?指上下移動,就是移動到開關
控件之外,則此時會觸發 ACTION_CANCLE ?不是 ACTION_UP ,造成開關的按鈕停頓在中間位
置。實際上我也去寫了?個? demo 做嘗試,我們知道 ViewGroup 是?個能放置其他 View 的布局
類,在每次事件分發的過程中,它都會調???的 onInterceptTouchEvent() 來判斷是否攔截事
件。假如前??直返回 false 在讓? View 處理事件,這時候突然 onInterceptTouchEvent() 返回
true 進?事件攔截,這時候便會在? View 中響應 ACTION_CANCEL 了。
Android 的多點觸摸機制
簡述 Android 的繪制流程。
View 的繪制流程是從 ViewRootImpl 的 performTraversals() ?法開始,其內部會調?
performMeasure()、performLayout()、performDraw()。
performMeasure():
performMeasure() 會調?最外層的 ViewGroup 的 measure(),measure() 會回調
onMeasure()。ViewGroup 的 onMeasure() 是抽象?法,但其提供了 measureChildren()。這
會遍歷? View 然后循環調? measureChild(),measureChild() 中會?
getChildMeasureSpec()、? View 的 MeasureSpec 和? View 的 LayoutParam ?起獲取本
View 的 MeasureSpec。然后再調? View 的 measure(),View 的 measure() 再調? Viwe 的
onMeasure()。該?法默認返回的是 measureSpec 的測量值,所以我們要實現?定義的
wrap_content 需要?寫該?法。
MeasureSpec (View 的內部類)測量規格為 int型,值由? 2 位規格模式 specMode 和 低 30 位具體尺? specSize 組成。其中 specMode 只有三種值:
MeasureSpec.EXACTLY //確定模式,? View 希望? View 的??是確定的,由
specSize 決定;
MeasureSpec.AT_MOST //最多模式,? View 希望? View 的??最多是 specSize
指定的值;
MeasureSpec.UNSPECIFIED //未指定模式,?View完全依據?View的設計值來決
定;
最頂層 DecorView 測量時的 MeasureSpec 是由 ViewRootImpl 中
getRootMeasureSpec() ?法確定的,LayoutParams 寬?參數均為MATCH_PARENT,
specMode 是 EXACTLY,specSize 為物理屏幕??。
只要是 ViewGroup 的?類就必須要求 LayoutParams 繼承? MarginLayoutParams,否
則?法使? layout_margin 參數。
View 的布局??由? View 和? View 共同決定。
使? View 的 getMeasuredWidth() 和 getMeasuredHeight() ?法來獲取 View 測量
的寬?,必須保證這兩個?法在 onMeasure() 流程之后被調?才能返回有效值。
View 的測量寬?和實際的寬?有區別么?
基本上 99% 都是沒區別的,但存在特殊情況,有時候可能會因為某種原因對 View 進
?多次測量,這樣每次測量的??可能是不相等的,但這樣的情況下,最后?次測量
基本是?致的。
View 的 MeasureSpec 由誰決定?
除了 DecorView 以外,其它 View 的 MeasureSpec 都是由??的 LayoutParams 和
?容器?起決定的。? DecorView 的測量存在于 ViewRootImpl 的源碼中。
?定義 View 中如何沒有處理 wrap_content 情況,會發?什么?為什么?如何解
決?
會發?設置成 match_parent ?樣的效果,View 設置為 wrap_content,實際上就是
MeasureSpec 的 AT_MOST 模式,如果沒有處理,測量出來的寬?則會是測量的?
布局的剩余容量??,實際上這樣是和設置為 match_parent 是?樣的。解決?案就
是給?定義 View 設置?個默認的??,這樣的話在 wrap_content 的時候就顯示默
認的??了。
performLayout():
performLayout() 會調?最外層 ViewGroup 的 layout() ?法,layout() ?法通過調?抽象?法
onLayout() 來確定? View 的位置。
使? View 的 getWidth() 和 getHeight() ?法來獲取 View 測量的寬?,必須保證這兩個?
法在 onLayout() 流程之后被調?才能返回有效值。
perfomrDraw():
performDraw() ?法會調?最外層的 ViewGroup 的 draw(),其中會先后繪制背景、繪制??、
繪制? View、繪制裝飾。
ViewRootImpl 中的代碼會創建?個 Canvas 對象,然后調? View 的 draw() ?法來執?具體的
繪制?作。主要點為:
如果該 View 是?個 ViewGroup,則需要遞歸繪制其所包含的所有? View。
View 默認不會繪制任何內容,真正的繪制都需要??在?類中實現。
View 的繪制是借助 onDraw() ?法傳?的 Canvas 類來進?的。
在獲取畫布剪切區(每個 View 的 draw 中傳?的 Canvas )時會?動處理掉
padding ,? View 獲取 Canvas 不?關注這些邏輯,只?關?如何繪制即可。
默認情況下? View 的 ViewGroup.drawChild 繪制順序和? View 被添加的順序?
致,但是你也可以?載 ViewGroup.getChildDrawingOrder() ?法提供不同順序。
Android 的進程間通信,Linux 操作系統的進程間通信。
Linux 操作系統的所有進程間通信 IPC 包括:
1. 管道:在創建時分配?個 page ??的內存,緩存區???較有限;
2. 消息隊列:信息復制兩次,額外的 CPU ?號,不適合頻繁或者信息量?的通信;
3. 共享內存:?需復制,共享緩沖區直接附加到進程虛擬地址空間,速度快。但進程間的同步問題
操作系統?法實現,必須各進程利?同步?具解決;
4. 套接字:作為更通?的接?,傳輸效率低,主要?于不同機器或跨?絡的通信;
5. 信號量:常作為?種鎖機制,防?某進程正在訪問共享資源時,其它進程也訪問該資源。因此它
主要作為進程間以及同?進程內不同線程之間的同步?段;
6. 信號:不適?于信息交換,更適?于進程中斷控制,?如?法內存訪問,殺死某個進程等。
Android 內核也是基于虛擬機內核,但它的 IPC 通信卻采? Binder,是有原因的:
性能優越:Binder 數據拷?僅需?次,?管道、消息隊列、Socket 均需要兩次,所以 Binder
性能僅次于共享內存(共享內存不需要拷?數據)。
因為 Linux 內核沒有直接從?個?戶空間到另?個?戶空間直接拷?的函數,需要先?
copy_from_user() 拷?到內核空間,再? copy_to_user() 拷?到另外?個?戶空
間。?在 Android 中,為了實現?戶空間到?戶空間的拷?, mmap() 分配的內存除了
映射進了接收?進程?,還映射到了內核空間。所以調? copy_from_user() 將數據拷
?進內核空間也相當于拷?進了接收?的?戶空間。
穩定性?:Binder 基于 C/S 架構,穩定性明顯優于不分客戶端和服務器端的共享內存?式;
安全性?:為每個 APP 分配 UID,進程的 UID 是鑒別進程身份的?要標志。
Android 的 IPC 通信?式。
1. 使? Bundle 的?式
因為 Bundle 實現了 Parceable 接?,所以可以很?便的在不同進程之間傳輸,傳輸的數據必須
是能夠被反序列化的數據或基本數據類型。Bundle 的?式簡單輕便,但只能單??傳輸數據,
使?有局限。所以僅僅適合四?組件的進程間通信。
2. 使??件共享的?式
共享?件也是?種不錯的進程間通信?式,兩個進程通過讀寫同?個?件來實現交換數據。但這
樣的?式在并發編程的時候容易出問題。所以只適合沒有并發的場景交換?些簡單的數據。
3. 使? Messenger 的?式
Messenger 底層實現是 AIDL,功能?般,?持?對多的串?通信,?持實時通信。但不能很好
的處理并發現象,不?持 RPC,只能傳輸 Bundle ?持的數據類型。適?于低并發的?對多即時
通信,? RPC 需求。
4. 使? AIDL 的?式
AIDL 是?種 IDL 語?,功能強?,?持?對多的并發通信,?持實時通信。但使?稍復雜,需
要處理好線程同步,適合于?對多通信?且有 RPC 需求。
在我印象中,AIDL 分為兩類,?類是定義 Parcelable 對象,以供其他 AIDL ?件使?。另?類
是定義?法接?,以供系統使?來完成跨進程通信。
它?持基本數據類型和實現 Parcelable 的類型。傳參除了 Java 基本類型和 String、
CharSequence 之外其他的都必須在前?加上 tag 參數。分別是:
in:表示數據只能由客戶端流向服務端;
out:表示數據只能由服務端流向客戶端;
inout:表示數據可以在服務端和客戶端雙向流通。
5. 使? ContentProvider 的?式
ContentProvider 數據訪問功能??特別強?,?持?對多并發數據共享,但卻是?種受約束的
AIDL,主要提供數據源的增刪查改操作,適?于?對多的進程間數據共享。
6. Socket
Socket ?式功能強?,可通過?絡傳輸字節流,?持?對多的并發實時通信,但其使?繁瑣,
不?持直接的 RPC,適?于?絡數據交換。
?個 APP 的程序??是什么?整個 APP 的主線程的消息循環是在哪?創建的?
APP 的程序??是 ActivityThread.main()。 在 ActivityThread 初始化的時候,就已經創建消息循環了,所以在主線程??創建 Handler 不需要指
定 Looper,?如果在其他線程使? Handler,則需要單獨使? Looper.prepare() 和 Looper.loop()
創建消息循環。
簡述 APP 的啟動流程
1. ?先是調? startActivity(intent),通過 Binder IPC 機制,最終調?到
ActivityManagerService,這個 Service ?先會通過 PackageManager 的 resolveIntent() ?法
來收集這個 intent 對象的指向信息;然后通過 grantUriPermissionLocked() ?法來驗證?戶是
否有?夠的權限去調?該 intent 對象指向的 Activity。如果有權限,則開始創建進程。
2. 創建進程。
ActivityManagerService 調? startProcessLocked() ?法來創建新的進程,這個?法會通過
socket 通道傳遞參數給 Zygote 進程,Zygote 孵化?身,并調? ZygoteInit.main() ?法來實例
化 ActivityThread 對象并最終返回新進程的 PID。
ActivityThread 隨后會在 main ?法中依次調? Looper.prepareLoop() 和 Lopper.loop() 來開
啟消息循環。這也是在主線程中使? Handler 并不需要使?這兩個?法來開啟消息循環的原
因。
3. 綁定 Application
接下來要做的就是將進程和指定的 Application 綁定起來,這個是通過 ActivityThread 對象中調
? bindApplication() ?法完成的,這個?法發送?個 BIND_APPLICATION 的消息到消息隊列
中,最終通過 handleBindApplication() ?法來處理這個消息,然后調? makeApplication() ?
法來加載 APP 的 classes 到內存中。
4. 啟動 Activity
經過前?的步驟,系統已經擁有了該 Application 的進程,后?的話就是普通的從?個已經存在
的進程中啟動?個新進程的 activity 了。實際調??法詳? Activity 的啟動過程。
簡述 Activity 的啟動過程。
?先還是得當前系統中有沒有擁有這個 Application 的進程。如果沒有,則需要處理 APP 的啟動過
程。在經過創建進程、綁定 Application 步驟后,才真正開始啟動 Activity 的?法。 startActivity() ?
法最終還是調?的 startActivityForResult()。 在 startActivityForResult() 中,真正去打開 Activity 的實現是在 Instrumentation 的
execStartActivivity() ?法中。
在 execStartActivity() 中采? checkStartActivityResult() 檢查在 manifest 中是否已經注冊,如果沒
有注冊則拋出異常。否則把打開 Activity 的任務交給 ActivityThread 的內部類 ApplicationThread,
該類實現了 IApplicationThread 接?。這個類完全搞定了 onCreate()、onStart() 等 Activity 的?命
周期回調?法。
在 ApplicationThread 類中,有?個?法叫 scheduleLaunchActivity(),它可以構造?個 Activity 記
錄,然后發送?個消息給事先定義好的 Handler。
這個 Handler 負責根據 LAUNCH_ACTIVITY 的類型來做不同的 Activity 啟動?式。其中有?個?要的
?法 handleLaunchActivity() 。 在 handleLaunchActivity() 中,會把啟動 Activity 交給 performLaunchActivity() ?法。
在 performLaunchActivity() ?法中,?先從 Intent 中解析出?標 Activity 的啟動參數,然后?
ClassLoader 將?標 Activity 的類通過類名加載出來并? newInstance() 來實例化?個對象。
創建完畢后, 開始調? Activity 的 onCreate() ?法,?此,Activity 被成功啟動。
Bundle 的數據結構,如何存儲?既然有了 Intent.putExtra(),為何還需要
Bundle?
Bundle 內部是采? ArrayMap 進?存儲的,并不是 HashMap 。? ArrayMap 內部是使?兩個
數組進?數據存儲,?個數組記錄 key 的 hash 值,另?個數組記錄 value 值,內部使??分法對
key 進?排序,并使??分法進?添加、刪除、查找數據,因此它只適合于?數據量操作。
Intent 可以附加的數據類型,?多數都是基礎類型,? Bundle 添加數據的功能更加強?。?如
有?些可以使? Bundle 的場景:
Activity A 傳遞給 Activity B 再傳遞給 Activity C,如果? Intent 的話,需要不斷地取和存,
?較麻煩,但? Bundle 就很好解決了這個問題;
在設備旋轉的時候保存數據,我們就會? Bundle ; 在 Fragment 中,傳遞數據采? Bundle 。
之所以不? HashMap 是因為 HashMap 內部使?是數組 + 鏈表的結構(JDK 1.8 增加了紅?
樹),在數據量較少的情況下,HashMap 的 Entry Array 會? ArrayMap 占?更多的內存。 ?
Bundle 的使?場景是?數據量,實際上 SDK 也對數據量做了限制,在數據量太?的時候使?
Bundle 傳遞會拋異常。所以相?之下,使? Bundle 來傳遞數據,可以保證更快的速度和更少
的內存占?。
Android 圖?加載框架對?區別。
ImageLoader:?持下載進度監聽,可以在 View 滾動的時候暫停圖?的加載,默認實現多種內
存緩存的?法,?如最?最先刪除、使?最少最先刪除、最近最少使?最先刪除、先進先刪除
等,?且也可以??配置緩存算法,可惜現在已經不再維護,該庫使?前還需要進?配置。
Picasso:包?較?,可以取消不在視?范圍內圖?資源的加載,并可使?最少的內存完成復雜
的圖?轉換,可以?動添加?級緩存,?持任務調度優先級處理,并發線程數可以根據?絡類型
進?調整,圖?的本地緩存交給了 OkHttp 處理,可以控制圖?的過期時間。但功能?較簡單,
?身并不能實現「本地緩存」的功能。
Glide:?持多種圖?格式緩存,適?于更多的內容表現形式,?如 Gif,WebP、縮略圖、
Video 等,?持根據 Activity 或者 Fragment 的?命周期管理圖?加載請求;對 Bitmap 的復?
和主動回收做的較好,緩存策略做的?較?效和靈活(Picasso 只會緩存原始尺?的圖?,?
Glide 緩存采?的是多種規格)。加載速度快且內存開銷?(默認 Bitmap 格式的不同,使得內
存開銷是 Picasso 的?半)。但?法較多較復雜,因為相當于 Picasso 的改進,包較?但總的來
說影響不?。
Glide 還可以設置 Gif 圖次數以及可以設置 Gif 的第?幀顯示,?如常?的地址展示 Gif 動
畫。
Fresco:最?的優勢莫過于在 5.0 以下設備的 Bitmap 加載。在 5.0 以下的系統, Fresco 會將
圖?放到?個特別的內存區域,??減少 OOM(會在更底層的 Native 層對 OOM 進?處理,
圖?將不再占? APP 的內存),所以它相當適?于?性能加載?量圖?的場景。但它的包太?
也?直為?詬病,?且?法?較復雜,底層還會涉及 C++ 領域。
講講 Glide 的三級緩存
Glide 的三級緩存為內存緩存、磁盤緩存和?絡緩存,默認采?的是內存緩存。
有緩存,必定就有緩存 key,之前簡單看了?下 key 的?成?法,真的繁瑣,傳了整整 10 個參數。
不過邏輯也不是很復雜,主要就是?寫 equals() 和 hashCode() ?法來保證 Key 的唯?性。
內存緩存
Glide 默認會采?內存緩存,當然可以通過 skipMemoryCache(true) ?法禁?。Glide 的內存緩
存其實也是使?了 LruCache 算法,它的主要原理就是把最近使?過的對象放在
LinkedHashMap 中,并且把最近最少使?的對象在緩存值達到預設值之前從內存中刪除。不過
在 Glide 中,除了 LruCache 算法以外,Glide 還結合了?種弱引?機制,共同完成緩存功能。
所以在獲取的時候,會先? LruCache 的 loadFromCache() ?法來獲取,如果獲取到則會將它
從緩存區移除,然后再把這個圖?存儲到 activeResource 中。這個 activeResource 就是?個緩
存的 HashMap,?來緩存正在使?中的圖?,這樣可以保護這些圖?不會被 LruCache 算法回
收掉。如果沒有獲取到再通過弱引?機制的 loadFromActiveResources() ?法來獲取緩存圖?。
若是都沒有獲取到,才會向下執?。
在 Glide 中,有?個變量,當這個變量?于 0,則代表圖?正在使?中,就放到 activeResource
弱引?緩存中。如果這個變量變成 0 以后,我們就從弱引?中移除,并 put 到
LruResourceCache 中。
硬盤緩存
硬盤緩存,有 4 種模式,默認只會緩存轉換過后的圖?,另外也可以選擇只緩存原始圖?或者既
緩存原始圖?也緩存轉換過的圖?,甚?是選擇不緩存。
和內存緩存類似,硬盤緩存的實現也是采?的 LruCache 算法。Glide 會優先從磁盤緩存中讀取
圖?,只有從緩存中讀取不到圖?時,才會去讀取原始圖?。
在緩存原始圖?的時候,其實傳?的 Key 和之前的 Key 不?樣,這是因為原始圖?并不需要那
么多的參數,所以這個 Key 的?成不需要那么多的參數。
主線程中 Looper.loop() ?限循環為什么不會造成 ANR?
Looper.loop() 主要功能是不斷地接收事件和處理事件,只能說是某?個消息或者說消息的處理阻
塞了 Looper.loop() ,?不是 Looper.loop() 阻塞了它。總的來說, Looper.loop() ?法可
能會引起主線程的阻塞,但只要它的消息循環沒有被阻塞,能?直處理事件就不會造成 ANR。
RxJava 2 的背壓是什么?如何形成的??如何解決?
被觀察者發送消息太快以?于它的操作符或者訂閱者不能及時處理相關的消息,從?操作消息造成阻
塞的現象叫做背壓。
在 RxJava 1.0 中,背壓事件的緩存尺很?,只有 16,所以不能處理較?量的并發事件,?且被觀察
者?法得知觀察者對事件的處理能?和事件處理進度,只知道把時間?股腦拋給觀察者,所以操作很
多事件不能被背壓,拋出我們聞名的 MissingBackpressureException 異常。
常規解決?案:
既然背壓是由于被觀察者發送事件太快或者太?所導致,所以我們肯定可以通過?定的?法讓被
觀察者發送事件更慢;
使? filter 過濾操作符或者 sample 操作符讓觀察者少處理?些事件;
很明顯,我們在正常開發中這樣的解決?案肯定不可取,因為我們不知道應該讓被觀察者保持?個怎
樣的頻率才是最佳的,所以在 RxJava 2.0 中官?推出了 Flowable 和 Subscriber 來?持背壓,
同時去除了 Observable 對背壓的?持。 Flowable 在設計的時候實際上采?了?種新的思路,也
就是「響應式拉取」的?式來處理處理事件和發出事件速率不均衡的問題。在 Flowable 的使?
中,我們會有?種背壓策略,并在 create() 的時候作為參數傳進去。值得注意的是我們可以通過
調? request() ?法來定義觀察者處理事件的能?,在被觀察者上?可以通過該 requested 變
量來獲取下游的處理能?(這個值最?能得到 128,因為它是內部調?的)。這樣只要被觀察者根據
觀察者的處理能?來決定發送多少事件,就可以避免發出?堆事件從?導致 OOM。這也就完美的解
決了事件丟失的問題,?解決了速度的問題。如果我們沒有調? request() 進?設置,依然會出現
收不到消息(不在同?個線程,且沒有超過緩存區的 128 個)或直接拋出異常(在同?個?作線
程)。
RxJava 2.0 背壓的策略確實相對 1.0 版本提升了很多,但是真的沒有我們想象的完美,因為丟失的事
件不?定是我們想要丟失的,所以還是應該根據實際需求來制定防阻塞策略。
RxJava 2.0 中增加了背壓策略模式:
ERROR:處理跟不上發送速度時報異常;
MISSING:如果流的速度?法同步可能會報異常;
BUFFER:和 1.0 的 Observable ?樣,超過緩存區直接報異常;
DROP:跟不上速度直接丟棄;
LASTED:?直保留最新值,直到被下游消費掉;
?定義 View 優化
頻繁調?的?法盡量減少不必要的代碼,?如 onDraw() ,盡量不要做內存分配的事情;
盡量地減少 onDraw() 的調?次數,如果可以盡量少使??參數的 invalidate() ,?選擇
4 個參數的 invalidate() 進?局部?繪;
避免使? requestLayout() , requestLayout() 的執?會去?新 measure ,這樣的計算
?旦遇到沖突,將需要?較?的時間;
布局優化的措施
1. 降低 Overdraw,減少不必要額背景繪制;
2. 減少嵌套和空間的個數:RelativeLayout 和 LinearLayout 都需要 measure 兩次才能完成,所
以?定要盡量少嵌套;
使? merge 標簽減少 UI 的層級,提?加載速度,替代 FrameLayout 或者當?個布局
include 另外?個布局的時候,主要?于消除?錄結構中的多余 ViewGroup;
使? ViewStub,作?和 include 類似,也是加載另外?個布局,但和 include 不同的是,
ViewStub 引?的布局模式是不顯示的,不會占? CPU 和內存,所以經常?來引?那些默
認不顯示的布局,?如進度條、錯誤提示等;
APP 如何保證后臺服務不被殺死?
主要是兩個??,提升進程的優先級,降低被殺死的概率,以及在進程被殺死后,進?拉活。
提升進程優先級的?案:
1. 利? Activity:監控?機鎖屏解屏事件,在鎖屏時啟動 1 個像素的 Activity,解鎖的時候把
Activity 銷毀,該?案可以使進程的優先級在屏幕鎖屏時間由 4 提升為最?優先級 1。
2. 在后臺播放?段?聲?樂。
3. 利? Notification:Service 的優先級為 4,可以通過 setForeground 把后臺 Service 設置為前
臺 Service,但是不做處理會是?戶感知的。解決?案是通過實現?個內部 Service,在
LiveService 和其內部 Service 中同時發送具有相同 ID 的 Notification,然后將內部 Service 結
束掉。隨著內部 Service 的結束,Notification 將會消失,但系統優先級依然保持為2。
進程死后拉活的?案:
1. 利?系統?播拉活。缺點是系統?播不可控,只能保證發?事件時拉活,?法保證進程掛掉后?
即拉活;?且?播接收器被管理軟件、系統軟件通過「?啟管理」等功能禁?的場景?法接收?
播,從??法?啟。所以該?案多?于備??案。
2. 利?第三?應??播拉活:?如個推 SDK、友盟等。
3. 利?系統 Service 機制拉活。把 Service 設置為 START_STICKY,利?系統機制在 Service 掛掉
后?動拉活,但在短時間只能有 5 次,?且取得 Root 權限的管理?具或者系統?具可以通過
forestop 停?掉,?法?啟。
如何定位 ANR,有哪些檢測?式
定位:如果在開發機器上出現問題,我們可以通過查看 /data/anr/traces.txt 即可,最新的 ANR 信息
會在最開始的部分。或者使? Systrace 和 TraceView 找出影響響應的問題。
TraceView 是 Android SDK ?帶的?個系統性能分析?具,可以定位應?代碼中的耗時操作。
Systrace 是 Android 4.1 新增的應?性能數據采樣和分析?具,需要借助 chrome 瀏覽器。
檢測:可以使? BlockCanary 分析 Android 的 ANR。
介紹下 MVP 模式,為什么從 MVC 重構到了 MVP?知道 MVVM 么?
MVP 實際上就是 MVC 的變種,MVP 把 Activity/Fragment 中的 UI 邏輯抽象成了 View 接?,把業務
邏輯抽象成 Presenter 接?,Model 類還是原來的 Model。主要有以下好處:
Activity 代碼更加簡潔,耦合性更低,可讀性更?,各板塊各司其職;
?便進?單元測試,因為Presenter 被抽象成接?,可以有多種具體的實現;
可以有效避免內存泄漏。在 MVC 模式中,異步任務對 UI 的操作會放在 Activity 中,所以異步任
務會持有 Activity 的引?,這在特定情況下肯定會造成內存泄漏的。
MVVM 雖然沒有使?過,但卻有所了解,最開始了解還是從 Google 2015 推出的 databinding 開始
的。MVVM 包含 View、Model、ViewModel 三個部分。核?思想和 MVP 類似,利?數據綁定、依
賴屬性、命令、路由事件等新特性,打造了?個更加靈活?效的架構。
View 對應于 Activity 和 XML,負責 View 的繪制以及?戶交互;
Model 同樣是代表實體模型;
ViewModel:負責 View 和 Model 間的交互,負責業務邏輯。
Parcelable 和 Serializable 的區別。
Parcelable 和 Serializable 都?持序列化和反序列化操作,但 Serializable 使? I/O 讀寫存儲在硬盤
上,? Parcelable 是直接在內存中讀寫,Parcelable 的性能? Serializable 好,在內存開銷??較
?,所以在內存間做數據傳輸時推薦使? Parcelable,如在 Activity 之間傳輸數據。
? Serializable 對數據持久化更?便保存,所以在保存或?絡傳輸數據時選擇 Serializable,因為
Android 不同版本 Parcelable 可能不同,所以不推薦使? Parcelable 進?數據持久化。
System.gc() 和 Runtime.gc() 的區別
System.gc() 和 Runtime.gc() 是等效的,在 System.gc() 內部也是調?的
Runtime.gc() 。調?兩者都是通知虛擬機要進? gc,但是否?即回收還是延遲回收,由 JVM 決
定。兩者唯?的區別就是?個是類?法,?個是實例?法。
算法 && Other
如何在 100 億個數中找到最?的 100 個數。
因為數字很多,所以可以先? hash 法進?去?,如果?復率很?的話,這樣可以減少很多的內存?
量,從?縮?運算空間。然后再采?最?堆的?式進?處理,即先讀取前 100 個數,形成最?堆,保
證堆頂的數最?,然后依次讀取剩余數字,只要出現?堆頂元素?的元素,則替換堆頂元素并?新調
整堆為最?堆,直到讀取完成,最?堆中的元素就是最?的 100 個數。
?成最?堆?式:
private static void heapAdjustMin(int[] arr, int i, int n) {
 int temp = arr[i];
 // j 代表左結點
 int j = 2 * i + 1;
 while (j < n) {
 // 我們先找出?的,如果滿?說明右結點?較?
 if (j + 1 < n && arr[j] > arr[j + 1])
 ++j;
 // 此時 arr[j] 就是最?值了
算法題:?個?列都有序的數組,給定?個數,找出具體的位置
如果都是升序,則從最右邊開始找,?了就往下找,?了就往左找。
TCP 和 UDP 區別是什么?簡單說?下 TCP 三次握?和四次揮?協議。
TCP 和 UDP 同為數據傳輸協議,但 TCP 更加強調安全,它?向字節流,需要先通過三次握?協議建
?連接后才可以進?數據傳輸。? UDP 更強調速度,它?向報?傳輸(數據報有?度),?需連接,所
以帶來的優勢是效率?,?安全性低也?直為?詬病。
TCP 并不能保證數據?定會被對?收到,只能夠做到如果有可能,就把數據傳遞到接收?,否
則就通知?戶(通過放棄?傳并且中斷連接這??段)。因此準確說 TCP 也不是 100% 可靠的
協議,它所能提供的是數據的可靠遞送或故障的可靠通知。
對于 TCP 三次握?:
第?次握?:客戶端發送 SYN 報?,并置發送序號為 X,然后客戶端進? SYN_SEND 狀態,等
待服務器確認;
第?次握?:服務器收到 SYN 報?段,然后發送 SYN + ACK 報?,并置發送序號為 Y,在確認
序號為 X + 1,發送后服務器進? SYN_RECV 狀態;
第三次握?:客戶端收到服務器的 SYN + ACK 報?段,然后向服務器發送 ACK 報?,并置發送
序號為 Z,在確認序號為 Y + 1,服務器和客戶端都進如 ESTABLISHED 狀態,完成 TCP 三次握
?。
進?三次握?,主要是為了防?服務器端?直等待?浪費資源。
對于 TCP 四次揮?:
第?次揮?:客戶端發送?個 FIN 標志位置為 1 的包,表示??已經沒有數據可以發送,但仍可
以接收數據,發送完畢后,客戶端進? FIN_WAIT_1 狀態;
第?次揮?:服務器確認客戶端的 FIN 包,發送?個確認包,表明??接收到了客戶端關閉連接
的請求,但還沒準備好關閉連接。發送完畢后,服務器端進? CLOSE_WAIT 狀態,客戶端接收
到這個確認包后,進? FIN_WAIT_2 狀態,等待服務器端關閉連接;
第三次揮?:服務器端準備關閉連接時,向客戶端發送結束連接請求,FIN 置為 1。發送完畢
后,服務器端進? LAST_ACK 狀態,等待來?客戶端的最后?個 ACK;
第四次揮?:客戶端接收到來?服務器端的關閉請求,發送?個確認包,并進?到 TIME_WAIT
狀態,等待可能出現的要求?傳的 ACK 包;服務器端接收到這個確認包之后,關閉連接,進?
CLOSE 狀態;客戶端等待了某個固定時間之后,沒有收到服務器端的 ACK,認為服務器端已經
正常關閉連接,于是??也關閉連接,進? CLOSE 狀態。
 // 如果 ?結點?左右結點最?的都?,說明已經符合條件
 if (temp <= arr[j])
 break;
 // 否則的話就把最?的值和 ?結點對換;
 arr[i] = arr[j];
 i = j;
 j = 2 * i + 1;
 }
 arr[i] = temp;
 } 
HTTP 狀態碼
1XX:指示信息,表示請求已接收,繼續處理
2XX:成功:常? 200
3XX:?定向:常? 301:請求永久?定向;302:請求臨時?定向;
4XX:客戶端錯誤:常? 400:客戶端請求語法錯誤,服務器?法理解;401:請求未經授權;
403:服務器收到請求,但拒絕服務;
5XX:服務器錯誤:常? 500:服務器發?不可預期的 URL;503:服務器當前不能處理客戶端
請求,?段時間后恢復正常。
講講 Kotlin 或者 Python 的 Lambda 表達式和?階函數。
Lambda 表達式其實就是?個未聲明的函數表達式(匿名函數)。其優勢有 3 個:
?較輕便,即?即扔,很適合需要完成?項功能,但是此功能只在此?處使?,連名字都很隨意
的情況下。
作為匿名函數,?般?來給 filter、map 這樣的函數式編程服務;
可以作為回調函數,傳遞給某些應?,?如消息處理。
??階函數就是把函數作為參數或者返回值的函數。
?寫線程安全的單例模式代碼
針對?前的簡歷
// 懶漢式
public class Single{
 private volatile static Single instance;
 private SIngles(){}
 public static Single getInstance(){
 if (instance == null){
 synchronized(Single.class){
 if (instance == null)
 instance = new Single();
 }
 }
 return instance;
 }
}
// 內部類?案
public class Single{
 private Single(){}
 private static class Holder{
 private static Single INSTANCE = new Single();
 }
 public static Single getInstance(){
 return Hodler.INSTANCE;
 }
}
針對?前的簡歷
Handler 機制引出的 ThreadLocal,?種常?的 ThreadLocal,講講原理。
ThreadLocal 是?個線程內部的數據存儲類,每個線程都會保存?個 ThreadLocalMap 的實例
threadlocals。ThreadLocalMap 是 ThreadLocal 的內部類,所以每個 Thread 都有?個對應的
ThreadLocalMap。??保存了?個 Entry 數組,這個數組專??于保存 ThreadLocal 的值。通過這
樣的?式,就能讓我們在多個線程中互不?擾存儲和修改數據。
ThreadLocal 是?個泛型類,其主要核?是 set() 和 get() ?法,在 set ?法中,我們先獲取當前線
程,再根據當前線程獲取 ThreadLocalMap,如果該 ThreadLocalMap 不為空,就把 ThreadLocal
和我們想存放的數據設置進去。如果 ThreadLocalMap 為空的話,則先創建 ThreadLocalMap 后再
設置數據。
在 get ?法中,同樣是先取出當前的 ThreadLocalMap 對象,如果該對象為空,則調?
setInitialValue(),默認情況下 initialValue() ?法返回為 null,不過我們可以通過?寫 initialValue()
?法來修改它。。如果取出的 ThreadLocalMap 對象不為空,那就取出它的 table 數組并找出對應的
數據。
因為 ThreadLocal 的 get() 和 set() ?法操作的對象都是當前線程的 ThreadLocalMap 對象的 table 數
組,它的所有讀寫操作都來?各?線程的內部,所以才能做到不同線程互不?擾存儲和修改數據。
在 Android 中,你是如何做系統簽名的?
1. ?先必須在應?的 Manifest.xml 中增加 android:sharedUserId="android.uid.system"
2. 然后打包出未簽名的 apk,再編譯出 signapk.jar、從系統源碼?錄獲取 platform.x509.pem、
platorm.pk8 兩個?件,利? java -jar 命令打包出帶系統簽名的 apk。
命令為 java -jar signapk.jar platform.x509.pem platform.pk8 未簽名.apk 已經簽名.apk
講講 Android 中常?的設計模式
單例模式
主要作?:省去創建?種對象創建的時間,對系統內存的使?頻率降低,降低 GC 壓?,縮短
GC 停頓的時間。
最終優化版本:靜態內部類。
建造者模式
適?于構造?個對象需要很多個參數,并且參數的個數或者類型不固定的時候。?如我開源的圖
?壓縮庫、Android 中的 AlertDialog、Glide、OkHttp。
適配器模式
在 Android 中應?:Adapter
裝飾模式
優點是:對于拓展?個對象的功能,裝飾模式?繼承更加靈活,不會導致類的個數急劇增加;可
以通過?種動態的?式來拓展?個對象的功能;可以對?個對象進?多次裝飾,通過使?不同的
具體裝飾類以及這些裝飾類的排列組合。
Android 中的應?:Context。
HTTP GET 和 POST 的區別
1. GET 的?點是從服務器上獲取資源;POST 的?點是向服務器發送數據。
2. GET 傳輸數據是通過 URL 請求,以 field = value 的?式,這個過程對?戶是可?;? POST 傳
輸數據通過 HTTP 的 POST 機制,將字段與對應值封存在請求體中發送給服務器,這個過程對?
戶是不可?的。
3. GET 的數據傳輸量較?,因為 URL 的?度是有限制的,但效率較?;? POST 可以傳輸?量數
據,所以上傳?件不能? GET ?式。
4. GET 是不安全的,因為 URL 是可?的,可能會泄漏私密信息;? POST 較 GET 安全性更?。
5. GET ?式只能?持 ASCII 字符,向服務器傳的中?字符可能會亂碼;? POST ?持標準字符集,
可以正確傳輸中?字符。
常?的 HTTP ?部字段
通?的?部字段(請求報?和響應報?都會使?的?部字段)
Date:報?創建時間
Connection:連接的管理
Cache-Control:緩存的控制
Transfer-Encoding:請求報?的傳輸編碼格式
請求?部字段(請求報?會使?的?部字段)
Host:請求資源所在服務器
Accept:可處理的媒體類型
Accept-Charset:可接受的字符集
Accept-Encoding:可接受的內容編碼
Accept-Language:可接受的?然語?
響應?部字段(響應報?會使?的?部字段)
Accept-Ranges:可接受的字節范圍
Location:令客戶端?新定向的 URI
Server:HTTP 服務器的安裝信息
實體?部字段(請求報?和響應報?的實體部分都會使?的?部字段)
Allow:資源科?持的 HTTP ?法
Content-Type:實體主體的類型
Content-Encoding:實體主體適?的編碼?式
Content-Language:實體主體的?然語?
Content-Length:實體主體的字節數
Content-Range:實體主體的位置范圍,?般?于發出部分請求時使?
Cookie 和 Session 的區別
Cookie 和 Session 都是保存客戶端狀態的機制,它們都是為了解決 HTTP ?狀態的問題的。它們有明
顯的不同:
Cookie 將狀態保存在客戶端;? Session 把狀態保存在服務器端。
Cookie 不是很安全,別?可以通過分析存在本地的 Cookie 并進? Cookie 欺騙,考慮到安全應
該? Session。
Session 會在?定時間內存在服務器上,當訪問增多,會?較占?服務器的性能,考慮到減輕服
務器性能??,應當考慮 Cookie。
介紹下這個 iSchool 協議,并講下它是如何?持原?和 H5 交互的?
iSchool 協議是我們的 iSchool 客戶端專有的協議,主要?于??和客戶端、服務器和客戶端交互。協
議以 iSchool:// 開頭,后?更上動作命令和參數。
?前我們?持的參數包括 page 和 function。
page 作?于服務器指定客戶端開啟特定的??,實際上我們 APP 的主?菜單??就是通過接?返回
page 協議的?式來做跳轉映射的。
function 協議主要?于?? H5 和客戶端雙向通信專?。??通過 ischool://function/ 協議調?客戶
端指定命令,客戶端執?結果以回調的形式調? JS 提供的。
?前我們的 function 協議?持是否隱藏原?標題欄、打開?個新的 URL、安裝 APP、播放視頻、錄
制視頻、選擇圖?、?付以及分享到微信等功能;
我們會分別定義?個 IschoolFunction 和 IschoolPage 接?,暴露各種?法。然后定義?個 Ischool
協議解析器類,?于解析協議,解析后把參數存放到 Map 中,?便原???讀取。
你在 iSchool 項?中都對分別對 6.0、7.0、8.0 做了哪些適配?
在 6.0 中,當時我加?公司時,項?還使?的 SdkVersion 還是 22,所以不存在權限問題,并且
還在使? HttpClient 庫,所以我?先是處理了 HttpClient 在 23 版本不能直接使?的問題,主要
是在 build.gradle 加上:useLibrary 'org.apache.http.legacy' 這句話。對于動態權限??,主
要是?先檢查應?是否有這個權限,如果沒有權限則采? requestPermission 來請求權限。
但在 Android 原?系統中,如果第?次彈出權限申請的對話框,會出現「以后不再彈出」的選
項,當?戶勾選后,再申請權限 shouldShowRequestPermissionRationale() ?法就會返回
true,導致不會彈出權限申請的對話框。我們?開始是采?彈窗告訴?戶,但后?發現有些?
機,?如??4 就?直讓 shouldShowRequestPermissionRationale() ?法返回 false,但也不
會彈出權限申請對話框。所以我們最終的處理?案是在?戶拒絕權限的回調??,直接彈窗提
示,并提供到系統設置??進?設置的處理。
在 7.0 中,雖然提供了不少新特性,?如多窗?、通知增強,低電耗模式等,但我們在項?中主
要適配了根據路徑獲取圖?需要采? FileProvider 的問題,之前我們可能都是使?
Uri.fromFile() ?法來獲取?件的 Uri,但 7.0 以后必須采? FileProvider.getUriFromFile() ?
法。
在 Andrid 8.0 中,主要適配的是不能再直接安裝 APK,?需要在 manifest.xml ?件中增加
REQUEST_INSTALL_PACKAGES 權限。
你是怎么處理各種機型的屏幕適配的?
在我們的項?中,對 Android 的各種機型做適配可謂是花了??夫。
1. ?先 Android 提供了 dp ?案,這也是我們最開始的適配?案,但我們很快發現了問題。我們的
應???有個 banner 圖輪播的功能,我們?度采? dp 做處理,寬度采? match_parent 占滿
屏幕寬度,對?多數?機顯示都不存在問題。但我們的屏幕寬度其實是不?樣的,??度固定后
肯定會出現圖?顯示不完全的情況。
2. 所以我們采?了?個笨辦法,那就是寬?限定符適配,也就是窮舉市場?主流?機的寬?像素
點,但很快發現這樣維護起來相當麻煩,所以?直在尋求好辦法進?處理。
3. 后?發現了鴻洋的 AutoLayout,簡單看了下??的源碼,也是受寬?限定符的影響,我們可以
直接在布局中寫 px 值,這樣布局會等?例放縮,但由于它不能兼顧到?定義 View 以及存在性
能問題?且他本?也沒有維護的情況下,所以我們后?并沒有?這個?案。
4. 終于在最后今?頭條開源了?個?較不錯的適配?案。我們直接通過修改 density 的值,強?把
所有不同尺?分辨率的?機的寬度值 dp 改成?個統?的值,這就解決了所有的適配問題。
設計模式的原則
設計模式可以讓我們的程序更健壯、更穩定、更加容易拓展,編寫的時候我們需要遵循 6 ?原則:
1. 單?職責原則:
單?原則很簡單,就是將?組相關性很?的函數、數據封裝到?個類中,換句話說,?個類應該
有職責單?。
2. 開閉原則:
開閉原則就是說?個類應該對于拓展是開放的,但是對于修改是封閉的。因為開放的 APP 或者
是系統中,經常需要升級和維護等,?旦進?修改,就容易破壞原有的系統,甚?帶阿??些難
以發現的 Bug。所以開閉原則相當?要。
3. ??替換原則:
??替換原則的定義為:所有引?基類的地?必須能透明地使?其?類對象。簡單地說,就是以
?類的形式聲明的變量或形參,賦值為任何繼承于這個?類的之類不影響程序的執?。
4. 依賴倒置原則:
依賴倒置主要是實現解耦,使得?層次的模塊不依賴于低層次模塊的具體實現細節。?個關鍵
點:
?層模塊不應該依賴底層模塊。?者都應該依賴其抽象類或接?;
抽象類或接?不應該依賴于實現類;
實現類應該依賴于抽象類或接?;
5. 接?隔離原則:
接?隔離原則定義:類之間的依賴關系應該建?在最?的接?上。其原則是將?常龐?的、臃腫
的接?拆分成更?的更具體的接?。
6. 迪?特原則:
描述的原則:?個對象應該對其他的對象有最少的了解。什么意思呢?就是說?個類應該對??
調?的類知道的最少。還是不懂?其實簡單來說:假設類 A 實現了某個功能,類B需要調?類 A
的去執?這個功能,那么類 A 應該只暴露?個函數給類 B,這個函數表示是實現這個功能的函
數,?不是讓類 A 把實現這個功能的所有細分的函數暴露給 B。
Android 中常?的設計模式
1. 單例 => 調?系統服務時拿的 Binder 對象;
2. Builder
主要是為了把?個復雜對象的構造和它的表示分離,使得同樣的構造過程可以創建不同的表示。
=> 常?的?如 Android 的 Dialog。
其中我的圖?壓縮庫就運?了單例和 Builder 模式。
3. ???法模式
根據傳?的參數決定創建哪個對象。
=> 典型的獲取系統服務 getSystemService() 。
4. 策略模式
如何使?策略模式呢,我不打算寫示例代碼了,簡單描述?下,就將前?說的算法選擇進?描
述。我們可以定義?個算法抽象類 AbstractAlgorithm,這個類定義?個抽象?法 sort()。每個
具體的排序算法去繼承 AbstractAlgorithm 類并?寫 sort() 實現排序。在需要使?排序的類
Client 類中,添加?個 setAlgorithm(AbstractAlgorithm al);?法將算法設置進去,每次
Client 需要排序?是就調? al.sort()。
=> ?如 Android 的屬性動畫的使?插值器。
5. 責任鏈模式
使多個對象都有機會處理請求,從?避免請求的發送者和接收者直接的耦合關系,將這些對象連
成?條鏈,并沿這條鏈傳遞該請求,直到有對象處理它位置。
=> ?如 Android 的事件分發機制。
6. 觀察者模式
=> ?如 Adapter 的 notifyDataSetChanged()。
7. 迭代器模式
=> Java 的迭代器 Iterator 類
8. 代理模式
為其他類提供?種代理以控制這個對象的訪問。
=> Android 中的典型應?就是 AIDL 的?成,它根據當前的線程判斷是否要跨進程訪問,如果不
需要跨進程訪問就直接返回實例,如果需要跨進程則返回?個代理。在跨進程通信時,需要把參
數寫?到 Parcelable 對象,然后再執? transact() 函數,我們要寫的代碼挺多的。AIDL 通過?
成?個代理類,代理類中?動幫我們寫好這些操作。
9. 適配器模式
=> 典型的 Android 的 ListView 和 RecyclerView。它們只關???的 ItemView,?不?關?這
個 ItemView ??具體顯示的是什么。?數據源存放的是顯示的內容,它保存了 ItemView 要顯
示的內容。ListView 和數據源本身沒有關系,這時候,適配器提供了 getView() ?法給 ListView
使?,每次 ListView 只需要提供位置信息給 getView() 函數,然后 getView() 函數根據位置信息
向數據源獲取對應的數據,根據數據返回不同的 View。
10. 裝飾模式
public abstract class Component{
 public abstract void operate();
}
public class ConcreteComponent extends Component{
 public void operate(){
 //具體的實現
 }
}
public class Decorator{
 private Component component;
 public Decorator(Component component){
 this.component=component;
=> 在 Android 中的 Context 。
11. 享元模式
使?享元對象有效地?持?量的細粒度對象。
=> ?如 Java 的常量池,線程池等,主要是為了??對象。在 Android ??的
Message.obtain() 就是?個很好的例?。
講講 LeakCanary 的實現原理。
LeakCanary 就是通過注冊 ActivityLifecycleCallbacks ,監聽?命周期?法的回調,作為整個
內存泄漏分析的??。每次 onActivityDestroyed(Activity activity) ?法被回調之后,都會
創建?個 KeyedWeakReference 對相應的 Activity 的狀態進?跟蹤。
1. 在后臺線程(HandlerThread)檢查引?是否被清除,如果沒有,?動調? GC。
2. 如果引?還是未被清除,把 heap 內存 dump 到 APP 對應的?件系統中的?個 .hprof ?件
中。
3. 在另外?個進程中的 HeapAnalyzerService 有?個 HeapAnalyzer 使? HAHA 開源庫解析
這個?件。
4. 得益于唯?的 reference key, HeapAnalyzer 找到 KeyedWeakReference ,定位內存泄漏。
5. HeapAnalyzer 計算到 GC roots 的最短強引?路徑,并確定是否是泄漏。如果是的話,建?
導致泄漏的引?鏈。
6. 引?鏈傳遞到 APP 進程中的 DisplayLeakService , 并以通知的形式展示出來。
多渠道打包?美團的多渠道打包?
之前有對美團的多渠道打包?案進?調研。
美團之前的多渠道打包?案主要是在 META-INF ?錄下添加空?件,?空?件的名稱來作為渠道的唯
?標示,之前在 META-INF ?錄下添加?件是不需要?新簽名應?的,所以好使。但 Android 7.0 出
了新的簽名?式,把 META-INF 列?了保護區,向 META-INF ?錄添加空?件將會對其他區產?影
響,所以美團之前的多渠道打包?式將會出現問題。但美團響應速度很快,很快就出現了 Walle,可
以完美在新簽名?式下進?多渠道打包。
新的簽名?案主要是在不受保護的 APK Signing Block 上做?章。好像是在 APK 中添加 ID-value。
 }
 public void operate(){
 operateA();
 component.operate();
 operateB();
 }
 public void operateA(){
 //具體操作
 }
 public void operateB(){
 //具體操作
 }
}
我們的 iSchool 項?采?多渠道打包并不是為了統計各個商店的下載量等,?是主要為了應對我們的
商業模式。所以采?的傳統的?式進?多渠道打包,在 Gradle ?件??通過 productFlavors 標簽設
置不同的 applicationId 和其它信息。并且設置 task 做到指定打?個代理商的渠道包。我們對資源?
件也是不?樣的。我們對同?個資源采?不?樣的后綴名稱,然后通過字符串截取去判斷真正的資源
?件,達到不?樣的項?引?不?樣的資源的?的。
RxJava 源碼。
簡單看了?下 RxJava2 的 源碼訂閱過程。
創建 Observable & 定義需要發送的事件
創建 Observable 的本質其實就是創建了?個 ObservableCreate 類的對象。它的內部有 1 個
subscribeActual() ?法。
創建 Observer & 定義響應事件的?為
Observer 是?個接?,內部定義了 4 個接??法,分別?于響應被觀察者發送的不同事件。
通過訂閱(subscribe)連接觀察者和被觀察者
訂閱的本質實際上就是調? ObservableCreate 類對象的 subscribeActual()。
執?邏輯為:
1. 創建?個 CreateEmitter 對象(封裝成 1 個 Disposable 對象);
2. 調?被觀察者 ObservableOnSubscribe 對象復寫的 subscribe();
3. 調?觀察者 Observer 復寫的 onSubscribe();
4. 被觀察者內部的 subscribe() 依次回調 Observer 復寫的?法。
RxJava 內置了許多操作符,每個操作符的 subscribeActual() 中的默認實現都不相同。但基本原理都
是封裝傳?的 Observer 對象,再調? Observable 復寫的 subscribe()。
各種操作符內的 Observable 中的 SubscirbeActual() ?法都含有?句代碼:
source.subscribe(Observer),這個 source 就是上游創建的被觀察者對象 Observable。所以才導致
RxJava 的多個被觀察者 Observable 發送事件的順序是?上?下的,和代碼順序?樣。
OkHttp 源碼。
OkHttp ?持異步請求和同步請求。因為異步請求不會阻塞主線程,所以我們通常會直接把異步請求
寫在 UI 主線程中。
我們?先會創建?個 OkHttpClient 對象,在源碼中可以知道,OkHttpClient 對象是通過 Builder 模
式創建的,在 OkHttpClient.Builder() 類的構造?法中,對它的屬性都有默認值和默認對象。
創建好 OkHttpClient 對象之后,我們會通過調?它的 newCall(Request request) ?法來創建?個
RealCall 對象。
對于異步請求,我們會直接調? RealCall 的 enqueue() ?法,這個?法???會調?調度器對象
Dispatcher 的 enqueue(AsyncCall call) ?法。這個?法接收?個 AsyncCall 對象參數。
從 Dispatcher 的 enqueue(AsyncCall call) ?法中可以看到,得到異步任務之后,如果異步
任務運?隊列中的個數?于 64 并且每個主機正在運?的異步任務?于 5,則將該異步任務加?到異步
運?隊列中,并通過線程池執?該異步任務,若不滿?以上兩個條件,則將該異步任務加?到預備異
步任務隊列中。
AsyncCall 是 RealCall 的內部類。它主要是響應?絡請求的各種回調,并最后從 Dispatcher 對象的異
步請求隊列中移除該任務,并將符合條件的預備異步任務隊列中的任務加?到正在運?的異步任務隊
列中,并將其放?線程池中執?。
在 AsyncCall 中最?要的就是 execute() ?法。這個?法??會通過?個?法把所有的攔截器做聚合
之后?成?個攔截器調?鏈對象并返回。
對于同步請求,和異步不?樣的是在直接執? RealCall 對象的 execute() ?法。它會調? Dispatcher
對象的 executed(RealCall call) ?法將這個同步任務加?到 Dispatcher 對象的同步任務隊列中去。接
著會調? getResponseWithInterceptorChain() ?法獲取到請求的響應對象。最終,也是在 finally 中
將同步任務從 Dispatcher 對象的同步任務隊列中移除。
攔截器是 OkHttp 設計的精髓所在,每個攔截器負責不同的功能,使?責任鏈模式,通過鏈式調?執
?所有的攔截器對象中的 intercept() ?法。攔截器在某種程度上也借鑒了?絡協議中的分層思想,請
求時從最上層到最下層,響應時從最下層到最上層。
系統中已經默認添加了?試?定向、橋接攔、緩存、連接服務器、請求服務器等攔截器,但我們依然
可以?定義攔截器。
編寫起來也?常簡單,我們只需要實現 Interceptor 接?,并?寫其中的 intercept() ?法即可。
ART 和 Dalvik 虛擬機以及 JVM 的區別
?先看看 JVM 和 Dalvik 的區別。
Dalvik 基于寄存器,? JVM 是基于棧的。
Dalvik 運? dex ?件,? JVM 運? Java 字節碼。
再來看看 ART 和 Dalvik。
ART 和 Dalvik 的機制不同。在 Dalvik 下,應?每次運?的時候,字節碼都需要通過即時編譯器轉換
為機器碼,這會拖慢應?的運?效率,?在 ART 環境中,應?在第?次安裝的時候,字節碼就會預先
編譯成機器碼,使其成為真正的本地應?。這個過程叫做預編譯。這樣的話,應?的啟動和執?都變
得更加快速。
ART 擁有著給帶來系統性能的顯著提升,讓應?啟動更快、運?更快、體驗更流暢、觸摸反饋更及
時、更?的電池續航能?、?持更低的硬件等優勢。當然 ART 也有缺點,那就是機器碼占?的存儲空
間更?,?且應?的安裝時間會變?。
sleep 和 wait 有什么區別
功能差不多,都?來做線程控制,但 sleep 不會釋放同步鎖,wait() 會釋放同步鎖。
?法不同,sleep 可以?時間指定來使它?動醒過來,如果時間不到,我們只能通過調? interreput()
來強?打斷,? wait() 可以? notify() 直接喚起。
講講?前 Android 主要的熱修復?案
總的來說熱修復主要是利?三個?法:類加載、底層替換和 Instant Run。
采?類加載?案的?前以騰訊系為主,?如 Tinker 等,但這個?案需要?啟 APP 后讓 ClassLoader
?新加載新的類。因為類是?法被卸載的,要想?新加載新的類就需要?啟 APP。所以采?類加載?
案的熱修復框架是不能即時?效的。
?底層替換?案不會加載新類,?是直接在 Native 層修改原有的類,由于是在原有類進?修改,所
以會有?較多的限制,不能增減原有的?法和字段。但這樣的?法可以?即?效并且不需要?啟,?
前采?底層替換?案的主要以阿?系為主。?如阿?百川、AndFix 等。底層替換?案存在?個問題,
那就是需要針對 Dalvik 虛擬機和 ART 虛擬機做適配沒需要考慮指令集的兼容問題,需要 native 代
碼?持,兼容性也會有?定的影響。
除了資源修復,當然還可以借鑒 Instant Run 的原理。?如美團開源的 Robust。簡單看了下 Robust
插件的原理,它主要是對每個產品代碼的每個函數都在編譯打包的階段?動地插??段代碼,插?過
程對業務開發是完全透明的。Robust 會為每個 class 增加?個類型為 ChangeQuickRedirect 的靜態
成員,?在每個?法前都插?了使? changeQuickRedirect 相關的邏輯,當 changeQuickRedirect
不為 null 的時候,可能會執?到 accessDispatch 從?替換掉之前?的邏輯,達到 fix 的?的。
講講 HTTPS 的加密過程。
客戶端請求 HTTPS 連接 => 服務器返回證書(證書中包含公鑰)=> 客戶端產?密鑰并?服務器的公
鑰加密 => 客戶端發送加密密鑰 => 服務器返回加密的密?通信
AsyncTask 的原理
AsyncTask 內部封裝了兩個線程池和?個 Handler。
其中?個線程池負責任務調度,這個線程池實際上是?個靜態內部類,即所有 AsyncTask 對象公有
的;其內部維護了?個雙向隊列,容量根據元素數量調節;通過同步鎖修飾 execute(),保證
AsyncTask 的任務是串?執?;每次都是從隊列頭部取出任務。
另?個線程負責真正執?具體的線程任務。實際上就是?個已經配置好的可執?并?任務的線程池。
? Handler 主要負責異步通信和消息傳遞。
OkHttp 的優勢。
1. 對同?個主機發出的所有請求都可以共享相同的套接字連接;
2. 使?線程池來復?連接以提?效率;
3. 提供了對 gzip 的默認?持來降低傳輸內容的??;
4. 對 HTTP 響應的緩存機制,可以避免不必要的?絡請求;
5. 當?絡出問題時,OkHttp 會?動?試?個主機的多個 IP 地址;
Android 9.0 新特性
全?屏?持,劉海屏?機可能會成為趨勢;
通知欄多種通知;
多攝像頭的更多動畫;
GPS 定位外的 WIFI 定位;
?絡還有神經?絡;
Material Design 2.0;
Android Dashboard:?戶可以??看清楚??在?機上都做了什么,在哪個應?停留了多少時
間,停留過?還會有提示;
增加夜間模式;
Adavance Battery:需要?戶?動開啟該模式,機器學習,降低后臺占?,提升續航;
Shush:屏幕朝下完全進?勿擾狀態。除了鬧鐘,其他?動調整為靜?或者震動的模式。
Actions 和 Slices:檢測?戶?為,讓系統做出對應的動作。
HR 猜測?
你對未來有什么規劃?
你過往?作中取得過怎樣的成功,?遭遇了哪些失敗?
咕咚總監??我介紹
?試官您好,我是劉世麟,?常榮幸能參加咕咚的復試,下?我簡單介紹?下我的個?情況:我從實
習到現在?直在致學教育?作,從事 Android 開發,憑借良好的?作能?和溝通能?,連續兩年蟬聯
「優秀員?」稱號,在今年初被公司內聘為技術總監助理,協助技術總監開展部?管理和項?推動?
作。在?作之外,我喜歡編寫技術博客和在 GitHub 上貢獻開源代碼,?前在 GitHub 上總共擁有 7k
左右的 Star,數篇技術博客也有數?萬閱讀。我?常地熱愛移動開發,我希望我的技術能像我的發際
線?樣深?。不瞞您說,我?直認為咕咚是?家技術氛圍良好的企業,所以?直渴望加?,通過前?
和徐哥以及超哥的交流,我更加地堅定了我內?的想法。同樣,我認為我很適合咕咚的技術團隊。?
先,我?常的熱愛移動開發,?且學習能?較強,這和咕咚?前的團隊配置?常相符,所以我認為我
可以很快融?團隊,并迅速開啟開發?作。此外,咕咚?前采?的 Java 和 Kotlin 混合開發 以及
MVVM 架構都是我可以迅速上?的。周三和徐哥交流的咕咚項?的問題點,我也專?去做了探索。聽
聞咕咚最近在籌備團隊技術博客的事情,我希望并且相信我能在這?發揮好??的優勢!
今?頭條?試
1. ClassLoader 加載原理。
ClassLoader加載類的原理
1. 原理介紹
ClassLoader 使?的是雙親委托模型來搜索類的,每個 ClassLoader 實例都有?個?類加載
器的引?(不是繼承的關系,是?個包含的關系),虛擬機內置的類加載器( Bootstrap
ClassLoader )本身沒有?類加載器,但可以?作其它ClassLoader實例的的?類加載器。當?
個 ClassLoader 實例需要加載某個類時,它會試圖親?搜索某個類之前,先把這個任務委托給
它的?類加載器,這個過程是由上?下依次檢查的,?先由最頂層的類加載器 Bootstrap
ClassLoader 試圖加載,如果沒加載到,則把任務轉交給 Extension ClassLoader 試圖加
載,如果也沒加載到,則轉交給 App ClassLoader 進?加載,如果它也沒有加載得到的話,則
返回給委托的發起者,由它到指定的?件系統或?絡等URL中加載該類。如果它們都沒有加載到
這個類時,則拋出 ClassNotFoundException 異常。否則將這個找到的類?成?個類的定義,
并將它加載到內存當中,最后返回這個類在內存中的Class實例對象。
2、為什么要使?雙親委托這種模型呢?
因為這樣可以避免?復加載,當?親已經加載了該類的時候,就沒有必要 ClassLoader 再加載
?次。考慮到安全因素,我們試想?下,如果不使?這種委托模式,那我們就可以隨時使??定
義的String來動態替代java核?api中定義的類型,這樣會存在?常?的安全隱患,?雙親委托的
?式,就可以避免這種情況,因為String已經在啟動時就被引導類加載器( Bootstrcp
ClassLoader )加載,所以?戶?定義的 ClassLoader 永遠也?法加載?個??寫的String,
除?你改變JDK中 ClassLoader 搜索類的默認算法。
3、 但是JVM在搜索類的時候,?是如何判定兩個class是相同的呢?
JVM在判定兩個class是否相同時,不僅要判斷兩個類名是否相同,?且要判斷是否由同?個類
加載器實例加載的。只有兩者同時滿?的情況下,JVM才認為這兩個class是相同的。就算兩個
class是同?份class字節碼,如果被兩個不同的ClassLoader實例所加載,JVM也會認為它們是兩
個不同class。?如?絡上的?個Java
類 org.classloader.simple.NetClassLoaderSimple ,javac編譯之后?成字節碼?件
NetClassLoaderSimple.class , ClassLoaderA 和 ClassLoaderB 這兩個類加載器并讀取
了 NetClassLoaderSimple.class ?件,并分別定義出了 java.lang.Class 實例來表示這
個類,對于JVM來說,它們是兩個不同的實例對象,但它們確實是同?份字節碼?件,如果試圖
將這個Class實例?成具體的對象進?轉換時,就會拋運?時異
常 java.lang.ClassCaseException ,提示這是兩個不同的類型。
2. LeakCanary ?作原理,GC 如何回收,可以作為GC 根結點 的對象有哪些?
JVM垃圾回收的根對象的范圍有以下?種:
(1)虛擬機(JVM)棧中引?對象 (2)?法區中的類靜態屬性引?對象
(3)?法區中常量引?的對象(final 的常量值)
(4)本地?法棧JNI的引?對象
3. JVM 內存模型,如何理解 Java 虛函數表
4. 如何從 100 萬個數中找到最?的?百個數,考慮算法的時間復雜度和空間復雜度。
5. JS 和 Java Native 如何通信?
6. APP 加固怎么做?
7. MVP 和 MVC 以及 MVVM 的主要卻別,為什么 MVP 要? MVC 好?
8. Android 的混淆原理?
9. 如何設計?個安卓的畫圖庫,做到對擴展開放,對修改封閉,同時?保持獨?性
10. 怎樣做系統調度?
11. 數組實現隊列。
12. 實現 LRUCache。
13. HashMap 原理,Hash 碰撞,如何設計讓遍歷效率更??
14. ConcurrentHashMap 特點是什么?如何實現的。JDK 1.8 ?紅?樹實現,什么是紅?樹?
15. Syncronized 和 Lock 的區別,join 和 yield 的?法,volitate ?法,CAS 怎么實現的?
16. jvm內存結構 ?寫代理模式 ?寫線程安全的單例模式;GC算法有哪些?Android觸摸機制;除
了讀取trace?件,還能如何獲取到ANR異常?
17. ?個?列都有序的數組,給定?個數,找出具體的位置。
18. Retrofit 原理。
主要是通過動態代理將接?直接轉換成代理對象。動態代理和靜態代理的區別,動態代理直接在
虛擬機層?構建字節碼對象。
19. View?定義的流程,實現哪些?法。
20. 鏈表回?結構。
21. Android內存優化怎么優化。性能優化怎么優化。卡頓是什么原理。
22. 中序遍歷,?遞歸實現


------分隔線----------------------------
鋒哥公眾號


鋒哥微信號


日本色在线