Android面試集錦

Java基礎(chǔ)知識(shí)

String a = "aaa" 和 String a = new String("aaa")

"aaa"盲再,這個(gè)本身就是一個(gè)字符串對象梦碗,字符串對象是一個(gè)常量循捺,一旦初始化恰力,就不可更改阵漏,存在于內(nèi)存常量池中叹洲。
前面String a = "aaa" 的意思就是在棧中創(chuàng)建一個(gè)字符串類型的變量a民泵,
所以String a = "aa"的意思就是將字符串對象"aa"的內(nèi)存地址賦給字符串變量a

String a = new String("aa")中有兩個(gè)對象,一個(gè)是"aa"同上,另一個(gè)則是new String()拾给,此對象創(chuàng)建時(shí)通過構(gòu)造函數(shù)用字符串"aa"進(jìn)行初始化额衙,這兩個(gè)對象內(nèi)存地址是不一樣的

Android基礎(chǔ)

Activity View Window 的理解

Activity像一個(gè)工匠(控制單元)硼啤,Window像窗戶(承載模型),View像窗花(顯示視圖) LayoutInflater像剪刀斧账,Xml配置像窗花圖紙咧织。Activity其實(shí)不是顯示視圖习绢,View才是真正的顯示視圖为迈。但是View不能單獨(dú)存在伴郁,它必須附著在Window這個(gè)抽象的概念上面,因此有視圖的地方就有Window狈涮。

  1. 一個(gè)Activity構(gòu)造的時(shí)候會(huì)初始化一個(gè)Window狐胎,準(zhǔn)確的說是PhoneWindow。
  2. 這個(gè)PhoneWindow有一個(gè)“ViewRoot”歌馍,引號(hào)是說其實(shí)這個(gè)“ViewRoot”是一個(gè)View或者說ViewGroup握巢,是最初始的根視圖。
  3. “ViewRoot”通過addView方法來一個(gè)個(gè)的添加View松却。比如TextView暴浦,Button等
  4. 這些View的事件監(jiān)聽,是由WindowManagerService來接受消息晓锻,并且回調(diào)Activity函數(shù)歌焦。比如onClickListener,onKeyDown等

Context 理解

Context的中文翻譯為:語境; 上下文; 背景; 環(huán)境砚哆,在開發(fā)中我們經(jīng)常說稱之為“上下文”独撇。一個(gè)Activity就是一個(gè)Context,一個(gè)Service也是一個(gè)Context躁锁。

Context類本身是一個(gè)純abstract類券勺,它有兩個(gè)具體的實(shí)現(xiàn)子類:ContextImpl和ContextWrapper。其中ContextWrapper類只是一個(gè)包裝而已灿里,提供attachBaseContext()用于給ContextWrapper對象中指定真正的Context對象,這個(gè)baseContext就是ContextImpl.
ContextImpl類則真正實(shí)現(xiàn)了Context中的所以函數(shù)程腹,應(yīng)用程序中所調(diào)用的各種Context類的方法匣吊,其實(shí)現(xiàn)均來自于該類。一句話總結(jié):Context的兩個(gè)子類分工明確寸潦,其中ContextImpl是Context的具體實(shí)現(xiàn)類色鸳,ContextWrapper是Context的包裝類。Activity见转,Application命雀,Service雖都繼承自ContextWrapper(Activity繼承自ContextWrapper的子類ContextThemeWrapper),但它們初始化的過程中都會(huì)創(chuàng)建ContextImpl對象斩箫,由ContextImpl實(shí)現(xiàn)Context中的方法吏砂。

一個(gè)應(yīng)用程序 Context數(shù)量=Activity數(shù)量+Service數(shù)量+1
getApplication()和getApplicationContext()指向的是同一塊內(nèi)存地址撵儿,也就說他們返回同一個(gè)對象
Context 作用域:凡是跟UI相關(guān)的,都應(yīng)該使用Activity做為Context來處理狐血;其他的一些操作淀歇,Service,Activity,Application等實(shí)例都可以,當(dāng)然了匈织,注意Context引用的持有浪默,防止內(nèi)存泄漏。
使用Context的正確姿勢:

  1. 當(dāng)Application的Context能搞定的情況下缀匕,并且生命周期長的對象纳决,優(yōu)先使用Application的Context。
  2. 不要讓生命周期長于Activity的對象持有到Activity的引用乡小。
  3. 盡量不要在Activity中使用非靜態(tài)內(nèi)部類阔加,因?yàn)榉庆o態(tài)內(nèi)部類會(huì)隱式持有外部類實(shí)例的引用,如果使用靜態(tài)內(nèi)部類劲件,將外部實(shí)例引用作為弱引用持有掸哑。

Activity Launch Mode

  1. standard: 每一個(gè)intent創(chuàng)建一個(gè)Activity處理
  2. singleTop: 只有在調(diào)用者和目標(biāo)Activity在同一Task中,并且目標(biāo)Activity位于棧頂零远,才使用現(xiàn)有目標(biāo)Activity實(shí)例苗分,否則創(chuàng)建新的目標(biāo)Activity實(shí)例。如果是外部程序啟動(dòng)singleTop的Activity牵辣,在Android 5.0之前新創(chuàng)建的Activity會(huì)位于調(diào)用者的Task中摔癣,5.0及以后會(huì)放入新的Task中。
  3. singleTask: 使用singleTask啟動(dòng)模式的Activity在系統(tǒng)中只會(huì)存在一個(gè)實(shí)例纬向。如果singleTask Activity實(shí)例已然存在择浊,那么在Activity回退棧中,所有位于該Activity上面的Activity實(shí)例都將被銷毀掉(銷毀過程會(huì)調(diào)用Activity生命周期回調(diào))逾条,這樣使得singleTask Activity實(shí)例位于棧頂琢岩。與此同時(shí),Intent會(huì)通過onNewIntent傳遞到這個(gè)SingleTask Activity實(shí)例师脂。
  4. singleInstance: 這個(gè)模式和singleTask差不多担孔,因?yàn)樗麄冊谙到y(tǒng)中都只有一份實(shí)例。唯一不同的就是存放singleInstance Activity實(shí)例的Task只能存放一個(gè)該模式的Activity實(shí)例吃警。如果從singleInstance Activity實(shí)例啟動(dòng)另一個(gè)Activity糕篇,那么這個(gè)Activity實(shí)例會(huì)放入其他的Task中。同理酌心,如果singleInstance Activity被別的Activity啟動(dòng)拌消,它也會(huì)放入不同于調(diào)用者的Task中。

Activity 和Fragment的理解

Fragment是在Android 3.0版本上開始出現(xiàn)安券,主要是解決手機(jī)和平板等折本上適配墩崩,多個(gè)Activity之間切換是性能的問題等氓英。
Fragment更多的生命周期。

onAttach onCreateView onViewCreated onActivityCreated onDestoryView onDetach

Activity和Fragment之間的通訊

  1. 廣播機(jī)制: 太重泰鸡,有延時(shí)
  2. 接口回調(diào)
  3. Event Bus: 性能問題
  4. 直接類方法調(diào)用

Fragment 遇到的坑:

  1. 多個(gè)Fragment疊加時(shí) 頂層空白處點(diǎn)擊會(huì)造成后面View響應(yīng): 原因是因?yàn)镕ragment添加進(jìn)去后其實(shí)就是添加的View债蓝,頂層View未消費(fèi)點(diǎn)擊事件就會(huì)傳遞到底層的View上∈⒘洌可以在跟layout上添加 clickable="true" 消費(fèi)掉該事件

內(nèi)存泄漏有哪些場景以及解決方法

  1. 類的靜態(tài)變量持有大數(shù)據(jù)對象 靜態(tài)變量長期維持到大數(shù)據(jù)對象的引用饰迹,阻止垃圾回收。
  2. 非靜態(tài)內(nèi)部類存在靜態(tài)實(shí)例 非靜態(tài)內(nèi)部類會(huì)維持一個(gè)到外部類實(shí)例的引用余舶,如果非靜態(tài)內(nèi)部類的實(shí)例是靜態(tài)的啊鸭,就會(huì)間接長期維持著外部類的引用,阻止被回收掉匿值。
  3. 資源對象未關(guān)閉 資源性對象比如(Cursor赠制,F(xiàn)ile文件等)往往都用了一些緩沖,我們在不使用的時(shí)候挟憔,應(yīng)該及時(shí)關(guān)閉它們钟些, 以便它們的緩沖及時(shí)回收內(nèi)存。它們的緩沖不僅存在于java虛擬機(jī)內(nèi)绊谭,還存在于java虛擬機(jī)外政恍。 如果我們僅僅是把它的引用設(shè)置為null,而不關(guān)閉它們,往往會(huì)造成內(nèi)存泄露达传。 解決辦法: 比如SQLiteCursor(在析構(gòu)函數(shù)finalize(),如果我們沒有關(guān)閉它篙耗,它自己會(huì)調(diào)close()關(guān)閉), 如果我們沒有關(guān)閉它宪赶,系統(tǒng)在回收它時(shí)也會(huì)關(guān)閉它宗弯,但是這樣的效率太低了。 因此對于資源性對象在不使用的時(shí)候搂妻,應(yīng)該調(diào)用它的close()函數(shù)蒙保,將其關(guān)閉掉,然后才置為null. 在我們的程序退出時(shí)一定要確保我們的資源性對象已經(jīng)關(guān)閉欲主。 程序中經(jīng)常會(huì)進(jìn)行查詢數(shù)據(jù)庫的操作追他,但是經(jīng)常會(huì)有使用完畢Cursor后沒有關(guān)閉的情況。如果我們的查詢結(jié)果集比較小岛蚤, 對內(nèi)存的消耗不容易被發(fā)現(xiàn),只有在常時(shí)間大量操作的情況下才會(huì)復(fù)現(xiàn)內(nèi)存問題懈糯,這樣就會(huì)給以后的測試和問題排查帶來困難和風(fēng)險(xiǎn)涤妒,記得try catch后,在finally方法中關(guān)閉連接
  4. Handler內(nèi)存泄漏 Handler作為內(nèi)部類存在于Activity中赚哗,但是Handler生命周期與Activity生命周期往往并不是相同的她紫,比如當(dāng)Handler對象有Message在排隊(duì)硅堆,則無法釋放,進(jìn)而導(dǎo)致本該釋放的Acitivity也沒有辦法進(jìn)行回收贿讹。 解決辦法:聲明handler為static類渐逃,這樣內(nèi)部類就不再持有外部類的引用了,就不會(huì)阻塞Activity的釋放民褂;如果內(nèi)部類實(shí)在需要用到外部類的對象茄菊,可在其內(nèi)部聲明一個(gè)弱引用引用外部類。
  5. 一些不良代碼習(xí)慣 有些代碼并不造成內(nèi)存泄露赊堪,但是他們的資源沒有得到重用面殖,頻繁的申請內(nèi)存和銷毀內(nèi)存,消耗CPU資源的同時(shí)哭廉,也引起內(nèi)存抖動(dòng) 解決方案 如果需要頻繁的申請內(nèi)存對象和和釋放對象脊僚,可以考慮使用對象池來增加對象的復(fù)用。 例如ListView便是采用這種思想遵绰,通過復(fù)用converview來避免頻繁的GC

如何避免 OOM 問題的出現(xiàn)

  1. 使用更加輕量的數(shù)據(jù)結(jié)構(gòu) 例如辽幌,我們可以考慮使用ArrayMap/SparseArray而不是HashMap等傳統(tǒng)數(shù)據(jù)結(jié)構(gòu)。通常的HashMap的實(shí)現(xiàn)方式更加消耗內(nèi)存椿访,因?yàn)樗枰粋€(gè)額外的實(shí)例對象來記錄Mapping操作乌企。另外,SparseArray更加高效赎离,在于他們避免了對key與value的自動(dòng)裝箱(autoboxing)逛犹,并且避免了裝箱后的解箱。
  2. 避免在Android里面使用Enum Android官方培訓(xùn)課程提到過“Enums often require more than twice as much memory as static constants. You should strictly avoid using enums on Android.”梁剔,具體原理請參考《Android性能優(yōu)化典范(三)》虽画,所以請避免在Android里面使用到枚舉。
  3. 減小Bitmap對象的內(nèi)存占用 Bitmap是一個(gè)極容易消耗內(nèi)存的大胖子荣病,減小創(chuàng)建出來的Bitmap的內(nèi)存占用可謂是重中之重码撰,,通常來說有以下2個(gè)措施: inSampleSize:縮放比例个盆,在把圖片載入內(nèi)存之前脖岛,我們需要先計(jì)算出一個(gè)合適的縮放比例,避免不必要的大圖載入颊亮。 decode format:解碼格式柴梆,選擇ARGB_6666/RBG_545/ARGB_4444/ALPHA_6,存在很大差異
  4. Bitmap對象的復(fù)用 縮小Bitmap的同時(shí)终惑,也需要提高BitMap對象的復(fù)用率绍在,避免頻繁創(chuàng)建BitMap對象,復(fù)用的方法有以下2個(gè)措施 LRUCache : “最近最少使用算法”在Android中有極其普遍的應(yīng)用。ListView與GridView等顯示大量圖片的控件里偿渡,就是使用LRU的機(jī)制來緩存處理好的Bitmap臼寄,把近期最少使用的數(shù)據(jù)從緩存中移除,保留使用最頻繁的數(shù)據(jù)溜宽, inBitMap高級(jí)特性:利用inBitmap的高級(jí)特性提高Android系統(tǒng)在Bitmap分配與釋放執(zhí)行效率吉拳。使用inBitmap屬性可以告知Bitmap解碼器去嘗試使用已經(jīng)存在的內(nèi)存區(qū)域,新解碼的Bitmap會(huì)嘗試去使用之前那張Bitmap在Heap中所占據(jù)的pixel data內(nèi)存區(qū)域适揉,而不是去問內(nèi)存重新申請一塊區(qū)域來存放Bitmap留攒。利用這種特性,即使是上千張的圖片涡扼,也只會(huì)僅僅只需要占用屏幕所能夠顯示的圖片數(shù)量的內(nèi)存大小
  5. 使用更小的圖片 在涉及給到資源圖片時(shí)稼跳,我們需要特別留意這張圖片是否存在可以壓縮的空間,是否可以使用更小的圖片吃沪。盡量使用更小的圖片不僅可以減少內(nèi)存的使用汤善,還能避免出現(xiàn)大量的InflationException。假設(shè)有一張很大的圖片被XML文件直接引用票彪,很有可能在初始化視圖時(shí)會(huì)因?yàn)閮?nèi)存不足而發(fā)生InflationException红淡,這個(gè)問題的根本原因其實(shí)是發(fā)生了OOM。
  6. StringBuilder 在有些時(shí)候降铸,代碼中會(huì)需要使用到大量的字符串拼接的操作在旱,這種時(shí)候有必要考慮使用StringBuilder來替代頻繁的“+”。
  7. 避免在onDraw方法里面執(zhí)行對象的創(chuàng)建 類似onDraw等頻繁調(diào)用的方法推掸,一定需要注意避免在這里做創(chuàng)建對象的操作桶蝎,因?yàn)樗麜?huì)迅速增加內(nèi)存的使用,而且很容易引起頻繁的gc谅畅,甚至是內(nèi)存抖動(dòng)登渣。
  8. 避免對象的內(nèi)存泄露 android中內(nèi)存泄漏的場景以及解決辦法

ListView 性能優(yōu)化

  1. 重用converView: 通過復(fù)用converview來減少不必要的view的創(chuàng)建,另外Infalte操作會(huì)把xml文件實(shí)例化成相應(yīng)的View實(shí)例毡泻,屬于IO操作胜茧,是耗時(shí)操作。
  2. 減少findViewById()操作: 將xml文件中的元素封裝成viewholder靜態(tài)類仇味,通過converview的setTag和getTag方法將view與相應(yīng)的holder對象綁定在一起呻顽,避免不必要的findviewbyid操作
  3. 避免在 getView 方法中做耗時(shí)的操作: 例如加載本地 Image 需要載入內(nèi)存以及解析 Bitmap ,都是比較耗時(shí)的操作丹墨,如果用戶快速滑動(dòng)listview廊遍,會(huì)因?yàn)間etview邏輯過于復(fù)雜耗時(shí)而造成滑動(dòng)卡頓現(xiàn)象。用戶滑動(dòng)時(shí)候不要加載圖片贩挣,待滑動(dòng)完成再加載喉前,可以使用這個(gè)第三方庫glide
  4. Item的布局層次結(jié)構(gòu)盡量簡單英染,避免布局太深或者不必要的重繪
  5. 盡量能保證 Adapter 的 hasStableIds() 返回 true 這樣在 notifyDataSetChanged() 的時(shí)候,如果item內(nèi)容并沒有變化被饿,ListView 將不會(huì)重新繪制這個(gè) View,達(dá)到優(yōu)化的目的
  6. 在一些場景中搪搏,ScollView內(nèi)會(huì)包含多個(gè)ListView狭握,可以把listview的高度寫死固定下來。 由于ScollView在快速滑動(dòng)過程中需要大量計(jì)算每一個(gè)listview的高度疯溺,阻塞了UI線程導(dǎo)致卡頓現(xiàn)象出現(xiàn)论颅,如果我們每一個(gè)item的高度都是均勻的,可以通過計(jì)算把listview的高度確定下來囱嫩,避免卡頓現(xiàn)象出現(xiàn)
  7. 使用 RecycleView 代替listview: 每個(gè)item內(nèi)容的變動(dòng)恃疯,listview都需要去調(diào)用notifyDataSetChanged來更新全部的item,太浪費(fèi)性能了墨闲。RecycleView可以實(shí)現(xiàn)當(dāng)個(gè)item的局部刷新今妄,并且引入了增加和刪除的動(dòng)態(tài)效果,在性能上和定制上都有很大的改善
  8. ListView 中元素避免半透明: 半透明繪制需要大量乘法計(jì)算鸳碧,在滑動(dòng)時(shí)不停重繪會(huì)造成大量的計(jì)算盾鳞,在比較差的機(jī)子上會(huì)比較卡。 在設(shè)計(jì)上能不半透明就不不半透明瞻离。實(shí)在要弄就把在滑動(dòng)的時(shí)候把半透明設(shè)置成不透明腾仅,滑動(dòng)完再重新設(shè)置成半透明。
  9. 盡量開啟硬件加速: 硬件加速提升巨大套利,避免使用一些不支持的函數(shù)導(dǎo)致含淚關(guān)閉某個(gè)地方的硬件加速推励。當(dāng)然這一條不只是對 ListView。

應(yīng)用常駐后臺(tái)

  1. Service設(shè)置成START_STICKY kill 后會(huì)被重啟(等待5秒左右)肉迫,重傳Intent验辞,保持與重啟前一樣
  2. 通過 startForeground將進(jìn)程設(shè)置為前臺(tái)進(jìn)程, 做前臺(tái)服務(wù)昂拂,優(yōu)先級(jí)和前臺(tái)應(yīng)用一個(gè)級(jí)別?受神,除非在系統(tǒng)內(nèi)存非常缺,否則此進(jìn)程不會(huì)被 kill
  3. 雙進(jìn)程Service: 讓2個(gè)進(jìn)程互相保護(hù)**格侯,其中一個(gè)Service被清理后蚪腐,另外沒被清理的進(jìn)程可以立即重啟進(jìn)程
  4. QQ黑科技: 在應(yīng)用退到后臺(tái)后,另起一個(gè)只有 1 像素的頁面停留在桌面上耕皮,讓自己保持前臺(tái)狀態(tài)仑濒,保護(hù)自己不被后臺(tái)清理工具殺死
  5. 在已經(jīng)root的設(shè)備下,修改相應(yīng)的權(quán)限文件,將App偽裝成系統(tǒng)級(jí)的應(yīng)用 Android4.0系列的一個(gè)漏洞朝墩,已經(jīng)確認(rèn)可行
  6. 用C編寫守護(hù)進(jìn)程(即子進(jìn)程) : Android系統(tǒng)中當(dāng)前進(jìn)程(Process)fork出來的子進(jìn)程醉拓,被系統(tǒng)認(rèn)為是兩個(gè)不同的進(jìn)程。當(dāng)父進(jìn)程被殺死的時(shí)候,子進(jìn)程仍然可以存活亿卤,并不受影響愤兵。鑒于目前提到的在Android->- Service層做雙守護(hù)都會(huì)失敗,我們可以fork出c進(jìn)程排吴,多進(jìn)程守護(hù)秆乳。死循環(huán)在那檢查是否還存在,具體的思路如下(Android5.0以上的版本不可行)
    用C編寫守護(hù)進(jìn)程(即子進(jìn)程)钻哩,守護(hù)進(jìn)程做的事情就是循環(huán)檢查目標(biāo)進(jìn)程是否存在屹堰,不存在則啟動(dòng)它。
    在NDK環(huán)境中將1中編寫的C代碼編譯打包成可執(zhí)行文件(BUILD_EXECUTABLE)街氢。主進(jìn)程啟動(dòng)時(shí)將守護(hù)進(jìn)程放入私有目錄下扯键,賦予可執(zhí)行權(quán)限,啟動(dòng)它即可珊肃。

Activity間數(shù)據(jù)傳遞 Serializable 和 Parcelable

Serializalbe會(huì)使用反射荣刑,序列化和反序列化過程需要大量I/O操作,Parcelable自已實(shí)現(xiàn)封送和解封(marshalled &unmarshalled)操作不需要用反射近范,數(shù)據(jù)也存放在Native內(nèi)存中嘶摊,效率要快很多。

Parcelable和Parcle這兩者之間的關(guān)系评矩。

Parcelable 接口定義在封送/解封送過程中混合和分解對象的契約叶堆。Parcelable接口的底層是Parcel容器對象。Parcel類是一種最快的序列化/反序列化機(jī)制斥杜,專為Android中的進(jìn)程間通信而設(shè)計(jì)虱颗。該類提供了一些方法來將成員容納到容器中,以及從容器展開成員蔗喂。

注意事項(xiàng)

Intent中的Bundle是在使用Binder機(jī)制進(jìn)行數(shù)據(jù)傳遞的忘渔,能使用的Binder的緩沖區(qū)是有大小限制的(有些手機(jī)是2M),而一個(gè)進(jìn)程默認(rèn)有16個(gè)binder線程缰儿,所以一個(gè)線程能占用的緩沖區(qū)就更小了(以前做過測試畦粮,大約一個(gè)線程可以占用128KB)。所以當(dāng)你看到“The Binder transaction failed because it was too large.”這類TransactionTooLargeException異常時(shí)乖阵,你應(yīng)該知道怎么解決了宣赔。

Android 動(dòng)畫原理

  1. 逐幀動(dòng)畫(Drawable Animation): 加載一系列Drawable資源來創(chuàng)建動(dòng)畫,簡單來說就是播放一系列的圖片來實(shí)現(xiàn)動(dòng)畫效果瞪浸,可以自定義每張圖片的持續(xù)時(shí)間
  2. 補(bǔ)間動(dòng)畫(Tween Animation): Tween可以對View對象實(shí)現(xiàn)一系列簡單的動(dòng)畫效果儒将,比如位移,縮放对蒲,旋轉(zhuǎn)钩蚊,透明度等等贡翘。但是它并不會(huì)改變View屬性的值,只是改變了View的繪制的位置砰逻,比如鸣驱,一個(gè)按鈕在動(dòng)畫過后,不在原來的位置蝠咆,但是觸發(fā)點(diǎn)擊事件的仍然是原來的坐標(biāo)丐巫。
  3. 屬性動(dòng)畫(Property Animation): 動(dòng)畫的對象除了傳統(tǒng)的View對象,還可以是Object對象勺美,動(dòng)畫結(jié)束后,Object對象的屬性值被實(shí)實(shí)在在的改變了

從調(diào)用動(dòng)畫start 開始碑韵,系統(tǒng)每隔 16ms 回調(diào)一次赡茸,也就是每次渲染的時(shí)間。動(dòng)畫根據(jù)初始值和結(jié)束值祝闻,再根據(jù)持續(xù)時(shí)間占卧,每次回調(diào)時(shí)計(jì)算當(dāng)前時(shí)間跟持續(xù)時(shí)間的進(jìn)度,然后根據(jù)進(jìn)度計(jì)算出屬性當(dāng)前時(shí)間點(diǎn)的值联喘,

Android 內(nèi)存泄露 (Java Leak 和 Native Leak)

  1. Java Leak: 比如 Activity华蜒,service中存在比自己生命周期還長的強(qiáng)引用,當(dāng)Activity 和 service 生命周期結(jié)束后豁遭,但是不能被系統(tǒng) gc 回收叭喜,就造成 內(nèi)存泄露
  2. Native Leak: 比如當(dāng)用 BitmapFactory decode 出來一個(gè)bitmap 的時(shí)候,由于decode 是調(diào)用的native 代碼蓖谢,當(dāng)bitmap 使用完畢后捂蕴,沒有主動(dòng)調(diào)用 回收方法,那么Java gc 在內(nèi)存回收的時(shí)候只能回收 Java層的內(nèi)存闪幽,而不能回收 native層的內(nèi)存啥辨,于是造成 native 層的內(nèi)存泄露

Java 基礎(chǔ)

多線程

線程池

Executors提供了四種創(chuàng)建ExecutorService的方法
1. Executors.newCachedThreadPool()
   創(chuàng)建一個(gè)定長的線程池,每提交一個(gè)任務(wù)就創(chuàng)建一個(gè)線程盯腌,直到達(dá)到池的最大長度溉知,這時(shí)線程池會(huì)保持長度不再變化
2. Executors.newFixedThreadPool()
   創(chuàng)建一個(gè)可緩存的線程池,如果當(dāng)前線程池的長度超過了處理的需要時(shí)腕够,它可以靈活的回收空閑的線程级乍,當(dāng)需要增加時(shí),
    它可以靈活的添加新的線程燕少,而不會(huì)對池的長度作任何限制
3. Executors.newScheduledThreadPool()
   創(chuàng)建一個(gè)定長的線程池卡者,而且支持定時(shí)的以及周期性的任務(wù)執(zhí)行,類似于Timer
4. Executors.newSingleThreadExecutor()
   創(chuàng)建一個(gè)單線程化的executor客们,它只創(chuàng)建唯一的worker線程來執(zhí)行任務(wù)
線程同步

synchronized的使用場景

1. 方法同步
public synchronized void method1
鎖住的是該對象,類的其中一個(gè)實(shí)例崇决,當(dāng)該對象(僅僅是這一個(gè)對象)在不同線程中執(zhí)行這個(gè)同步方法時(shí)材诽,線程之間會(huì)形成互斥。達(dá)到同步效果恒傻,但如果不同線程同時(shí)對該類的不同對象執(zhí)行這個(gè)同步方法時(shí)脸侥,則線程之間不會(huì)形成互斥,因?yàn)樗麄儞碛械氖遣煌逆i盈厘。

2. 代碼塊同步
synchronized(this){ //TODO } 描述同①

3. 方法同步
public synchronized static void method3
鎖住的是該類睁枕,當(dāng)所有該類的對象(多個(gè)對象)在不同線程中調(diào)用這個(gè)static同步方法時(shí),線程之間會(huì)形成互斥沸手,達(dá)到同步效果外遇。

4. 代碼塊同步
synchronized(Test.class){ //TODO} 同③

5. 代碼塊同步
synchronized(o) {}
這里面的o可以是一個(gè)任何Object對象或數(shù)組,并不一定是它本身對象或者類契吉,誰擁有o這個(gè)鎖跳仿,誰就能夠操作該塊程序代碼。

Lock 接口:

Lock捐晶,鎖對象菲语。在Java中鎖是用來控制多個(gè)線程訪問共享資源的方式,一般來說惑灵,一個(gè)鎖能夠防止多個(gè)線程同時(shí)訪問共享資源(但有的鎖可以允許多個(gè)線程并發(fā)訪問共享資源山上,比如讀寫鎖,后面我們會(huì)分析)英支。在Lock接口出現(xiàn)之前佩憾,Java程序是靠synchronized關(guān)鍵字(后面分析)實(shí)現(xiàn)鎖功能的,而JAVA SE5.0之后并發(fā)包中新增了Lock接口用來實(shí)現(xiàn)鎖的功能干花,它提供了與synchronized關(guān)鍵字類似的同步功能鸯屿,只是在使用時(shí)需要顯式地獲取和釋放鎖,缺點(diǎn)就是缺少像synchronized那樣隱式獲取釋放鎖的便捷性把敢,但是卻擁有了鎖獲取與釋放的可操作性寄摆,可中斷的獲取鎖以及超時(shí)獲取鎖等多種synchronized關(guān)鍵字所不具備的同步特性。

CPU在調(diào)度線程的時(shí)候是在等待隊(duì)列里隨機(jī)挑選一個(gè)線程修赞,由于這種隨機(jī)性所以是無法保證線程先到先得的(synchronized控制的鎖就是這種非公平鎖)婶恼。但這樣就會(huì)產(chǎn)生饑餓現(xiàn)象,即有些線程(優(yōu)先級(jí)較低的線程)可能永遠(yuǎn)也無法獲取CPU的執(zhí)行權(quán)柏副,優(yōu)先級(jí)高的線程會(huì)不斷的強(qiáng)制它的資源勾邦。那么如何解決饑餓問題呢,這就需要公平鎖了割择。公平鎖可以保證線程按照時(shí)間的先后順序執(zhí)行眷篇,避免饑餓現(xiàn)象的產(chǎn)生。但公平鎖的效率比較低荔泳,因?yàn)橐獙?shí)現(xiàn)順序執(zhí)行蕉饼,需要維護(hù)一個(gè)有序隊(duì)列虐杯。

ReentrantLock便是一種公平鎖,通過在構(gòu)造方法中傳入true就是公平鎖昧港,傳入false擎椰,就是非公平鎖。

synchronized和ReentrantLock的比較

  1. Lock是一個(gè)接口创肥,而synchronized是Java中的關(guān)鍵字达舒,synchronized是內(nèi)置的語言實(shí)現(xiàn);
  2. synchronized在發(fā)生異常時(shí)叹侄,會(huì)自動(dòng)釋放線程占有的鎖巩搏,因此不會(huì)導(dǎo)致死鎖現(xiàn)象發(fā)生;而Lock在發(fā)生異常時(shí)趾代,如果沒有主動(dòng)通過unLock()去釋放鎖塔猾,則很可能造成死鎖現(xiàn)象,因此使用Lock時(shí)需要在finally塊中釋放鎖稽坤;
  3. Lock可以讓等待鎖的線程響應(yīng)中斷,而synchronized卻不行糯俗,使用synchronized時(shí)尿褪,等待的線程會(huì)一直等待下去,不能夠響應(yīng)中斷得湘;
  4. 通過Lock可以知道有沒有成功獲取鎖杖玲,而synchronized卻無法辦到。
  5. Lock可以提高多個(gè)線程進(jìn)行讀操作的效率淘正。

Runnable接口和Callable接口的區(qū)別

Runnable接口中的run()方法的返回值是void摆马,它做的事情只是純粹地去執(zhí)行run()方法中的代碼而已;Callable接口中的call()方法是有返回值的鸿吆,是一個(gè)泛型囤采,和Future、FutureTask配合可以用來獲取異步執(zhí)行的結(jié)果惩淳。

這其實(shí)是很有用的一個(gè)特性蕉毯,因?yàn)槎嗑€程相比單線程更難、更復(fù)雜的一個(gè)重要原因就是因?yàn)槎嗑€程充滿著未知性思犁,某條線程是否執(zhí)行了代虾?某條線程執(zhí)行了多久?某條線程執(zhí)行的時(shí)候我們期望的數(shù)據(jù)是否已經(jīng)賦值完畢激蹲?無法得知棉磨,我們能做的只是等待這條多線程的任務(wù)執(zhí)行完畢而已。而Callable+Future/FutureTask卻可以獲取多線程運(yùn)行的結(jié)果学辱,可以在等待時(shí)間太長沒獲取到需要的數(shù)據(jù)的情況下取消該線程的任務(wù)乘瓤,真的是非常有用环形。

volatile關(guān)鍵字的作用

一個(gè)非常重要的問題,是每個(gè)學(xué)習(xí)馅扣、應(yīng)用多線程的Java程序員都必須掌握的斟赚。理解volatile關(guān)鍵字的作用的前提是要理解Java內(nèi)存模型,這里就不講Java內(nèi)存模型了差油,可以參見第31點(diǎn)拗军,volatile關(guān)鍵字的作用主要有兩個(gè):

  1. 多線程主要圍繞可見性和原子性兩個(gè)特性而展開,使用volatile關(guān)鍵字修飾的變量蓄喇,保證了其在多線程之間的可見性发侵,即每次讀取到volatile變量,一定是最新的數(shù)據(jù)
  2. 代碼底層執(zhí)行不像我們看到的高級(jí)語言—-Java程序這么簡單妆偏,它的執(zhí)行是Java代碼–>字節(jié)碼–>根據(jù)字節(jié)碼執(zhí)行對應(yīng)的C/C++代碼–>C/C++代碼被編譯成匯編語言–>和硬件電路交互刃鳄,現(xiàn)實(shí)中,為了獲取更好的性能JVM可能會(huì)對指令進(jìn)行重排序钱骂,多線程下可能會(huì)出現(xiàn)一些意想不到的問題叔锐。使用volatile則會(huì)對禁止語義重排序,當(dāng)然這也一定程度上降低了代碼執(zhí)行效率

從實(shí)踐角度而言见秽,volatile的一個(gè)重要作用就是和CAS結(jié)合愉烙,保證了原子性,詳細(xì)的可以參見java.util.concurrent.atomic包下的類解取,比如AtomicInteger步责。

線程安全

Java中標(biāo)注自己是線程安全的類,實(shí)際上絕大多數(shù)都不是線程安全的禀苦,不過絕對線程安全的類蔓肯,Java中也有,比方說CopyOnWriteArrayList振乏、CopyOnWriteArraySet

相對線程安全也就是我們通常意義上所說的線程安全蔗包,像Vector這種,add慧邮、remove方法都是原子操作气忠,不會(huì)被打斷,但也僅限于此赋咽,如果有個(gè)線程在遍歷某個(gè)Vector旧噪、有個(gè)線程同時(shí)在add這個(gè)Vector,99%的情況下都會(huì)出現(xiàn)ConcurrentModificationException脓匿,也就是fail-fast機(jī)制淘钟。

算法篇

冒泡排序

原理:臨近的數(shù)字兩兩進(jìn)行比較,按照從小到大或者從大到小的順序進(jìn)行交換,這樣一趟過去后,最大或最小的數(shù)字被交換到了最后一位,然后再從頭開始進(jìn)行兩兩比較交換,直到倒數(shù)第二位時(shí)結(jié)束

void bubbleSort(int[] unsorted)
{
    for (int i = 0; i < unsorted.Length; i++)
    {
        for (int j = i; j < unsorted.Length; j++)
        {
            if (unsorted[i] > unsorted[j])
            {
                int temp = unsorted[i];
                unsorted[i] = unsorted[j];
                unsorted[j] = temp;
            }
        }
    }
}

鏈表是否有環(huán)

通過快慢指針

/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class ChkLoop {
    public int chkLoop(ListNode head, int adjust) {
        // write code here
        if(head==null||head.next==null)return -1;
        ListNode fast=head,slow=head;
        do{
            slow=slow.next;
            fast=fast.next.next;

        }while(slow!=fast&&fast.next!=null&&fast.next.next!=null);
        if(slow!=fast)return -1;
        slow=head;
        while(slow!=fast){
            fast=fast.next;
            slow=slow.next;
        }
        return slow.val;

    }
}

不用遞歸計(jì)算斐波那契數(shù)列fib
可以通過循環(huán)來做

public int fib(int index) {
    if (index <= 2)
        return 1;

    int f1 = 1;// 前前位
    int f2 = 1;// 前一位
    int fn = 0;
    for (int i = 0; i < index - 2; i++) {
        //這里是換位操作
        fn = f1 + f2;
        f1 = f2;
        f2 = fn;
    }
    return fn;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市陪毡,隨后出現(xiàn)的幾起案子米母,更是在濱河造成了極大的恐慌勾扭,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,948評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件铁瞒,死亡現(xiàn)場離奇詭異妙色,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)慧耍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,371評論 3 385
  • 文/潘曉璐 我一進(jìn)店門身辨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人芍碧,你說我怎么就攤上這事煌珊。” “怎么了泌豆?”我有些...
    開封第一講書人閱讀 157,490評論 0 348
  • 文/不壞的土叔 我叫張陵定庵,是天一觀的道長。 經(jīng)常有香客問我踪危,道長蔬浙,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,521評論 1 284
  • 正文 為了忘掉前任贞远,我火速辦了婚禮畴博,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘兴革。我一直安慰自己,他們只是感情好蜜唾,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,627評論 6 386
  • 文/花漫 我一把揭開白布杂曲。 她就那樣靜靜地躺著,像睡著了一般袁余。 火紅的嫁衣襯著肌膚如雪擎勘。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,842評論 1 290
  • 那天颖榜,我揣著相機(jī)與錄音棚饵,去河邊找鬼。 笑死掩完,一個(gè)胖子當(dāng)著我的面吹牛噪漾,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播且蓬,決...
    沈念sama閱讀 38,997評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼欣硼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了恶阴?” 一聲冷哼從身側(cè)響起诈胜,我...
    開封第一講書人閱讀 37,741評論 0 268
  • 序言:老撾萬榮一對情侶失蹤豹障,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后焦匈,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體血公,經(jīng)...
    沈念sama閱讀 44,203評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,534評論 2 327
  • 正文 我和宋清朗相戀三年缓熟,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了累魔。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,673評論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡荚虚,死狀恐怖薛夜,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情版述,我是刑警寧澤梯澜,帶...
    沈念sama閱讀 34,339評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站渴析,受9級(jí)特大地震影響晚伙,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜俭茧,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,955評論 3 313
  • 文/蒙蒙 一咆疗、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧母债,春花似錦午磁、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,770評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至衙熔,卻和暖如春登颓,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背红氯。 一陣腳步聲響...
    開封第一講書人閱讀 32,000評論 1 266
  • 我被黑心中介騙來泰國打工框咙, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人痢甘。 一個(gè)月前我還...
    沈念sama閱讀 46,394評論 2 360
  • 正文 我出身青樓喇嘱,卻偏偏與公主長得像,于是被迫代替她去往敵國和親塞栅。 傳聞我的和親對象是個(gè)殘疾皇子婉称,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,562評論 2 349

推薦閱讀更多精彩內(nèi)容