自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

經(jīng)歷的某度的一場(chǎng)面試

移動(dòng)開發(fā) Android
九月份的時(shí)候有了換工作的躁動(dòng),然后投了某度的Android崗位,本以為像我這種非211、985沒工作經(jīng)驗(yàn)的渣渣只能被直接pass,結(jié)果卻意外的收到了電話,真是受寵若驚.經(jīng)過電面,技術(shù)三面,然后就是等通知到最后拿到了OFFER,如夢(mèng)一般,真是挺激動(dòng)的.

如夢(mèng)朦朧

九月份的時(shí)候有了換工作的躁動(dòng),然后投了某度的Android崗位,本以為像我這種非211、985沒工作經(jīng)驗(yàn)的渣渣只能被直接pass,結(jié)果卻意外的收到了電話,真是受寵若驚.經(jīng)過電面,技術(shù)三面,然后就是等通知到最后拿到了OFFER,如夢(mèng)一般,真是挺激動(dòng)的.

面試的準(zhǔn)備

當(dāng)收到HR的面試的通知還是很懵逼的,因?yàn)楦杏X自己突然啥都不會(huì)了,迅速鎮(zhèn)定下來,去網(wǎng)上找了一下某度的面試題,但是發(fā)現(xiàn)都只有提問了什么并沒有對(duì)所提問題的解答,那只能自力更生,像做試卷一樣,一遍總結(jié)一遍溫顧.其實(shí)大多都是平時(shí)開發(fā)中用到的,只是我們沒有總結(jié)過,被問起來的時(shí)候回答的難免會(huì)有點(diǎn)捉襟見肘,不能回答的很全面.下面為我個(gè)人總(bai)結(jié)(du)的,希望對(duì)你能有所幫助,但畢竟能力有限,有寫的不對(duì)的地方,還望輕噴.雖然噴我我也不會(huì)改的.

因?yàn)楸疚钠^長(zhǎng)建議收藏,在用到時(shí)候找出來看一眼.有一些知識(shí)點(diǎn)可能沒涉及到,以后會(huì)加以補(bǔ)足.因?yàn)槊嬖嚐o非是考察你對(duì)技術(shù)的理解和總結(jié),所以本篇的每個(gè)點(diǎn)總結(jié)的比較精簡(jiǎn),只是讓你大概的說出來,有的部分是需要能夠畫出原理圖并進(jìn)行解釋說明,這個(gè)要在工作中多積累.

JAVA

一. 類的加載過程,Person person = new Person();為例進(jìn)行說明。

  1. 因?yàn)閚ew用到了Person.class,所以會(huì)先找到Person.class文件,并加載到內(nèi)存中;
  2. 執(zhí)行該類中的static代碼塊,如果有的話,給Person.class類進(jìn)行初始化;
  3. 在堆內(nèi)存中開辟空間分配內(nèi)存地址;
  4. 在堆內(nèi)存中建立對(duì)象的特有屬性,并進(jìn)行默認(rèn)初始化;
  5. 對(duì)屬性進(jìn)行顯示初始化;
  6. 對(duì)對(duì)象進(jìn)行構(gòu)造代碼塊初始化;
  7. 對(duì)對(duì)象進(jìn)行與之對(duì)應(yīng)的構(gòu)造函數(shù)進(jìn)行初始化;
  8. 將內(nèi)存地址付給棧內(nèi)存中的p變量

二. JVM相關(guān)知識(shí),GC機(jī)制。

JVM基本構(gòu)成 

從上圖可知,JVM主要包括四個(gè)部分:

1.類加載器(ClassLoader):在JVM啟動(dòng)時(shí)或者在類運(yùn)行時(shí)將需要的class加載到JVM中。(下圖表示了從java源文件到JVM的整個(gè)過程,可配合理解。 

2.執(zhí)行引擎:負(fù)責(zé)執(zhí)行class文件中包含的字節(jié)碼指令;

3.內(nèi)存區(qū)(也叫運(yùn)行時(shí)數(shù)據(jù)區(qū)):是在JVM運(yùn)行的時(shí)候操作所分配的內(nèi)存區(qū)。運(yùn)行時(shí)內(nèi)存區(qū)主要可以劃分為5個(gè)區(qū)域,如圖: 

方法區(qū)(Method Area):用于存儲(chǔ)類結(jié)構(gòu)信息的地方,包括常量池、靜態(tài)變量、構(gòu)造函數(shù)等。雖然JVM規(guī)范把方法區(qū)描述為堆的一個(gè)邏輯部分, 但它卻有個(gè)別名non-heap(非堆),所以大家不要搞混淆了。方法區(qū)還包含一個(gè)運(yùn)行時(shí)常量池。

java堆(Heap):存儲(chǔ)java實(shí)例或者對(duì)象的地方。這塊是GC的主要區(qū)域。從存儲(chǔ)的內(nèi)容我們可以很容易知道,方法區(qū)和堆是被所有java線程共享的。

java棧(Stack):java??偸呛途€程關(guān)聯(lián)在一起,每當(dāng)創(chuàng)建一個(gè)線程時(shí),JVM就會(huì)為這個(gè)線程創(chuàng)建一個(gè)對(duì)應(yīng)的java棧。在這個(gè)java棧中又會(huì)包含多個(gè)棧幀,每運(yùn)行一個(gè)方法就創(chuàng)建一個(gè)棧幀,用于存儲(chǔ)局部變量表、操作棧、方法返回值等。每一個(gè)方法從調(diào)用直至執(zhí)行完成的過程,就對(duì)應(yīng)一個(gè)棧幀在java棧中入棧到出棧的過程。所以java棧是現(xiàn)成私有的。

程序計(jì)數(shù)器(PC Register):用于保存當(dāng)前線程執(zhí)行的內(nèi)存地址。由于JVM程序是多線程執(zhí)行的(線程輪流切換),所以為了保證線程切換回來后,還能恢復(fù)到原先狀態(tài),就需要一個(gè)獨(dú)立的計(jì)數(shù)器,記錄之前中斷的地方,可見程序計(jì)數(shù)器也是線程私有的。

本地方法棧(Native Method Stack):和java棧的作用差不多,只不過是為JVM使用到的native方法服務(wù)的。

4.本地方法接口:主要是調(diào)用C或C++實(shí)現(xiàn)的本地方法及返回結(jié)果。

GC機(jī)制

垃圾收集器一般必須完成兩件事:檢測(cè)出垃圾;回收垃圾。怎么檢測(cè)出垃圾?一般有以下幾種方法:

給一個(gè)對(duì)象添加引用計(jì)數(shù)器,每當(dāng)有個(gè)地方引用它,計(jì)數(shù)器就加1;引用失效就減1。好了,問題來了,如果我有兩個(gè)對(duì)象A和B,互相引用,除此之外,沒有其他任何對(duì)象引用它們,實(shí)際上這兩個(gè)對(duì)象已經(jīng)無法訪問,即是我們說的垃圾對(duì)象。但是互相引用,計(jì)數(shù)不為0,導(dǎo)致無法回收,所以還有另一種方法:

以根集對(duì)象為起始點(diǎn)進(jìn)行搜索,如果有對(duì)象不可達(dá)的話,即是垃圾對(duì)象。這里的根集一般包括java棧中引用的對(duì)象、方法區(qū)常良池中引用的對(duì)象、本地方法中引用的對(duì)象等。

總之,JVM在做垃圾回收的時(shí)候,會(huì)檢查堆中的所有對(duì)象是否會(huì)被這些根集對(duì)象引用,不能夠被引用的對(duì)象就會(huì)被垃圾收集器回收。一般回收算法也有如下幾種:

  1. 標(biāo)記-清除(Mark-sweep)
  2. 復(fù)制(Copying
  3. 標(biāo)記-整理(Mark-Compact)
  4. 分代收集算法

三. 類的加載器,雙親機(jī)制,Android的類加載器。

類的加載器

大家都知道,當(dāng)我們寫好一個(gè)Java程序之后,不是管是CS還是BS應(yīng)用,都是由若干個(gè).class文件組織而成的一個(gè)完整的Java應(yīng)用程序,當(dāng)程序在運(yùn)行時(shí),即會(huì)調(diào)用該程序的一個(gè)入口函數(shù)來調(diào)用系統(tǒng)的相關(guān)功能,而這些功能都被封裝在不同的class文件當(dāng)中,所以經(jīng)常要從這個(gè)class文件中要調(diào)用另外一個(gè)class文件中的方法,如果另外一個(gè)文件不存在的,則會(huì)引發(fā)系統(tǒng)異常。而程序在啟動(dòng)的時(shí)候,并不會(huì)一次性加載程序所要用的所有class文件,而是根據(jù)程序的需要,通過Java的類加載機(jī)制(ClassLoader)來動(dòng)態(tài)加載某個(gè)class文件到內(nèi)存當(dāng)中的,從而只有class文件被載入到了內(nèi)存之后,才能被其它c(diǎn)lass所引用。所以ClassLoader就是用來動(dòng)態(tài)加載class文件到內(nèi)存當(dāng)中用的。

雙親機(jī)制

ClassLoader使用的是雙親委托模型來搜索類的,每個(gè)ClassLoader實(shí)例都有一個(gè)父類加載器的引用(不是繼承的關(guān)系,是一個(gè)包含的關(guān)系),虛擬機(jī)內(nèi)置的類加載器(Bootstrap ClassLoader)本身沒有父類加載器,但可以用作其它ClassLoader實(shí)例的的父類加載器。當(dāng)一個(gè)ClassLoader實(shí)例需要加載某個(gè)類時(shí),它會(huì)試圖親自搜索某個(gè)類之前,先把這個(gè)任務(wù)委托給它的父類加載器,這個(gè)過程是由上至下依次檢查的,首先由最頂層的類加載器Bootstrap ClassLoader試圖加載,如果沒加載到,則把任務(wù)轉(zhuǎn)交給Extension ClassLoader試圖加載,如果也沒加載到,則轉(zhuǎn)交給App ClassLoader 進(jìn)行加載,如果它也沒有加載得到的話,則返回給委托的發(fā)起者,由它到指定的文件系統(tǒng)或網(wǎng)絡(luò)等URL中加載該類。如果它們都沒有加載到這個(gè)類時(shí),則拋出ClassNotFoundException異常。否則將這個(gè)找到的類生成一個(gè)類的定義,并將它加載到內(nèi)存當(dāng)中,最后返回這個(gè)類在內(nèi)存中的Class實(shí)例對(duì)象。

因?yàn)檫@樣可以避免重復(fù)加載,當(dāng)父親已經(jīng)加載了該類的時(shí)候,就沒有必要子ClassLoader再加載一次??紤]到安全因素,我們?cè)囅胍幌?,如果不使用這種委托模式,那我們就可以隨時(shí)使用自定義的String來動(dòng)態(tài)替代java核心api中定義的類型,這樣會(huì)存在非常大的安全隱患,而雙親委托的方式,就可以避免這種情況,因?yàn)镾tring已經(jīng)在啟動(dòng)時(shí)就被引導(dǎo)類加載器(Bootstrcp ClassLoader)加載,所以用戶自定義的ClassLoader永遠(yuǎn)也無法加載一個(gè)自己寫的String,除非你改變JDK中ClassLoader搜索類的默認(rèn)算法。

JVM在判定兩個(gè)class是否相同時(shí),不僅要判斷兩個(gè)類名是否相同,而且要判斷是否由同一個(gè)類加載器實(shí)例加載的。只有兩者同時(shí)滿足的情況下,JVM才認(rèn)為這兩個(gè)class是相同的。就算兩個(gè)class是同一份class字節(jié)碼,如果被兩個(gè)不同的ClassLoader實(shí)例所加載,JVM也會(huì)認(rèn)為它們是兩個(gè)不同class。比如網(wǎng)絡(luò)上的一個(gè)Java類org.classloader.simple.NetClassLoaderSimple,javac編譯之后生成字節(jié)碼文件NetClassLoaderSimple.class,ClassLoaderA和ClassLoaderB這兩個(gè)類加載器并讀取了NetClassLoaderSimple.class文件,并分別定義出了java.lang.Class實(shí)例來表示這個(gè)類,對(duì)于JVM來說,它們是兩個(gè)不同的實(shí)例對(duì)象,但它們確實(shí)是同一份字節(jié)碼文件,如果試圖將這個(gè)Class實(shí)例生成具體的對(duì)象進(jìn)行轉(zhuǎn)換時(shí),就會(huì)拋運(yùn)行時(shí)異常java.lang.ClassCaseException,提示這是兩個(gè)不同的類型。

Android類加載器

對(duì)于Android而言,最終的apk文件包含的是dex類型的文件,dex文件是將class文件重新打包,打包的規(guī)則又不是簡(jiǎn)單地壓縮,而是完全對(duì)class文件內(nèi)部的各種函數(shù)表,變量表進(jìn)行優(yōu)化,產(chǎn)生一個(gè)新的文件,即dex文件。因此加載這種特殊的Class文件就需要特殊的類加載器DexClassLoader。

四. 集合框架,list,map,set都有哪些具體的實(shí)現(xiàn)類,區(qū)別都是什么?

1.List,Set都是繼承自Collection接口,Map則不是;

2.List特點(diǎn):元素有放入順序,元素可重復(fù);

  • Set特點(diǎn):元素?zé)o放入順序,元素不可重復(fù),重復(fù)元素會(huì)覆蓋掉,(注意:元素雖然無放入順序,但是元素在set中的位置是有該元素的HashCode決定的,其位置其實(shí)是固定的,加入Set 的Object必須定義equals()方法;

另外list支持for循環(huán),也就是通過下標(biāo)來遍歷,也可以用迭代器,但是set只能用迭代,因?yàn)樗麩o序,無法用下標(biāo)來取得想要的值)。

3.Set和List對(duì)比:

  • Set:檢索元素效率低下,刪除和插入效率高,插入和刪除不會(huì)引起元素位置改變。
  • List:和數(shù)組類似,List可以動(dòng)態(tài)增長(zhǎng),查找元素效率高,插入刪除元素效率低,因?yàn)闀?huì)引起其他元素位置改變。

4.Map適合儲(chǔ)存鍵值對(duì)的數(shù)據(jù)。

5.線程安全集合類與非線程安全集合類

  • LinkedList、ArrayList、HashSet是非線程安全的,Vector是線程安全的;
  • HashMap是非線程安全的,HashTable是線程安全的;
  • StringBuilder是非線程安全的,StringBuffer是線程安全的。

下面是這些類具體的使用介紹:

ArrayList與LinkedList的區(qū)別和適用場(chǎng)景

優(yōu)點(diǎn):ArrayList是實(shí)現(xiàn)了基于動(dòng)態(tài)數(shù)組的數(shù)據(jù)結(jié)構(gòu),因?yàn)榈刂愤B續(xù),一旦數(shù)據(jù)存儲(chǔ)好了,查詢操作效率會(huì)比較高(在內(nèi)存里是連著放的)。

缺點(diǎn):因?yàn)榈刂愤B續(xù), ArrayList要移動(dòng)數(shù)據(jù),所以插入和刪除操作效率比較低。

優(yōu)點(diǎn):LinkedList基于鏈表的數(shù)據(jù)結(jié)構(gòu),地址是任意的,所以在開辟內(nèi)存空間的時(shí)候不需要等一個(gè)連續(xù)的地址,對(duì)于新增和刪除操作add和remove,LinedList比較占優(yōu)勢(shì)。LinkedList 適用于要頭尾操作或插入指定位置的場(chǎng)景。

缺點(diǎn):因?yàn)長(zhǎng)inkedList要移動(dòng)指針,所以查詢操作性能比較低。

適用場(chǎng)景分析:

當(dāng)需要對(duì)數(shù)據(jù)進(jìn)行對(duì)此訪問的情況下選用ArrayList,當(dāng)需要對(duì)數(shù)據(jù)進(jìn)行多次增加刪除修改時(shí)采用LinkedList。

ArrayList與Vector的區(qū)別和適用場(chǎng)景

ArrayList有三個(gè)構(gòu)造方法:

  • public ArrayList(int initialCapacity)//構(gòu)造一個(gè)具有指定初始容量的空列表。
  • public ArrayList()//構(gòu)造一個(gè)初始容量為10的空列表。
  • public ArrayList(Collection c)//構(gòu)造一個(gè)包含指定 collection 的元素的列表

Vector有四個(gè)構(gòu)造方法:

  • public Vector()//使用指定的初始容量和等于零的容量增量構(gòu)造一個(gè)空向量。
  • public Vector(int initialCapacity)//構(gòu)造一個(gè)空向量,使其內(nèi)部數(shù)據(jù)數(shù)組的大小,其標(biāo)準(zhǔn)容量增量為零。
  • public Vector(Collection c)//構(gòu)造一個(gè)包含指定 collection 中的元素的向量
  • public Vector(int initialCapacity,int capacityIncrement)//使用指定的初始容量和容量增量構(gòu)造一個(gè)空的向量

ArrayList和Vector都是用數(shù)組實(shí)現(xiàn)的,主要有這么三個(gè)區(qū)別:

  1. Vector是多線程安全的,線程安全就是說多線程訪問同一代碼,不會(huì)產(chǎn)生不確定的結(jié)果。而ArrayList不是,這個(gè)可以從源碼中看出,Vector類中的方法很多有synchronized進(jìn)行修飾,這樣就導(dǎo)致了Vector在效率上無法與ArrayList相比;
  2. 兩個(gè)都是采用的線性連續(xù)空間存儲(chǔ)元素,但是當(dāng)空間不足的時(shí)候,兩個(gè)類的增加方式是不同。
  3. Vector可以設(shè)置增長(zhǎng)因子,而ArrayList不可以。
  4. Vector是一種老的動(dòng)態(tài)數(shù)組,是線程同步的,效率很低,一般不贊成使用。

適用場(chǎng)景:

  1. Vector是線程同步的,所以它也是線程安全的,而ArrayList是線程異步的,是不安全的。如果不考慮到線程的安全因素,一般用ArrayList效率比較高。
  2. 如果集合中的元素的數(shù)目大于目前集合數(shù)組的長(zhǎng)度時(shí),在集合中使用數(shù)據(jù)量比較大的數(shù)據(jù),用Vector有一定的優(yōu)勢(shì)。

HashSet與Treeset的適用場(chǎng)景

  1. TreeSet 是二叉樹(紅黑樹的樹據(jù)結(jié)構(gòu))實(shí)現(xiàn)的,Treeset中的數(shù)據(jù)是自動(dòng)排好序的,不允許放入null值。
  2. HashSet 是哈希表實(shí)現(xiàn)的,HashSet中的數(shù)據(jù)是無序的,可以放入null,但只能放入一個(gè)null,兩者中的值都不能重復(fù),就如數(shù)據(jù)庫(kù)中唯一約束。
  3. HashSet要求放入的對(duì)象必須實(shí)現(xiàn)HashCode()方法,放入的對(duì)象,是以hashcode碼作為標(biāo)識(shí)的,而具有相同內(nèi)容的String對(duì)象,hashcode是一樣,所以放入的內(nèi)容不能重復(fù)。但是同一個(gè)類的對(duì)象可以放入不同的實(shí)例。

適用場(chǎng)景分析:

HashSet是基于Hash算法實(shí)現(xiàn)的,其性能通常都優(yōu)于TreeSet。為快速查找而設(shè)計(jì)的Set,我們通常都應(yīng)該使用HashSet,在我們需要排序的功能時(shí),我們才使用TreeSet。

HashMap與TreeMap、HashTable的區(qū)別及適用場(chǎng)景

HashMap:基于哈希表(散列表)實(shí)現(xiàn)。使用HashMap要求添加的鍵類明確定義了hashCode()和equals()[可以重寫hashCode()和equals()],為了優(yōu)化HashMap空間的使用,您可以調(diào)優(yōu)初始容量和負(fù)載因子。其中散列表的沖突處理主要分兩種,一種是開放定址法,另一種是鏈表法。HashMap的實(shí)現(xiàn)中采用的是鏈表法。

TreeMap:非線程安全基于紅黑樹實(shí)現(xiàn)。TreeMap沒有調(diào)優(yōu)選項(xiàng),因?yàn)樵摌淇偺幱谄胶鉅顟B(tài)。

適用場(chǎng)景分析:

HashMap和HashTable:HashMap去掉了HashTable的contains方法,但是加上了containsValue()和containsKey()方法。HashTable同步的,而HashMap是非同步的,效率上比HashTable要高。HashMap允許空鍵值,而HashTable不允許。

HashMap:適用于Map中插入、刪除和定位元素。

Treemap:適用于按自然順序或自定義順序遍歷鍵(key)。

(ps:其實(shí)我們工作的過程中對(duì)集合的使用是很頻繁的,稍加注意并總結(jié)積累一下,在面試的時(shí)候應(yīng)該會(huì)回答的很輕松)

五. concurrentHashmap原理,原子類。

ConcurrentHashMap作為一種線程安全且高效的哈希表的解決方案,尤其其中的"分段鎖"的方案,相比HashTable的全表鎖在性能上的提升非常之大.

六. volatile原理

在《Java并發(fā)編程:核心理論》一文中,我們已經(jīng)提到過可見性、有序性及原子性問題,通常情況下我們可以通過Synchronized關(guān)鍵字來解決這些個(gè)問題,不過如果對(duì)Synchronized原理有了解的話,應(yīng)該知道Synchronized是一個(gè)比較重量級(jí)的操作,對(duì)系統(tǒng)的性能有比較大的影響,所以,如果有其他解決方案,我們通常都避免使用Synchronized來解決問題。而volatile關(guān)鍵字就是Java中提供的另一種解決可見性和有序性問題的方案。對(duì)于原子性,需要強(qiáng)調(diào)一點(diǎn),也是大家容易誤解的一點(diǎn):對(duì)volatile變量的單次讀/寫操作可以保證原子性的,如long和double類型變量,但是并不能保證i++這種操作的原子性,因?yàn)楸举|(zhì)上i++是讀、寫兩次操作。

參考文章插好眼了等傳送

七. 多線程的使用場(chǎng)景

使用多線程就一定效率高嗎? 有時(shí)候使用多線程并不是為了提高效率,而是使得CPU能夠同時(shí)處理多個(gè)事件。

1).為了不阻塞主線程,啟動(dòng)其他線程來做好事的事情,比如APP中耗時(shí)操作都不在UI中做.

2).實(shí)現(xiàn)更快的應(yīng)用程序,即主線程專門監(jiān)聽用戶請(qǐng)求,子線程用來處理用戶請(qǐng)求,以獲得大的吞吐量.感覺這種情況下,多線程的效率未必高。 這種情況下的多線程是為了不必等待, 可以并行處理多條數(shù)據(jù)。

比如JavaWeb的就是主線程專門監(jiān)聽用戶的HTTP請(qǐng)求,然后啟動(dòng)子線程去處理用戶的HTTP請(qǐng)求。

3).某種雖然優(yōu)先級(jí)很低的服務(wù),但是卻要不定時(shí)去做。

比如Jvm的垃圾回收。

4.)某種任務(wù),雖然耗時(shí),但是不耗CPU的操作時(shí),開啟多個(gè)線程,效率會(huì)有顯著提高。

比如讀取文件,然后處理。 磁盤IO是個(gè)很耗費(fèi)時(shí)間,但是不耗CPU計(jì)算的工作。 所以可以一個(gè)線程讀取數(shù)據(jù),一個(gè)線程處理數(shù)據(jù)??隙ū纫粋€(gè)線程讀取數(shù)據(jù),然后處理效率高。 因?yàn)閮蓚€(gè)線程的時(shí)候充分利用了CPU等待磁盤IO的空閑時(shí)間。

八. JAVA常量池

**

  • a.當(dāng)數(shù)值范圍為-128~127時(shí):如果兩個(gè)new出來Integer對(duì)象,即使值相同,通過“==”比較結(jié)果為false,但兩個(gè)對(duì)象直接賦值,則通過“==”比較結(jié)果為“true,這一點(diǎn)與String非常相似。
  • b.當(dāng)數(shù)值不在-128~127時(shí),無論通過哪種方式,即使兩個(gè)對(duì)象的值相等,通過“==”比較,其結(jié)果為false;
  • c.當(dāng)一個(gè)Integer對(duì)象直接與一個(gè)int基本數(shù)據(jù)類型通過“==”比較,其結(jié)果與第一點(diǎn)相同;
  • d.Integer對(duì)象的hash值為數(shù)值本身;

**

在Integer類中有一個(gè)靜態(tài)內(nèi)部類IntegerCache,在IntegerCache類中有一個(gè)Integer數(shù)組,用以緩存當(dāng)數(shù)值范圍為-128~127時(shí)的Integer對(duì)象。

九. 簡(jiǎn)單介紹一下java中的泛型,泛型擦除以及相關(guān)的概念。

泛型是Java SE 1.5的新特性,泛型的本質(zhì)是參數(shù)化類型,也就是說所操作的數(shù)據(jù)類型被指定為一個(gè)參數(shù)。這種參數(shù)類型可以用在類、接口和方法的創(chuàng)建中,分別稱為泛型類、泛型接口、泛型方法。 Java語(yǔ)言引入泛型的好處是安全簡(jiǎn)單。

在Java SE 1.5之前,沒有泛型的情況的下,通過對(duì)類型Object的引用來實(shí)現(xiàn)參數(shù)的“任意化”,“任意化”帶來的缺點(diǎn)是要做顯式的強(qiáng)制類型轉(zhuǎn)換,而這種轉(zhuǎn)換是要求開發(fā)者對(duì)實(shí)際參數(shù)類型可以預(yù)知的情況下進(jìn)行的。對(duì)于強(qiáng)制類型轉(zhuǎn)換錯(cuò)誤的情況,編譯器可能不提示錯(cuò)誤,在運(yùn)行的時(shí)候才出現(xiàn)異常,這是一個(gè)安全隱患。

泛型的好處是在編譯的時(shí)候檢查類型安全,并且所有的強(qiáng)制轉(zhuǎn)換都是自動(dòng)和隱式的,提高代碼的重用率。

  1. 泛型的類型參數(shù)只能是類類型(包括自定義類),不能是簡(jiǎn)單類型。
  2. 同一種泛型可以對(duì)應(yīng)多個(gè)版本(因?yàn)閰?shù)類型是不確定的),不同版本的泛型類實(shí)例是不兼容的。
  3. 泛型的類型參數(shù)可以有多個(gè)。
  4. 泛型的參數(shù)類型可以使用extends語(yǔ)句,例如。習(xí)慣上稱為“有界類型”。
  5. 泛型的參數(shù)類型還可以是通配符類型。例如Class classType = Class.forName("java.lang.String");

泛型擦除以及相關(guān)的概念

Java中的泛型基本上都是在編譯器這個(gè)層次來實(shí)現(xiàn)的。在生成的Java字節(jié)碼中是不包含泛型中的類型信息的。使用泛型的時(shí)候加上的類型參數(shù),會(huì)在編譯器在編譯的時(shí)候去掉。這個(gè)過程就稱為類型擦除。

  1. 類型擦除引起的問題及解決方法
  2. 先檢查,在編譯,以及檢查編譯的對(duì)象和引用傳遞的問題
  3. 自動(dòng)類型轉(zhuǎn)換
  4. 類型擦除與多態(tài)的沖突和解決方法
  5. 泛型類型變量不能是基本數(shù)據(jù)類型
  6. 運(yùn)行時(shí)類型查詢
  7. 異常中使用泛型的問題
  8. 數(shù)組(這個(gè)不屬于類型擦除引起的問題)
  9. 類型擦除后的沖突
  10. 泛型在靜態(tài)方法和靜態(tài)類中的問題

Android

一. Handler機(jī)制

Android 的消息機(jī)制也就是 handler 機(jī)制,創(chuàng)建 handler 的時(shí)候會(huì)創(chuàng)建一個(gè) looper ( 通過 looper.prepare() 來創(chuàng)建 ),looper 一般為主線程 looper.

handler 通過 send 發(fā)送消息 (sendMessage) ,當(dāng)然 post 一系列方法最終也是通過 send 來實(shí)現(xiàn)的,在 send 方法中handler 會(huì)通過 enqueueMessage() 方法中的 enqueueMessage(msg,millis )向消息隊(duì)列 MessageQueue 插入一條消息,同時(shí)會(huì)把本身的 handler 通過 msg.target = this 傳入.

Looper 是一個(gè)死循環(huán),不斷的讀取MessageQueue中的消息,loop 方法會(huì)調(diào)用 MessageQueue 的 next 方法來獲取新的消息,next 操作是一個(gè)阻塞操作,當(dāng)沒有消息的時(shí)候 next 方法會(huì)一直阻塞,進(jìn)而導(dǎo)致 loop 一直阻塞,當(dāng)有消息的時(shí)候,Looper 就會(huì)處理消息 Looper 收到消息之后就開始處理消息: msg.target.dispatchMessage(msg),當(dāng)然這里的 msg.target 就是上面?zhèn)鬟^來的發(fā)送這條消息的 handler 對(duì)象,這樣 handler 發(fā)送的消息最終又交給他的dispatchMessage方法來處理了,這里不同的是,handler 的 dispatchMessage 方法是在創(chuàng)建 Handler時(shí)所使用的 Looper 中執(zhí)行的,這樣就成功的將代碼邏輯切換到了主線程了.

Handler 處理消息的過程是:首先,檢查Message 的 callback 是否為 null,不為 null 就通過 handleCallBack 來處理消息,Message 的 callback 是一個(gè) Runnable 對(duì)象,實(shí)際上是 handler 的 post 方法所傳遞的 Runnable 參數(shù).其次是檢查 mCallback 是 否為 null,不為 null 就調(diào)用 mCallback 的handleMessage 方法來處理消息.

二. View的繪制流程

View的繪制流程:OnMeasure()——>OnLayout()——>OnDraw()

各步驟的主要工作:

測(cè)量視圖大小。從頂層父View到子View遞歸調(diào)用measure方法,measure方法又回調(diào)OnMeasure。

確定View位置,進(jìn)行頁(yè)面布局。從頂層父View向子View的遞歸調(diào)用view.layout方法的過程,即父View根據(jù)上一步measure子View所得到的布局大小和布局參數(shù),將子View放在合適的位置上。

繪制視圖:ViewRoot創(chuàng)建一個(gè)Canvas對(duì)象,然后調(diào)用OnDraw()。六個(gè)步驟:①、繪制視圖的背景;②、保存畫布的圖層(Layer);③、繪制View的內(nèi)容;④、繪制View子視圖,如果沒有就不用;⑤、還原圖層(Layer);⑥、繪制滾動(dòng)條。

三. 事件傳遞機(jī)制

  • Android事件分發(fā)機(jī)制的本質(zhì)是要解決:點(diǎn)擊事件由哪個(gè)對(duì)象發(fā)出,經(jīng)過哪些對(duì)象,最終達(dá)到哪個(gè)對(duì)象并最終得到處理。這里的對(duì)象是指Activity、ViewGroup、View.
  • Android中事件分發(fā)順序:Activity(Window) -> ViewGroup -> View.
  • 事件分發(fā)過程由dispatchTouchEvent() 、onInterceptTouchEvent()和onTouchEvent()三個(gè)方法協(xié)助完成

設(shè)置Button按鈕來響應(yīng)點(diǎn)擊事件事件傳遞情況:(如下圖)

布局如下: 

  • 最外層:Activiy A,包含兩個(gè)子View:ViewGroup B、View C
  • 中間層:ViewGroup B,包含一個(gè)子View:View C
  • 最內(nèi)層:View C

假設(shè)用戶首先觸摸到屏幕上View C上的某個(gè)點(diǎn)(如圖中黃色區(qū)域),那么Action_DOWN事件就在該點(diǎn)產(chǎn)生,然后用戶移動(dòng)手指并最后離開屏幕。

  • 按鈕點(diǎn)擊事件:
  • DOWN事件被傳遞給C的onTouchEvent方法,該方法返回true,表示處理這個(gè)事件;
  • 因?yàn)镃正在處理這個(gè)事件,那么DOWN事件將不再往上傳遞給B和A的onTouchEvent();
  • 該事件列的其他事件(Move、Up)也將傳遞給C的onTouchEvent(); 

(記住這個(gè)圖的傳遞順序,面試的時(shí)候能夠畫出來,就很詳細(xì)了)

四. Binder機(jī)制

1.了解Binder

在Android系統(tǒng)中,每一個(gè)應(yīng)用程序都運(yùn)行在獨(dú)立的進(jìn)程中,這也保證了當(dāng)其中一個(gè)程序出現(xiàn)異常而不會(huì)影響另一個(gè)應(yīng)用程序的正常運(yùn)轉(zhuǎn)。在許多情況下,我們activity都會(huì)與各種系統(tǒng)的service打交道,很顯然,我們寫的程序中activity與系統(tǒng)service肯定不是同一個(gè)進(jìn)程,但是它們之間是怎樣實(shí)現(xiàn)通信的呢?所以Binder是android中一種實(shí)現(xiàn)進(jìn)程間通信(IPC)的方式之一。

1).首先,Binder分為Client和Server兩個(gè)進(jìn)程。

注意,Client和Server是相對(duì)的。誰發(fā)消息,誰就是Client,誰接收消息,誰就是Server。

舉個(gè)例子,兩個(gè)進(jìn)程A和B之間使用Binder通信,進(jìn)程A發(fā)消息給進(jìn)程B,那么這時(shí)候A是Binder Client,B是Binder Server;進(jìn)程B發(fā)消息給進(jìn)程A,那么這時(shí)候B是Binder Client,A是Binder Server——其實(shí)這么說雖然簡(jiǎn)單了,但還是不太嚴(yán)謹(jǐn),我們先這么理解著。

2).其次,我們看下面這個(gè)圖(摘自田維術(shù)的博客),基本說明白了Binder的組成解構(gòu): 


圖中的IPC就是進(jìn)程間通信的意思。

圖中的ServiceManager,負(fù)責(zé)把Binder Server注冊(cè)到一個(gè)容器中。

有人把ServiceManager比喻成電話局,存儲(chǔ)著每個(gè)住宅的座機(jī)電話,還是很恰當(dāng)?shù)?。張三給李四打電話,撥打電話號(hào)碼,會(huì)先轉(zhuǎn)接到電話局,電話局的接線員查到這個(gè)電話號(hào)碼的地址,因?yàn)槔钏牡碾娫捥?hào)碼之前在電話局注冊(cè)過,所以就能撥通;沒注冊(cè),就會(huì)提示該號(hào)碼不存在。

對(duì)照著Android Binder機(jī)制,對(duì)著上面這圖,張三就是Binder Client,李四就是Binder Server,電話局就是ServiceManager,電話局的接線員在這個(gè)過程中做了很多事情,對(duì)應(yīng)著圖中的Binder驅(qū)動(dòng).

3).接下來我們看Binder通信的過程,還是摘自田維術(shù)博客的一張圖: 


注:圖中的SM也就是ServiceManager。

我們看到,Client想要直接調(diào)用Server的add方法,是不可以的,因?yàn)樗鼈冊(cè)诓煌倪M(jìn)程中,這時(shí)候就需要Binder來幫忙了。

  • 首先是Server在SM這個(gè)容器中注冊(cè)。
  • 其次,Client想要調(diào)用Server的add方法,就需要先獲取Server對(duì)象, 但是SM不會(huì)把真正的Server對(duì)象返回給Client,而是把Server的一個(gè)代理對(duì)象返回給Client,也就是Proxy。
  • 然后,Client調(diào)用Proxy的add方法,SM會(huì)幫他去調(diào)用Server的add方法,并把結(jié)果返回給Client。

以上這3步,Binder驅(qū)動(dòng)出了很多力,但我們不需要知道Binder驅(qū)動(dòng)的底層實(shí)現(xiàn),涉及到C++的代碼了——把有限的時(shí)間去做更有意義的事情。

(ps:以上節(jié)選自包建強(qiáng)老師的文章點(diǎn)我點(diǎn)我 ).

2.為什么android選用Binder來實(shí)現(xiàn)進(jìn)程間通信?

1).可靠性。在移動(dòng)設(shè)備上,通常采用基于Client-Server的通信方式來實(shí)現(xiàn)互聯(lián)網(wǎng)與設(shè)備間的內(nèi)部通信。目前l(fā)inux支持IPC包括傳統(tǒng)的管道,System V IPC,即消息隊(duì)列/共享內(nèi)存/信號(hào)量,以及socket中只有socket支持Client-Server的通信方式。Android系統(tǒng)為開發(fā)者提供了豐富進(jìn)程間通信的功能接口,媒體播放,傳感器,無線傳輸。這些功能都由不同的server來管理。開發(fā)都只關(guān)心將自己應(yīng)用程序的client與server的通信建立起來便可以使用這個(gè)服務(wù)。毫無疑問,如若在底層架設(shè)一套協(xié)議來實(shí)現(xiàn)Client-Server通信,增加了系統(tǒng)的復(fù)雜性。在資源有限的手機(jī) 上來實(shí)現(xiàn)這種復(fù)雜的環(huán)境,可靠性難以保證。

2).傳輸性能。socket主要用于跨網(wǎng)絡(luò)的進(jìn)程間通信和本機(jī)上進(jìn)程間的通信,但傳輸效率低,開銷大。消息隊(duì)列和管道采用存儲(chǔ)-轉(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/管道/消息隊(duì)列:2次。

3).安全性。Android是一個(gè)開放式的平臺(tái),所以確保應(yīng)用程序安全是很重要的。Android對(duì)每一個(gè)安裝應(yīng)用都分配了UID/PID,其中進(jìn)程的UID是可用來鑒別進(jìn)程身份。傳統(tǒng)的只能由用戶在數(shù)據(jù)包里填寫UID/PID,這樣不可靠,容易被惡意程序利用。而我們要求由內(nèi)核來添加可靠的UID。

所以,出于可靠性、傳輸性、安全性。android建立了一套新的進(jìn)程間通信方式。

五. Android中進(jìn)程的級(jí)別,以及各自的區(qū)別。

1、前臺(tái)進(jìn)程

用戶當(dāng)前正在做的事情需要這個(gè)進(jìn)程。如果滿足下面的條件之一,一個(gè)進(jìn)程就被認(rèn)為是前臺(tái)進(jìn)程:

  1. 這個(gè)進(jìn)程擁有一個(gè)正在與用戶交互的Activity(這個(gè)Activity的onResume()方法被調(diào)用)。
  2. 這個(gè)進(jìn)程擁有一個(gè)綁定到正在與用戶交互的activity上的Service。
  3. 這個(gè)進(jìn)程擁有一個(gè)前臺(tái)運(yùn)行的Service(service調(diào)用了方法startForeground()).
  4. 這個(gè)進(jìn)程擁有一個(gè)正在執(zhí)行其任何一個(gè)生命周期回調(diào)方法(onCreate(),onStart(),或onDestroy())的Service。
  5. 這個(gè)進(jìn)程擁有正在執(zhí)行其onReceive()方法的BroadcastReceiver。

通常,在任何時(shí)間點(diǎn),只有很少的前臺(tái)進(jìn)程存在。它們只有在達(dá)到無法調(diào)合的矛盾時(shí)才會(huì)被殺--如內(nèi)存太小而不能繼續(xù)運(yùn)行時(shí)。通常,到了這時(shí),設(shè)備就達(dá)到了一個(gè)內(nèi)存分頁(yè)調(diào)度狀態(tài),所以需要?dú)⒁恍┣芭_(tái)進(jìn)程來保證用戶界面的反應(yīng).

2、可見進(jìn)程

一個(gè)進(jìn)程不擁有運(yùn)行于前臺(tái)的組件,但是依然能影響用戶所見。滿足下列條件時(shí),進(jìn)程即為可見:

這個(gè)進(jìn)程擁有一個(gè)不在前臺(tái)但仍可見的Activity(它的onPause()方法被調(diào)用)。當(dāng)一個(gè)前臺(tái)activity啟動(dòng)一個(gè)對(duì)話框時(shí),就出了這種情況。

3、服務(wù)進(jìn)程

一個(gè)可見進(jìn)程被認(rèn)為是極其重要的。并且,除非只有殺掉它才可以保證所有前臺(tái)進(jìn)程的運(yùn)行,否則是不能動(dòng)它的。

這個(gè)進(jìn)程擁有一個(gè)綁定到可見activity的Service。

一個(gè)進(jìn)程不在上述兩種之內(nèi),但它運(yùn)行著一個(gè)被startService()所啟動(dòng)的service。

盡管一個(gè)服務(wù)進(jìn)程不直接影響用戶所見,但是它們通常做一些用戶關(guān)心的事情(比如播放音樂或下載數(shù)據(jù)),所以系統(tǒng)不到前臺(tái)進(jìn)程和可見進(jìn)程活不下去時(shí)不會(huì)殺它。

4、后臺(tái)進(jìn)程

一個(gè)進(jìn)程擁有一個(gè)當(dāng)前不可見的activity(activity的onStop()方法被調(diào)用)。

這樣的進(jìn)程們不會(huì)直接影響到用戶體驗(yàn),所以系統(tǒng)可以在任意時(shí)刻殺了它們從而為前臺(tái)、可見、以及服務(wù)進(jìn)程們提供存儲(chǔ)空間。通常有很多后臺(tái)進(jìn)程在運(yùn)行。它們被保存在一個(gè)LRU(最近最少使用)列表中來確保擁有最近剛被看到的activity的進(jìn)程最后被殺。如果一個(gè)activity正確的實(shí)現(xiàn)了它的生命周期方法,并保存了它的當(dāng)前狀態(tài),那么殺死它的進(jìn)程將不會(huì)對(duì)用戶的可視化體驗(yàn)造成影響。因?yàn)楫?dāng)用戶返回到這個(gè)activity時(shí),這個(gè)activity會(huì)恢復(fù)它所有的可見狀態(tài)。

5、空進(jìn)程

一個(gè)進(jìn)程不擁有入何active組件。

保留這類進(jìn)程的唯一理由是高速緩存,這樣可以提高下一次一個(gè)組件要運(yùn)行它時(shí)的啟動(dòng)速度。系統(tǒng)經(jīng)常為了平衡在進(jìn)程高速緩存和底層的內(nèi)核高速緩存之間的整體系統(tǒng)資源而殺死它們。

六. 線程池的相關(guān)知識(shí)。

Android中的線程池都是之間或間接通過配置ThreadPoolExecutor來實(shí)現(xiàn)不同特性的線程池.Android中最常見的四類具有不同特性的線程池分別為FixThreadPool、CachedThreadPool、SingleThreadPool、ScheduleThreadExecutor.

1).FixThreadPool

只有核心線程,并且數(shù)量固定的,也不會(huì)被回收,所有線程都活動(dòng)時(shí),因?yàn)殛?duì)列沒有限制大小,新任務(wù)會(huì)等待執(zhí)行.

優(yōu)點(diǎn):更快的響應(yīng)外界請(qǐng)求.

2).SingleThreadPool

只有一個(gè)核心線程,確保所有的任務(wù)都在同一線程中按順序完成.因此不需要處理線程同步的問題.

3).CachedThreadPool

只有非核心線程,最大線程數(shù)非常大,所有線程都活動(dòng)時(shí),會(huì)為新任務(wù)創(chuàng)建新線程,否則會(huì)利用空閑線程(60s空閑時(shí)間,過了就會(huì)被回收,所以線程池中有0個(gè)線程的可能)處理任務(wù).

優(yōu)點(diǎn):任何任務(wù)都會(huì)被立即執(zhí)行(任務(wù)隊(duì)列SynchronousQueue相當(dāng)于一個(gè)空集合);比較適合執(zhí)行大量的耗時(shí)較少的任務(wù).

4).ScheduledThreadPool

核心線程數(shù)固定,非核心線程(閑著沒活干會(huì)被立即回收)數(shù)沒有限制.

優(yōu)點(diǎn):執(zhí)行定時(shí)任務(wù)以及有固定周期的重復(fù)任務(wù)

七. 內(nèi)存泄露,怎樣查找,怎么產(chǎn)生的內(nèi)存泄露。

產(chǎn)生的內(nèi)存泄露

  1. 資源對(duì)象沒關(guān)閉造成的內(nèi)存泄漏
  2. 構(gòu)造Adapter時(shí),沒有使用緩存的convertView
  3. Bitmap對(duì)象不在使用時(shí)調(diào)用recycle()釋放內(nèi)存
  4. 試著使用關(guān)于application的context來替代和activity相關(guān)的context
  5. 注冊(cè)沒取消造成的內(nèi)存泄漏
  6. 集合中對(duì)象沒清理造成的內(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í)行后臺(tái)任務(wù)的話,只有當(dāng)任務(wù)正在執(zhí)行的時(shí)候才應(yīng)該讓Service運(yùn)行起來。當(dāng)啟動(dòng)一個(gè)Service時(shí),系統(tǒng)會(huì)傾向于將這個(gè)Service所依賴的進(jìn)程進(jìn)行保留,系統(tǒng)可以在LRUcache當(dāng)中緩存的進(jìn)程數(shù)量也會(huì)減少,導(dǎo)致切換程序的時(shí)候耗費(fèi)更多性能。我們可以使用IntentService,當(dāng)后臺(tái)任務(wù)執(zhí)行結(jié)束后會(huì)自動(dòng)停止,避免了Service的內(nèi)存泄漏。
  2. 當(dāng)界面不可見時(shí)釋放內(nèi)存 當(dāng)用戶打開了另外一個(gè)程序,我們的程序界面已經(jīng)不可見的時(shí)候,我們應(yīng)當(dāng)將所有和界面相關(guān)的資源進(jìn)行釋放。重寫Activity的onTrimMemory()方法,然后在這個(gè)方法中監(jiān)聽TRIM_MEMORY_UI_HIDDEN這個(gè)級(jí)別,一旦觸發(fā)說明用戶離開了程序,此時(shí)就可以進(jìn)行資源釋放操作了。
  3. 當(dāng)內(nèi)存緊張時(shí)釋放內(nèi)存 onTrimMemory()方法還有很多種其他類型的回調(diào),可以在手機(jī)內(nèi)存降低的時(shí)候及時(shí)通知我們,我們應(yīng)該根據(jù)回調(diào)中傳入的級(jí)別來去決定如何釋放應(yīng)用程序的資源。
  4. 避免在Bitmap上浪費(fèi)內(nèi)存 讀取一個(gè)Bitmap圖片的時(shí)候,千萬不要去加載不需要的分辨率??梢詨嚎s圖片等操作。
  5. 使用優(yōu)化過的數(shù)據(jù)集合 Android提供了一系列優(yōu)化過后的數(shù)據(jù)集合工具類,如SparseArray、SparseBooleanArray、LongSparseArray,使用這些API可以讓我們的程序更加高效。HashMap工具類會(huì)相對(duì)比較低效,因?yàn)樗枰獮槊恳粋€(gè)鍵值對(duì)都提供一個(gè)對(duì)象入口,而SparseArray就避免掉了基本數(shù)據(jù)類型轉(zhuǎn)換成對(duì)象數(shù)據(jù)類型的時(shí)間。

布局優(yōu)化

1).重用布局文件

標(biāo)簽可以允許在一個(gè)布局當(dāng)中引入另一個(gè)布局,那么比如說我們程序的所有界面都有一個(gè)公共的部分,這個(gè)時(shí)候最好的做法就是將這個(gè)公共的部分提取到一個(gè)獨(dú)立的布局中,然后每個(gè)界面的布局文件當(dāng)中來引用這個(gè)公共的布局。

Tips:如果我們要在標(biāo)簽中覆寫layout屬性,必須要將layout_width和layout_height這兩個(gè)屬性也進(jìn)行覆寫,否則覆寫效果將不會(huì)生效。

標(biāo)簽是作為標(biāo)簽的一種輔助擴(kuò)展來使用的,它的主要作用是為了防止在引用布局文件時(shí)引用文件時(shí)產(chǎn)生多余的布局嵌套。布局嵌套越多,解析起來就越耗時(shí),性能就越差。因此編寫布局文件時(shí)應(yīng)該讓嵌套的層數(shù)越少越好。

舉例:比如在LinearLayout里邊使用一個(gè)布局。里邊又有一個(gè)LinearLayout,那么其實(shí)就存在了多余的布局嵌套,使用merge可以解決這個(gè)問題。

2).僅在需要時(shí)才加載布局

某個(gè)布局當(dāng)中的元素不是一起顯示出來的,普通情況下只顯示部分常用的元素,而那些不常用的元素只有在用戶進(jìn)行特定操作時(shí)才會(huì)顯示出來。

舉例:填信息時(shí)不是需要全部填的,有一個(gè)添加更多字段的選項(xiàng),當(dāng)用戶需要添加其他信息的時(shí)候,才將另外的元素顯示到界面上。用VISIBLE性能表現(xiàn)一般,可以用ViewStub。ViewStub也是View的一種,但是沒有大小,沒有繪制功能,也不參與布局,資源消耗非常低,可以認(rèn)為完全不影響性能。

 

  1. <ViewStub    
  2.     android:id="@+id/view_stub"   
  3.     android:layout="@layout/profile_extra"   
  4.     android:layout_width="match_parent"   
  5.     android:layout_height="wrap_content"   
  6.     />   
  7.  
  8. public void onMoreClick() {   
  9.     ViewStub viewStub = (ViewStub) findViewById(R.id.view_stub);   
  10.     if (viewStub != null) {   
  11.         View inflatedView = viewStub.inflate();   
  12.         editExtra1 = (EditText) inflatedView.findViewById(R.id.edit_extra1);   
  13.         editExtra2 = (EditText) inflatedView.findViewById(R.id.edit_extra2);   
  14.         editExtra3 = (EditText) inflatedView.findViewById(R.id.edit_extra3);   
  15.     }   
  16. }   

tips:ViewStub所加載的布局是不可以使用標(biāo)簽的,因此這有可能導(dǎo)致加載出來出來的布局存在著多余的嵌套結(jié)構(gòu)。

高性能編碼優(yōu)化

都是一些微優(yōu)化,在性能方面看不出有什么顯著的提升的。使用合適的算法和數(shù)據(jù)結(jié)構(gòu)是優(yōu)化程序性能的最主要手段。

1).避免創(chuàng)建不必要的對(duì)象 不必要的對(duì)象我們應(yīng)該避免創(chuàng)建:

如果有需要拼接的字符串,那么可以優(yōu)先考慮使用StringBuffer或者StringBuilder來進(jìn)行拼接,而不是加號(hào)連接符,因?yàn)槭褂眉犹?hào)連接符會(huì)創(chuàng)建多余的對(duì)象,拼接的字符串越長(zhǎng),加號(hào)連接符的性能越低。

當(dāng)一個(gè)方法的返回值是String的時(shí)候,通常需要去判斷一下這個(gè)String的作用是什么,如果明確知道調(diào)用方會(huì)將返回的String再進(jìn)行拼接操作的話,可以考慮返回一個(gè)StringBuffer對(duì)象來代替,因?yàn)檫@樣可以將一個(gè)對(duì)象的引用進(jìn)行返回,而返回String的話就是創(chuàng)建了一個(gè)短生命周期的臨時(shí)對(duì)象。

盡可能地少創(chuàng)建臨時(shí)對(duì)象,越少的對(duì)象意味著越少的GC操作。

2).在沒有特殊原因的情況下,盡量使用基本數(shù)據(jù)類型來代替封裝數(shù)據(jù)類型,int比Integer要更加有效,其它數(shù)據(jù)類型也是一樣。

基本數(shù)據(jù)類型的數(shù)組也要優(yōu)于對(duì)象數(shù)據(jù)類型的數(shù)組。另外兩個(gè)平行的數(shù)組要比一個(gè)封裝好的對(duì)象數(shù)組更加高效,舉個(gè)例子,F(xiàn)oo[]和Bar[]這樣的數(shù)組,使用起來要比Custom(Foo,Bar)[]這樣的一個(gè)數(shù)組高效的多。

3).靜態(tài)優(yōu)于抽象

如果你并不需要訪問一個(gè)對(duì)系那個(gè)中的某些字段,只是想調(diào)用它的某些方法來去完成一項(xiàng)通用的功能,那么可以將這個(gè)方法設(shè)置成靜態(tài)方法,調(diào)用速度提升15%-20%,同時(shí)也不用為了調(diào)用這個(gè)方法去專門創(chuàng)建對(duì)象了,也不用擔(dān)心調(diào)用這個(gè)方法后是否會(huì)改變對(duì)象的狀態(tài)(靜態(tài)方法無法訪問非靜態(tài)字段)。

4).對(duì)常量使用static final修飾符 

  1. static int intVal = 42;   
  2. static String strVal = "Hello, world!";  

編譯器會(huì)為上面的代碼生成一個(gè)初始方法,稱為方法,該方法會(huì)在定義類第一次被使用的時(shí)候調(diào)用。這個(gè)方法會(huì)將42的值賦值到intVal當(dāng)中,從字符串常量表中提取一個(gè)引用賦值到strVal上。當(dāng)賦值完成后,我們就可以通過字段搜尋的方式去訪問具體的值了。

final進(jìn)行優(yōu)化: 

  1. static final int intVal = 42;   
  2. static final String strVal = "Hello, world!";  

這樣,定義類就不需要方法了,因?yàn)樗械某A慷紩?huì)在dex文件的初始化器當(dāng)中進(jìn)行初始化。當(dāng)我們調(diào)用intVal時(shí)可以直接指向42的值,而調(diào)用strVal會(huì)用一種相對(duì)輕量級(jí)的字符串常量方式,而不是字段搜尋的方式。

這種優(yōu)化方式只對(duì)基本數(shù)據(jù)類型以及String類型的常量有效,對(duì)于其他數(shù)據(jù)類型的常量是無效的。

5).使用增強(qiáng)型for循環(huán)語(yǔ)法 

  1. static class Counter {   
  2. int mCount;   
  3. }   
  4.  
  5. Counter[] mArray = ...   
  6.  
  7. public void zero() {   
  8. int sum = 0;   
  9. for (int i = 0; i < mArray.length; ++i) {   
  10.     sum += mArray[i].mCount;   
  11. }  }   
  12.  
  13. public void one() {   
  14. int sum = 0;   
  15. Counter[] localArray = mArray;   
  16. int len = localArray.length;   
  17. for (int i = 0; i < len; ++i) {   
  18.     sum += localArray[i].mCount;   
  19. }   
  20. }   
  21.  
  22. public void two() {   
  23. int sum = 0;   
  24. for (Counter a : mArray) {   
  25.     sum += a.mCount;   
  26. }   
  27. }   

zero()最慢,每次都要計(jì)算mArray的長(zhǎng)度,one()相對(duì)快得多,two()fangfa在沒有JIT(Just In Time Compiler)的設(shè)備上是運(yùn)行最快的,而在有JIT的設(shè)備上運(yùn)行效率和one()方法不相上下,需要注意這種寫法需要JDK1.5之后才支持。

Tips:ArrayList手寫的循環(huán)比增強(qiáng)型for循環(huán)更快,其他的集合沒有這種情況。因此默認(rèn)情況下使用增強(qiáng)型for循環(huán),而遍歷ArrayList使用傳統(tǒng)的循環(huán)方式。

6).多使用系統(tǒng)封裝好的API

系統(tǒng)提供不了的Api完成不了我們需要的功能才應(yīng)該自己去寫,因?yàn)槭褂孟到y(tǒng)的Api很多時(shí)候比我們自己寫的代碼要快得多,它們的很多功能都是通過底層的匯編模式執(zhí)行的。 舉個(gè)例子,實(shí)現(xiàn)數(shù)組拷貝的功能,使用循環(huán)的方式來對(duì)數(shù)組中的每一個(gè)元素一一進(jìn)行賦值當(dāng)然可行,但是直接使用系統(tǒng)中提供的System.arraycopy()方法會(huì)讓執(zhí)行效率快9倍以上。

7).避免在內(nèi)部調(diào)用Getters/Setters方法

面向?qū)ο笾蟹庋b的思想是不要把類內(nèi)部的字段暴露給外部,而是提供特定的方法來允許外部操作相應(yīng)類的內(nèi)部字段。但在Android中,字段搜尋比方法調(diào)用效率高得多,我們直接訪問某個(gè)字段可能要比通過getters方法來去訪問這個(gè)字段快3到7倍。但是編寫代碼還是要按照面向?qū)ο笏季S的,我們應(yīng)該在能優(yōu)化的地方進(jìn)行優(yōu)化,比如避免在內(nèi)部調(diào)用getters/setters方法。

九. 插件化相關(guān)技術(shù),熱修補(bǔ)技術(shù)是怎樣實(shí)現(xiàn)的,和插件化有什么區(qū)別

相同點(diǎn):

都使用ClassLoader來實(shí)現(xiàn)的加載的新的功能類,都可以使用PathClassLoader與DexClassLoader

不同點(diǎn):

熱修復(fù)因?yàn)槭菫榱诵迯?fù)Bug的,所以要將新的同名類替代同名的Bug類,要搶先加載新的類而不是Bug類,所以多做兩件事:在原先的app打包的時(shí)候,阻止相關(guān)類去打上CLASS_ISPREVERIFIED標(biāo)志,還有在熱修復(fù)時(shí)動(dòng)態(tài)改變BaseDexClassLoader對(duì)象間接引用的dexElements,這樣才能搶先代替Bug類,完成系統(tǒng)不加載舊的Bug類.

而插件化只是增肌新的功能類或者是資源文件,所以不涉及搶先加載舊的類這樣的使命,就避過了阻止相關(guān)類去打上CLASS_ISPREVERIFIED標(biāo)志和還有在熱修復(fù)時(shí)動(dòng)態(tài)改變BaseDexClassLoader對(duì)象間接引用的dexElements.

所以插件化比熱修復(fù)簡(jiǎn)單,熱修復(fù)是在插件化的基礎(chǔ)上在進(jìn)行替舊的Bug類

十. 怎樣計(jì)算一張圖片的大小,加載bitmap過程(怎樣保證不產(chǎn)生內(nèi)存溢出),二級(jí)緩存,LRUCache算法。

計(jì)算一張圖片的大小

圖片占用內(nèi)存的計(jì)算公式:圖片高度 圖片寬度 一個(gè)像素占用的內(nèi)存大小.所以,計(jì)算圖片占用內(nèi)存大小的時(shí)候,要考慮圖片所在的目錄跟設(shè)備密度,這兩個(gè)因素其實(shí)影響的是圖片的高寬,android會(huì)對(duì)圖片進(jìn)行拉升跟壓縮。

加載bitmap過程(怎樣保證不產(chǎn)生內(nèi)存溢出)

由于Android對(duì)圖片使用內(nèi)存有限制,若是加載幾兆的大圖片便內(nèi)存溢出。Bitmap會(huì)將圖片的所有像素(即長(zhǎng)x寬)加載到內(nèi)存中,如果圖片分辨率過大,會(huì)直接導(dǎo)致內(nèi)存OOM,只有在BitmapFactory加載圖片時(shí)使用BitmapFactory.Options對(duì)相關(guān)參數(shù)進(jìn)行配置來減少加載的像素。

BitmapFactory.Options相關(guān)參數(shù)詳解

(1).Options.inPreferredConfig值來降低內(nèi)存消耗。

比如:默認(rèn)值A(chǔ)RGB_8888改為RGB_565,節(jié)約一半內(nèi)存。

(2).設(shè)置Options.inSampleSize 縮放比例,對(duì)大圖片進(jìn)行壓縮 。

(3).設(shè)置Options.inPurgeable和inInputShareable:讓系統(tǒng)能及時(shí)回 收內(nèi)存。

A:inPurgeable:設(shè)置為True時(shí),表示系統(tǒng)內(nèi)存不足時(shí)可以被回 收,設(shè)置為False時(shí),表示不能被回收。

B:inInputShareable:設(shè)置是否深拷貝,與inPurgeable結(jié)合使用,inPurgeable為false時(shí),該參數(shù)無意義。

(4).使用decodeStream代替其他方法。

decodeResource,setImageResource,setImageBitmap等方法

十一. LRUCache算法是怎樣實(shí)現(xiàn)的。

內(nèi)部存在一個(gè)LinkedHashMap和maxSize,把最近使用的對(duì)象用強(qiáng)引用存儲(chǔ)在 LinkedHashMap中,給出來put和get方法,每次put圖片時(shí)計(jì)算緩存中所有圖片總大小,跟maxSize進(jìn)行比較,大于maxSize,就將最久添加的圖片移除;反之小于maxSize就添加進(jìn)來。

之前,我們會(huì)使用內(nèi)存緩存技術(shù)實(shí)現(xiàn),也就是軟引用或弱引用,在Android 2.3(APILevel 9)開始,垃圾回收器會(huì)更傾向于回收持有軟引用或弱引用的對(duì)象,這讓軟引用和弱引用變得不再可靠。

算法

一. 算法題

m n的矩陣,能形成幾個(gè)正方形(2 2能形成1個(gè)正方形,2 3 2個(gè),3 3 6個(gè))

計(jì)數(shù)的關(guān)鍵是要觀察到任意一個(gè)傾斜的正方形必然唯一內(nèi)接于一個(gè)非傾斜的正方形,而一個(gè)非傾斜的邊長(zhǎng)為k的非傾斜正方形,一條邊上有k-1個(gè)內(nèi)點(diǎn),每個(gè)內(nèi)點(diǎn)恰好確定一個(gè)內(nèi)接于其中的傾斜正方形,加上非傾斜正方形本身,可知,將邊長(zhǎng)為k的非傾斜正方形數(shù)目乘以k,再按k求和即可得到所有正方形的數(shù)目。

  • 設(shè)2≤n≤m,k≤n-1,則邊長(zhǎng)為k的非傾斜有
  • (n-k)(m-k)個(gè),故所有正方形有
  • ∑(m-k)(n-k)k個(gè)
  • 例如m=n=4
  • 正方形有33=20個(gè)

下面是面試過程中遇到的題目

大多數(shù)題目都可以在上面找到答案.

電話面試題

  1. ArrayList 和 Hashmap 簡(jiǎn)單說一些,區(qū)別,底層的數(shù)據(jù)結(jié)構(gòu).
  2. Handler 消息機(jī)制
  3. 引起內(nèi)存泄漏的場(chǎng)景
  4. 多線程的使用場(chǎng)景?
  5. 常用的線程池有哪幾種?
  6. 在公司做了什么?團(tuán)隊(duì)規(guī)模?為什么離職?

面試中實(shí)際涉及到的問題

第一輪

  1. 知道哪些單例模式,寫一個(gè)線程安全的單例,并分析為什么是線程安全的?
  2. Java中的集合有哪些?解釋一下HashMap?底部的數(shù)據(jù)結(jié)構(gòu)?散列表沖突的處理方法,散列表是一個(gè)什么樣的數(shù)據(jù)結(jié)構(gòu)?HashMap是采用什么方法處理沖突的?
  3. 解釋一下什么是MVP架構(gòu),畫出圖解,一句話解釋MVP和MVC的區(qū)別?
  4. Handle消息機(jī)制?在使用Handler的時(shí)候要注意哪些東西,是否會(huì)引起內(nèi)存泄漏?畫一下Handler機(jī)制的圖解?
  5. 是否做過性能優(yōu)化?已經(jīng)采取了哪些措施進(jìn)行優(yōu)化?
  6. 引起內(nèi)存泄漏的原因是什么?以及你是怎么解決的?

這些問題應(yīng)該都是比較基礎(chǔ)的問題,每個(gè)開發(fā)者都應(yīng)該是非常熟悉并能詳細(xì)敘述的.這一輪的面試官問的技術(shù)都是平時(shí)用到的.

第二輪

  1. 關(guān)于并發(fā)理解多少?說幾個(gè)并發(fā)的集合?
  2. Handler 消息機(jī)制圖解?
  3. 在項(xiàng)目中做了哪些東西?
  4. 畫圖說明View 事件傳遞機(jī)制?并舉一個(gè)例子闡述
  5. 類加載機(jī)制,如何換膚,換膚插件中存在的問題?hotfix是否用過,原理是否了解?
  6. 說說項(xiàng)目中用到了哪些設(shè)計(jì)模式,說了一下策略模式和觀察者模式?
  7. 會(huì)JS么?有Hybid開發(fā)經(jīng)驗(yàn)么?
  8. 說一下快排的思想?手寫代碼
  9. 堆有哪些數(shù)據(jù)結(jié)構(gòu)?

對(duì)于這輪米那是明顯感覺到壓力,知識(shí)的縱向了解也比較深,應(yīng)該是個(gè)leader.

第三輪

  1. 介紹一下在項(xiàng)目中的角色?
  2. 遇到困難是怎么解決的?
  3. 如何與人相處,與別人意見相左的時(shí)候是怎么解決的,并舉生活中的一個(gè)例子.
  4. 有沒有壓力特別大的時(shí)候?

這個(gè)應(yīng)該是項(xiàng)目經(jīng)理了,問的問題偏向于生活性格方面.

以上面試中問到的題目基本上都可以在上面找到答案,所以做準(zhǔn)備是很重要的,但技術(shù)是一點(diǎn)點(diǎn)積累的,就算你全會(huì)背了,面試過了,真正等到工作的時(shí)候還是會(huì)捉襟見肘的,所以踏實(shí)點(diǎn)吧騷年.

責(zé)任編輯:未麗燕 來源: 安卓巴士
相關(guān)推薦

2023-09-04 14:52:35

2010-08-04 14:21:21

面試

2015-06-02 11:30:59

面試技術(shù)面試

2017-11-30 15:00:50

面試代碼面試官

2017-03-20 19:40:29

AndroidSwipeRefres下拉刷新

2020-04-03 14:05:10

面試RedisJava

2015-05-26 15:17:44

OpenStack

2011-03-08 11:42:56

2015-04-20 17:32:16

鈦媒體

2013-01-24 11:03:30

2014-07-21 15:32:03

Wi-Fi

2012-12-04 17:44:13

2010-05-14 00:19:43

2020-06-19 11:43:09

戴爾

2022-07-27 16:05:57

元宇宙AISummit

2010-05-05 09:14:07

2023-03-22 07:19:21

2022-05-07 18:08:09

物聯(lián)網(wǎng)

2022-11-14 17:11:29

鴻蒙操作系統(tǒng)

2013-09-26 13:21:05

數(shù)學(xué)算法
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)