2020年Android中高級面試題彙總,攢了一年的面試題及解答

2020年Android初級面試題彙總

JVM的加載原理

1.編譯機制

從下圖可以看出java文件經過了一次編譯後,java代碼編譯成java字節碼(class文件)。然後再不同平臺上使用不同的虛擬機(JVM)解釋,解釋成機器碼,然後執行。由此可見,如果我們要在mac系統上運行,只需要裝一個mac 的java虛擬機就可以了。這也就是java的一次編譯,到處運行

2.類的加載機制

1.加載

加載簡單來說分爲三步。

第一步:獲取二進制字節流也就是上面的class文件。

第二步:將靜態的存儲結構轉換爲方法區中的運行時數據結構。

第三步:生成一個對象放入java堆中,做爲對方法區的引用。

2.驗證

驗證主要是檢驗如下的幾項是否正確:

class文件的表示(魔數),class文件的版本號,class文件的每個部分是否正確(字段表、方法表等),驗證常量池(常量類型、常量類型數據結構是否正確,utf-8是否標準),元數據驗證(父類驗證,繼承驗證,final驗證),字節碼(指令)驗證,符號引用驗證(是否能根據符號找到對應的字段、表、方法等)
如果一項不對,就會驗證失敗。

3.準備

準備階段爲類變量分配內存 和設置類變量初始化。這個過程中,只對static類變量進行內存分配,這個時候只是分配內存,沒有進行復制,所有的類變量都是初始化值。如果是final的話,會直接對應到常量池中。會在準備階段直接賦值。

4.解析

解析階段是讀符號引用進行解析。將符號引用解析爲直接引用(指向目標的指針或者偏移量)。主要涉及到的解析有類,接口,字段,方法等。

5.初始化

初始化就是執行<clinte><linte>方法的過程, <clinte>對靜態變量,靜態代碼塊進行初始化,<linte>對類進行初始化。

6.使用

使用階段就是使用這個class。

7.卸載

卸載階段就是不在使用,將class給卸載。

3.類加載器

如圖所示,每當我們類被加載的時候,都會去走類加載器。
我們自定義加載器有一個父類,就是AppClassLoader,而AppClassLoader也有一個父類加載器ExtClassLoader,ExtClassLoader同樣也有一個父類加載器BootstarpClassLoader,BootstarpClassLoader就是最終的加載器了。

當加載類的時候,會啓動類加載器,會通過自己定義的類加載器去找AppClassLoader,然後通過AppClassLoader找到ExtClassLoader,再通過ExtClassLoader找到最終BootstarpClassLoader。可能看到這裏,有人人就有疑問了,爲什麼我有自己的加載器,幹嘛還要去找父類的加載器呢?這這種機制叫雙親委任機制,目的就是爲了加載累的安全。也就是說我父類加載的不給子類去加載,這樣保證最終都是BootstarpClassLoader去加載,可以保證一個類只會加載一次。而你判斷兩個對象時候一樣的時候,最重要的一個條件就是是不是一個加載器去加載的

全局變量和局部變量的區別

1.全局變量也稱爲外部變量,它是在函數外部定義的變量。 它不屬於哪一個函數,它屬於一個源程序文件。其作用域是整個源程序。在函數中使用全局變量,一般應作全局變量說明。 只有在函數內經過說明的全局變量才能使用。全局變量的說明符爲extern。 但在一個函數之前定義的全局變量,在該函數內使用可不再加以說明

2.局部變量也稱爲內部變量。局部變量是在函數內作定義說明的。其作用域僅限於函數內, 離開該函數後再使用這種變量是非法的

  1. 在同一源文件中,允許全局變量和局部變量同名。在局部變量的作用域內,全局變量不起作用。

4.從作用域看

全局變量具有全局作用域。全局變量只需在一個源文件中定義,就可以作用於所有的源文件。當然,其他不包含全局變量定義的源文件需要用extern 關鍵字再次聲明這個全局變量。

靜態局部變量具有局部作用域,它只被初始化一次,自從第一次被初始化直到程序運行結束都一直存在,它和全局變量的區別在於全局變量對所有的函數都是可見的,而靜態局部變量只對定義自己的函數體始終可見。

局部變量也只有局部作用域,它是自動對象(auto),它在程序運行期間不是一直存在,而是隻在函數執行期間存在,函數的一次調用執行結束後,變量被撤銷,其所佔用的內存也被收回。

靜態全局變量也具有全局作用域,它與全局變量的區別在於如果程序包含多個文件的話,它作用於定義它的文件裏,不能作用到其它文件裏,即被static關鍵字修飾過的變量具有文件作用域。這樣即使兩個不同的源文件都定義了相同名字的靜態全局變量,它們也是不同的變量。

5.從分配內存空間看:

全局變量,靜態局部變量,靜態全局變量都在靜態存儲區分配空間,而局部變量在棧裏分配空間。

全局變量本身就是靜態存儲方式,靜態全局變量當然也是靜態存儲方式。這兩者在存儲方式上並無不同。這兩者的區別雖在於非靜態全局變量的作用域是整個源程序,當一個源程序由多個源文件組成時,非靜態的全局變量在各個源文件中都是有效的。而靜態全局變量則限制了其作用域,即只在定義該變量的源文件內有效,在同一源程序的其它源文件中不能使用它。由於靜態全局變量的作用域侷限於一個源文件內,只能爲該源文件內的函數公用,因此可以避免在其它源文件中引起錯誤。

1)、靜態變量會被放在程序的靜態數據存儲區(全局可見)中,這樣可以在下一次調用的時候還可以保持原來的賦值。這一點是它與堆棧變量和堆變量的區別。
2)、變量用static告知編譯器,自己僅僅在變量的作用範圍內可見。這一點是它與全局變量的區別。

從以上分析可以看出,把局部變量改變爲靜態變量後是改變了它的存儲方式即改變了它的生存期。把全局變量改變爲靜態變量後是改變了它的作用域,限制了它的使用範圍。因此static 這個說明符在不同的地方所起的作用是不同的。應予以注意。

Tips:

A.若全局變量僅在單個C文件中訪問,則可以將這個變量修改爲靜態全局變量,以降低模塊間的耦合度;

B.若全局變量僅由單個函數訪問,則可以將這個變量改爲該函數的靜態局部變量,以降低模塊間的耦合度;

C.設計和使用訪問動態全局變量、靜態全局變量、靜態局部變量的函數時,需要考慮重入問題,因爲他們都放在靜態數據存儲區,全局可見;

D.如果我們需要一個可重入的函數,那麼,我們一定要避免函數中使用static變量(這樣的函數被稱爲:帶“內部存儲器”功能的的函數)

E.函數中必須要使用static變量情況:比如當某函數的返回值爲指針類型時,則必須是static的局部變量的地址作爲返回值,若爲auto類型,則返回爲錯指針。

靜態方法鎖和非靜態方法鎖區別

1.一個鎖的是類對象,一個鎖的是實例對象。
2.若類對象被lock,則類對象的所有同步方法(static synchronized func)全被lock。
3.若實例對象被lock,則該實例對象的所有同步方法(synchronized func)全被lock。
4.關於同一個類的方法上的鎖,來自於調用該方法的對象,如果調用該方法的對象是相同的,那麼鎖必然相同,否則就不相同。比如 new A().x() 和 new A().x(),對象不同,鎖不同,如果A的單利的,就能互斥。
5.靜態方法加鎖,能和所有其他靜態方法加鎖的 進行互斥

線程本地ThreadLocal的作用

當使用ThreadLocal維護變量時,ThreadLocal爲每個使用該變量的線程提供獨立的變量副本,所以每一個線程都可以獨立地改變自己的副本,而不會影響其它線程所對應的副本。

ThreadLocal與synchronized同步機制的比較

在同步機制中,通過對象的鎖機制保證同一時間只有一個線程訪問變量。這時該變量是多個線程共享的,使用同步機制要求程序慎密地分析什麼時候對變量進行讀寫,什麼時候需要鎖定某個對象,什麼時候釋放對象鎖等繁雜的問題,程序設計和編寫難度相對較大。

ThreadLocal是線程局部變量,是一種多線程間併發訪問變量的解決方案。和synchronized等加鎖的方式不同,ThreadLocal完全不提供鎖,而使用以空間換時間的方式,爲每個線程提供變量的獨立副本,以保證線程的安全。

單例模式幾種寫法

1.餓漢式

public class SingletonInstance {
    //私有構造方法
    private static SingletonInstance (){

    }
    //聲明成員變量
    private static SingletonInstance singletonInstance = new SingletonInstance();
    //對外提供接口獲取該實例
    public static SingletonInstance getSingletonInstance(){
        return singletonInstance ;
    }

}

2.懶漢式

public class SingletonInstance {
    //私有構造方法
    private SingletonInstance (){

    }
    //聲明成員變量
    private static SingletonInstance singletonInstance ;
    //對外提供接口獲取該實例
    public static SingletonInstance getSingletonInstance(){
        if(singletonInstance == null){
            singletonInstance = new SingletonInstance();
        }
        return singletonInstance ;
    }

}

3.double check lock(dcl)

public class SingletonInstance {
    //私有構造方法
    private SingletonInstance (){

    }
    //聲明成員變量
    private static SingletonInstance singletonInstance ;
    //對外提供接口獲取該實例
    public static SingletonInstance getSingletonInstance(){
        if(singletonInstance == null){
            synchronized (SingletonInstance.class){
                //兩次判斷是否爲null
                if(singletonInstance==null){
                    singletonInstance = new SingletonInstance();
                }
            }
        }
        return singletonInstance ;
    }

}

4.靜態內部類

public class SingletonInstance {
    //私有構造方法
    private SingletonInstance (){

    }

    private static class Builder{
        //聲明成員變量
        private static SingletonInstance singletonInstance = new SingletonInstance();
    }
    //對外提供接口獲取該實例
    public static SingletonInstance getSingletonInstance(){
        return Builder.singletonInstance ;
    }

}

死鎖

參考鏈接

死鎖,是指多個進程在運行過程中因爭奪資源而造成的一種僵局,當進程處於這種僵持狀態時,若無外力作用,它們都將無法再向前推進。 因此我們舉個例子來描述,如果此時有一個線程A,按照先鎖a再獲得鎖b的的順序獲得鎖,而在此同時又有另外一個線程B,按照先鎖b再鎖a的順序獲得鎖

1.死鎖產生的原因

a. 競爭資源

系統中的資源可以分爲兩類:

可剝奪資源,是指某進程在獲得這類資源後,該資源可以再被其他進程或系統剝奪,CPU和主存均屬於可剝奪性資源;

另一類資源是不可剝奪資源,當系統把這類資源分配給某進程後,再不能強行收回,只能在進程用完後自行釋放,如磁帶機、打印機等。

產生死鎖中的競爭資源之一指的是競爭不可剝奪資源(例如:系統中只有一臺打印機,可供進程P1使用,假定P1已佔用了打印機,若P2繼續要求打印機打印將阻塞)
產生死鎖中的競爭資源另外一種資源指的是競爭臨時資源(臨時資源包括硬件中斷、信號、消息、緩衝區內的消息等),通常消息通信順序進行不當,則會產生死鎖

b. 進程間推進順序非法

若P1保持了資源R1,P2保持了資源R2,系統處於不安全狀態,因爲這兩個進程再向前推進,便可能發生死鎖
例如,當P1運行到P1:Request(R2)時,將因R2已被P2佔用而阻塞;當P2運行到P2:Request(R1)時,也將因R1已被P1佔用而阻塞,於是發生進程死鎖

2.產生死鎖的必要條件

1.互斥條件:進程要求對所分配的資源進行排它性控制,即在一段時間內某資源僅爲一進程所佔用。
2.請求和保持條件:當進程因請求資源而阻塞時,對已獲得的資源保持不放。
3.不剝奪條件:進程已獲得的資源在未使用完之前,不能剝奪,只能在使用完時由自己釋放。
4.環路等待條件:在發生死鎖時,必然存在一個進程--資源的環形鏈。

3.解決死鎖的基本方法

1.資源一次性分配:一次性分配所有資源,這樣就不會再有請求了:(破壞請求條件)
只要有一個資源得不到分配,也不給這個進程分配其他的資源:(破壞請保持條件)
2.可剝奪資源:即當某進程獲得了部分資源,但得不到其它資源,則釋放已佔有的資源(破壞不可剝奪條件)
資源有序分配法:系統給每類資源賦予一個編號,每一個進程按編號遞增的順序請求資源,釋放則相反(破壞環路等待條件)

下次更新。。。

1.事件分發,dispatch和intercept和ontouchevent是如何執行的, onTouchEvent是如何執行的
2.場景1:addView方法加載A佈局然後remove(A),然後addView(B佈局)方法執行 場景2:addView方法加載A然後addView(B), requestView方法在場景1和2分別是如何執行的, 各自執行效率如何
3.佈局A經過scale進行屬性縮放, 縮放之後y軸對應的點座標有沒有變化
4.數組和鏈表區別
5.rxjava中map和flapmap區別
6.viewGroup和view的onDraw方法區別
7.recyclerview的緩存機制,item不可見時是否回執行onBind方法
8.requestLayout,measureLayout,invalidata區別
9.圖片壓縮加載
10.項目中的設計模式理解
11.進程間通訊
12.單例模式雙重校驗機制
13.內存優化
14.ANR
15.webview處理
16.hashmap怎麼實現的,擴展大小機制
17.圖片壓縮,狂傲壓縮和質量壓縮
18.https的ssl
19.有哪些進程通信,binder機制底層
20.activity裏面的setContentView做了什麼
21.acitvity,window,surface三者區別
22.listview和recyclerview區別
23.recyclerview四級緩存,自定義緩存如何做
24.GC回收機制,是如何世道那個對象被回收的
25.算法,輸入兩個鏈表,讓其合一起輸出一個鏈表

面試前的系統複習路線

有時候,選擇比努力更加重要,機遇比奮鬥更加重要。但是,機會只留給有準備的人。我們只有時刻準備着,才能在機會到來的時候,去抓住它。

這裏給大家分享一下我的面試複習路線,有需要的朋友可以參考一下:

1、看視頻進行系統學習

前幾年的Crud經歷,讓我明白自己真的算是菜雞中的戰鬥機,也正因爲Crud,導致自己技術比較零散,也不夠深入不夠系統,所以重新進行學習是很有必要的。我差的是系統知識,差的結構框架和思路,所以通過視頻來學習,效果更好,也更全面。關於視頻學習,個人可以推薦去B站進行學習,B站上有很多學習視頻,唯一的缺點就是免費的容易過時。

2、進行系統梳理知識,提升儲備

客戶端開發的知識點就那麼多,面試問來問去還是那麼點東西。所以面試沒有其他的訣竅,只看你對這些知識點準備的充分程度。so,出去面試時先看看自己複習到了哪個階段就好。

系統學習方向:

  • 架構師築基必備技能:深入Java泛型+註解深入淺出+併發編程+數據傳輸與序列化+Java虛擬機原理+反射與類加載+動態代理+高效IO

  • Android高級UI與FrameWork源碼:高級UI晉升+Framework內核解析+Android組件內核+數據持久化

  • 360°全方面性能調優:設計思想與代碼質量優化+程序性能優化+開發效率優化

  • 解讀開源框架設計思想:熱修復設計+插件化框架解讀+組件化框架設計+圖片加載框架+網絡訪問框架設計+RXJava響應式編程框架設計+IOC架構設計+Android架構組件Jetpack

  • NDK模塊開發:NDK基礎知識體系+底層圖片處理+音視頻開發

  • 微信小程序:小程序介紹+UI開發+API操作+微信對接

  • Hybrid 開發與Flutter:Html5項目實戰+Flutter進階

知識梳理完之後,就需要進行查漏補缺,所以針對這些知識點,我手頭上也準備了不少的電子書和筆記,這些筆記將各個知識點進行了完美的總結。

3、讀源碼,看實戰筆記,學習大神思路

“編程語言是程序員的表達的方式,而架構是程序員對世界的認知”。所以,程序員要想快速認知並學習架構,讀源碼是必不可少的。閱讀源碼,是解決問題 + 理解事物,更重要的:看到源碼背後的想法;程序員說:讀萬行源碼,行萬種實踐。

4、面試前夕,刷題衝刺

面試的前一週時間內,就可以開始刷題衝刺了。請記住,刷題的時候,技術的優先,算法的看些基本的,比如排序等即可,而智力題,除非是校招,否則一般不怎麼會問。

關於面試刷題,我個人也準備了一套系統的面試題,幫助你舉一反三:

以上內容均免費分享給大家,需要完整版的朋友,點這裏可以看到全部內容。或者關注主頁掃描加 微信 獲取。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章