【威哥說】雖然不能說所有的面試題都是必學(xué)的技術(shù)知識點,但是大家學(xué)習(xí)都是以找工作為目標(biāo)肪虎,時刻了解用人單位的技術(shù)要求豌研,對于自己絕對是有利無害的。希望我所做的每一件事都可以對大家有所幫助贬丛。
Java部分
1. 抽象類和接口的區(qū)別
一個類只能繼承單個類撩银,但是可以實現(xiàn)多個接口 接口強(qiáng)調(diào)特定功能的實現(xiàn),而抽象類強(qiáng)調(diào)所屬關(guān)系抽象類中的所有方法并不一定要是抽象的豺憔,你可以選擇在抽象類中實現(xiàn)一些基本的方法额获。而接口要求所有的方法都必須是抽象的
2. Override和Overload的含義和區(qū)別
Overload(重載),它可以表現(xiàn)類的多態(tài)性恭应,可以是函數(shù)里面可以有相同的函數(shù)名但是參數(shù)名咪啡、返回值、類型不能相同暮屡;或者說可以改變參數(shù)撤摸、類型、返回值但是函數(shù)名字依然不變。 Override(重寫)准夷,在子類繼承父類的時候子類中可以定義某方法與其父類有相同的名稱和參數(shù)钥飞,當(dāng)子類在調(diào)用這一函數(shù)時自動調(diào)用子類的方法,而父類相當(dāng)于被覆蓋(重寫)了衫嵌。
3. 解析XML的幾種方式的原理與特點:DOM读宙、SAX、PULL
DOM:消耗內(nèi)存:先把xml文檔都讀到內(nèi)存中楔绞,然后再用DOM API來訪問樹形結(jié)構(gòu)结闸,并獲取數(shù)據(jù)。這個寫起來很簡單酒朵,但是很消耗內(nèi)存桦锄。要是數(shù)據(jù)過大,手機(jī)不夠牛逼蔫耽,可能手機(jī)直接死機(jī)结耀。 SAX:解析效率高,占用內(nèi)存少匙铡,基于事件驅(qū)動的:更加簡單地說就是對文檔進(jìn)行順序掃描图甜,當(dāng)掃描到文檔(document)開始與結(jié)束、元素(element)開始與結(jié)束鳖眼、文檔(document)結(jié)束等地方時通知事件處理函數(shù)黑毅,由事件處理函數(shù)做相應(yīng)動作,然后繼續(xù)同樣的掃描钦讳,直至文檔結(jié)束博肋。 PULL:與 SAX 類似,也是基于事件驅(qū)動蜂厅,我們可以調(diào)用它的next()方法匪凡,來獲取下一個解析事件(就是開始文檔,結(jié)束文檔掘猿,開始標(biāo)簽病游,結(jié)束標(biāo)簽),當(dāng)處于某個元素時可以調(diào)用XmlPullParser的getAttributte()方法來獲取屬性的值稠通,也可調(diào)用它的nextText()獲取本節(jié)點的值衬衬。
4. ArrayList、LinkedList改橘、Vector的區(qū)別
ArrayList 和Vector底層是采用數(shù)組方式存儲數(shù)據(jù)滋尉,Vector由于使用了synchronized方法(線程安全)所以性能上比ArrayList要差。 LinkedList使用雙向鏈表實現(xiàn)存儲飞主,隨機(jī)存取比較慢狮惜,但是插入速度快高诺。 HashMap的底層源碼實現(xiàn):當(dāng)我們往HashMap中put元素的時候,先根據(jù)key的hashCode重新計算hash值碾篡,根據(jù)hash值得到這個元素在數(shù)組中的位置(即下標(biāo))虱而,如果數(shù)組該位置上已經(jīng)存放有其他元素了,那么在這個位置上的元素將以鏈表的形式存放开泽,新加入的放在鏈頭牡拇,最先加入的放在鏈尾。如果數(shù)組該位置上沒有元素穆律,就直接將該元素放到此數(shù)組中的該位置上惠呼。
5. HashMap和 HashTable的區(qū)別
HashTable 則是基于 Map接口實現(xiàn)的 HashTable 是線程安全的, HashMap 則是線程不安全的 HashMap可以讓你將空值作為一個表的條目的key或value峦耘。
6. 對象的創(chuàng)建有幾種方式
有4種顯式地創(chuàng)建對象的方式: 用new語句創(chuàng)建對象剔蹋,這是最常用的創(chuàng)建對象的方式。 運用反射手段贡歧,調(diào)用Java.lang.Class或者java.lang.reflect.Constructor類的newInstance()實例方法滩租。 調(diào)用對象的clone()方法赋秀。 運用反序列化手段利朵,調(diào)用java.io.ObjectInputStream對象的readObject()方法.
Android部分
1. Activity 生命周期
生命周期描述的是一個類 從創(chuàng)建(new出來)到死亡(垃圾回收)的過程中會執(zhí)行的方法。在這個過程中猎莲,會針對不同的生命階段會調(diào)用不同的方法 Activity從創(chuàng)建到銷毀有多種狀態(tài)绍弟,從一種狀態(tài)到另一種狀態(tài)時會激發(fā)相應(yīng)的回調(diào)方法,這些回調(diào)方法包括:oncreate ondestroy onstop onstart onresume onpause 其實這些方法都是兩兩對應(yīng)的著洼,onCreate創(chuàng)建與onDestroy銷毀樟遣;onStart可見與onStop不可見;onResume可編輯(即焦點)與onPause身笤;這6個方法是相對應(yīng)的豹悬,那么就只剩下一個onRestart方法了,在Activity被onStop后液荸,但是沒有被onDestroy瞻佛,在再次啟動此Activity時就調(diào)用onRestart(而不再調(diào)用onCreate)方法;如果被onDestroy了娇钱,則是調(diào)用onCreate方法伤柄。
2. dvm的進(jìn)程和Linux的進(jìn)程, 應(yīng)用程序的進(jìn)程是否為同一個概念
Dvm的進(jìn)程是dalivk虛擬機(jī)進(jìn)程,每個android程序都運行在自己的進(jìn)程里面,每個android程序系統(tǒng)都會給他分配一個單獨的liunx uid(user id),每個dvm都是linux里面的一個進(jìn)程.所以說這兩個進(jìn)程是一個進(jìn)程.
3. 請解釋下在單線程模型中Message,Handler,Message Queue,Looper之間的關(guān)系。
拿主線程來說文搂,主線程啟動時會調(diào)用Looper.prepare()方法适刀,會初始化一個Looper,放入Threadlocal中煤蹭,接著調(diào)用Looper.loop()不斷遍歷Message Queue笔喉, Handler的創(chuàng)建依賴與當(dāng)前線程中的Looper取视,如果當(dāng)前線程沒有Looper則必須調(diào)用Looper.prepare()。Handler , sendMessage到MessageQueue然遏,Looper不斷從MessageQueue中取出消息贫途,回調(diào)handleMessage方法。
4. 啟動一個程序待侵,可以主界面點擊圖標(biāo)進(jìn)入赁豆,也可以從一個程序中跳轉(zhuǎn)過去,二者有什么區(qū)別覆履?
是因為啟動程序(主界面也是一個app)崩侠,發(fā)現(xiàn)了在這個程序中存在一個設(shè)置為的activity,所以這個launcher會把icon提出來,放在主界面上那先。當(dāng)用戶點擊icon的時候农猬,發(fā)出一個Intent:
Intent intent = mActivity.getPackageManager().getLaunchIntentForPackage(packageName);
mActivity.startActivity(intent);
跳過去可以跳到任意允許的頁面,如一個程序可以下載售淡,那么真正下載的頁面可能不是首頁(也有可能是首頁)斤葱,這時還是構(gòu)造一個Intent,startActivity.
這個intent中的action可能有多種view,download都有可能揖闸。系統(tǒng)會根據(jù)第三方程序向系統(tǒng)注冊的功能揍堕,為你的Intent選擇可以打開的程序或者頁面。所以唯一的一點汤纸。
不同的是從icon的點擊啟動的intent的action是相對單一的衩茸,從程序中跳轉(zhuǎn)或者啟動可能樣式更多一些。本質(zhì)是相同的贮泞。
5.注冊Service需要注意什么
Service還是運行在主線程當(dāng)中的楞慈,所以如果需要執(zhí)行一些復(fù)雜的邏輯操作,最好在服務(wù)的內(nèi)部手動創(chuàng)建子線程進(jìn)行處理啃擦,否則會出現(xiàn)UI線程被阻塞的問題
6. Service與Activity怎么實現(xiàn)通信
方法一:添加一個繼承Binder的內(nèi)部類囊蓝,并添加相應(yīng)的邏輯方法
重寫Service的onBind方法,返回我們剛剛定義的那個內(nèi)部類實例
Activity中創(chuàng)建一個ServiceConnection的匿名內(nèi)部類令蛉,并且重寫里面的onServiceConnected方法和onServiceDisconnected方法聚霜,這兩個方法分別會在活動與服務(wù)成功綁定以及解除綁定的時候調(diào)用,在onServiceConnected方法中言询,我們可以得到一個剛才那個service的binder對象俯萎,通過對這個binder對象進(jìn)行向下轉(zhuǎn)型,得到我們那個自定義的Binder實例运杭,有了這個實例夫啊,做可以調(diào)用這個實例里面的具體方法進(jìn)行需要的操作了
方法二:通過BroadCast(廣播)的形式 當(dāng)我們的進(jìn)度發(fā)生變化的時候我們發(fā)送一條廣播,然后在Activity的注冊廣播接收器辆憔,接收到廣播之后更新視圖
7.ListView卡頓的原因與性能優(yōu)化撇眯,越多越好
重用converView: 通過復(fù)用converview來減少不必要的view的創(chuàng)建报嵌,另外Infalte操作會把xml文件實例化成相應(yīng)的View實例,屬于IO操作熊榛,是耗時操作锚国。
減少findViewById()操作: 將xml文件中的元素封裝成viewholder靜態(tài)類,通過converview的setTag和getTag方法將view與相應(yīng)的holder對象綁定在一起玄坦,避免不必要的findviewbyid操作
避免在 getView 方法中做耗時的操作: 例如加載本地 Image 需要載入內(nèi)存以及解析 Bitmap 血筑,都是比較耗時的操作,如果用戶快速滑動listview煎楣,會因為getview邏輯過于復(fù)雜耗時而造成滑動卡頓現(xiàn)象豺总。用戶滑動時候不要加載圖片,待滑動完成再加載择懂,可以使用這個第三方庫glide
Item的布局層次結(jié)構(gòu)盡量簡單喻喳,避免布局太深或者不必要的重繪
盡量能保證 Adapter 的 hasStableIds() 返回 true 這樣在 notifyDataSetChanged() 的時候,如果item內(nèi)容并沒有變化困曙,ListView 將不會重新繪制這個 View表伦,達(dá)到優(yōu)化的目的
在一些場景中,ScollView內(nèi)會包含多個ListView慷丽,可以把listview的高度寫死固定下來蹦哼。 由于ScollView在快速滑動過程中需要大量計算每一個listview的高度,阻塞了UI線程導(dǎo)致卡頓現(xiàn)象出現(xiàn)盈魁,如果我們每一個item的高度都是均勻的翔怎,可以通過計算把listview的高度確定下來窃诉,避免卡頓現(xiàn)象出現(xiàn)
使用 RecycleView 代替listview: 每個item內(nèi)容的變動杨耙,listview都需要去調(diào)用notifyDataSetChanged來更新全部的item,太浪費性能了飘痛。RecycleView可以實現(xiàn)當(dāng)個item的局部刷新珊膜,并且引入了增加和刪除的動態(tài)效果,在性能上和定制上都有很大的改善
ListView 中元素避免半透明: 半透明繪制需要大量乘法計算宣脉,在滑動時不停重繪會造成大量的計算车柠,在比較差的機(jī)子上會比較卡。 在設(shè)計上能不半透明就不不半透明塑猖。實在要弄就把在滑動的時候把半透明設(shè)置成不透明竹祷,滑動完再重新設(shè)置成半透明。
盡量開啟硬件加速: 硬件加速提升巨大羊苟,避免使用一些不支持的函數(shù)導(dǎo)致含淚關(guān)閉某個地方的硬件加速塑陵。當(dāng)然這一條不只是對 ListView。
8. 內(nèi)存泄漏有哪些場景以及解決方法
類的靜態(tài)變量持有大數(shù)據(jù)對象 靜態(tài)變量長期維持到大數(shù)據(jù)對象的引用蜡励,阻止垃圾回收令花。
非靜態(tài)內(nèi)部類存在靜態(tài)實例 非靜態(tài)內(nèi)部類會維持一個到外部類實例的引用阻桅,如果非靜態(tài)內(nèi)部類的實例是靜態(tài)的,就會間接長期維持著外部類的引用兼都,阻止被回收掉嫂沉。
資源對象未關(guān)閉 資源性對象比如(Cursor,F(xiàn)ile文件等)往往都用了一些緩沖扮碧,我們在不使用的時候趟章,應(yīng)該及時關(guān)閉它們, 以便它們的緩沖及時回收內(nèi)存慎王。它們的緩沖不僅存在于java虛擬機(jī)內(nèi)尤揣,還存在于java虛擬機(jī)外。 如果我們僅僅是把它的引用設(shè)置為null,而不關(guān)閉它們柬祠,往往會造成內(nèi)存泄露北戏。 解決辦法: 比如SQLiteCursor(在析構(gòu)函數(shù)finalize(),如果我們沒有關(guān)閉它,它自己會調(diào)close()關(guān)閉)漫蛔, 如果我們沒有關(guān)閉它嗜愈,系統(tǒng)在回收它時也會關(guān)閉它,但是這樣的效率太低了莽龟。 因此對于資源性對象在不使用的時候蠕嫁,應(yīng)該調(diào)用它的close()函數(shù),將其關(guān)閉掉毯盈,然后才置為null. 在我們的程序退出時一定要確保我們的資源性對象已經(jīng)關(guān)閉剃毒。 程序中經(jīng)常會進(jìn)行查詢數(shù)據(jù)庫的操作,但是經(jīng)常會有使用完畢Cursor后沒有關(guān)閉的情況搂赋。如果我們的查詢結(jié)果集比較小赘阀, 對內(nèi)存的消耗不容易被發(fā)現(xiàn),只有在常時間大量操作的情況下才會復(fù)現(xiàn)內(nèi)存問題脑奠,這樣就會給以后的測試和問題排查帶來困難和風(fēng)險基公,記得try catch后,在finally方法中關(guān)閉連接
Handler內(nèi)存泄漏 Handler作為內(nèi)部類存在于Activity中宋欺,但是Handler生命周期與Activity生命周期往往并不是相同的轰豆,比如當(dāng)Handler對象有Message在排隊,則無法釋放齿诞,進(jìn)而導(dǎo)致本該釋放的Acitivity也沒有辦法進(jìn)行回收酸休。 解決辦法:
一些不良代碼習(xí)慣 有些代碼并不造成內(nèi)存泄露,但是他們的資源沒有得到重用祷杈,頻繁的申請內(nèi)存和銷毀內(nèi)存斑司,消耗CPU資源的同時,也引起內(nèi)存抖動 解決方案 如果需要頻繁的申請內(nèi)存對象和和釋放對象吠式,可以考慮使用對象池來增加對象的復(fù)用陡厘。 例如ListView便是采用這種思想抽米,通過復(fù)用converview來避免頻繁的GC
9. 如何避免 OOM 問題的出現(xiàn)
使用更加輕量的數(shù)據(jù)結(jié)構(gòu) 例如,我們可以考慮使用ArrayMap/SparseArray而不是HashMap等傳統(tǒng)數(shù)據(jù)結(jié)構(gòu)糙置。通常的HashMap的實現(xiàn)方式更加消耗內(nèi)存云茸,因為它需要一個額外的實例對象來記錄Mapping操作。另外谤饭,SparseArray更加高效标捺,在于他們避免了對key與value的自動裝箱(autoboxing),并且避免了裝箱后的解箱揉抵。
避免在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里面使用到枚舉冤今。
減小Bitmap對象的內(nèi)存占用 Bitmap是一個極容易消耗內(nèi)存的大胖子闺兢,減小創(chuàng)建出來的Bitmap的內(nèi)存占用可謂是重中之重,戏罢,通常來說有以下2個措施: inSampleSize:縮放比例屋谭,在把圖片載入內(nèi)存之前,我們需要先計算出一個合適的縮放比例龟糕,避免不必要的大圖載入桐磁。 decode format:解碼格式,選擇ARGB_6666/RBG_545/ARGB_4444/ALPHA_6讲岁,存在很大差異
Bitmap對象的復(fù)用 縮小Bitmap的同時我擂,也需要提高BitMap對象的復(fù)用率,避免頻繁創(chuàng)建BitMap對象缓艳,復(fù)用的方法有以下2個措施 LRUCache : “最近最少使用算法”在Android中有極其普遍的應(yīng)用校摩。ListView與GridView等顯示大量圖片的控件里,就是使用LRU的機(jī)制來緩存處理好的Bitmap郎任,把近期最少使用的數(shù)據(jù)從緩存中移除秧耗,保留使用最頻繁的數(shù)據(jù)备籽, inBitMap高級特性:利用inBitmap的高級特性提高Android系統(tǒng)在Bitmap分配與釋放執(zhí)行效率舶治。使用inBitmap屬性可以告知Bitmap解碼器去嘗試使用已經(jīng)存在的內(nèi)存區(qū)域,新解碼的Bitmap會嘗試去使用之前那張Bitmap在Heap中所占據(jù)的pixel data內(nèi)存區(qū)域车猬,而不是去問內(nèi)存重新申請一塊區(qū)域來存放Bitmap霉猛。利用這種特性,即使是上千張的圖片珠闰,也只會僅僅只需要占用屏幕所能夠顯示的圖片數(shù)量的內(nèi)存大小
使用更小的圖片 在涉及給到資源圖片時惜浅,我們需要特別留意這張圖片是否存在可以壓縮的空間,是否可以使用更小的圖片伏嗜。盡量使用更小的圖片不僅可以減少內(nèi)存的使用坛悉,還能避免出現(xiàn)大量的InflationException伐厌。假設(shè)有一張很大的圖片被XML文件直接引用,很有可能在初始化視圖時會因為內(nèi)存不足而發(fā)生InflationException裸影,這個問題的根本原因其實是發(fā)生了OOM挣轨。
StringBuilder 在有些時候,代碼中會需要使用到大量的字符串拼接的操作轩猩,這種時候有必要考慮使用StringBuilder來替代頻繁的“+”卷扮。避免在onDraw方法里面執(zhí)行對象的創(chuàng)建 類似onDraw等頻繁調(diào)用的方法,一定需要注意避免在這里做創(chuàng)建對象的操作均践,因為他會迅速增加內(nèi)存的使用晤锹,而且很容易引起頻繁的gc,甚至是內(nèi)存抖動彤委。
10. Android中的動畫有哪些鞭铆,區(qū)別是什么
逐幀動畫(Drawable Animation):加載一系列Drawable資源來創(chuàng)建動畫,簡單來說就是播放一系列的圖片來實現(xiàn)動畫效果焦影,可以自定義每張圖片的持續(xù)時間
補間動畫(Tween Animation):Tween可以對View對象實現(xiàn)一系列簡單的動畫效果衔彻,比如位移,縮放偷办,旋轉(zhuǎn)艰额,透明度等等。但是它并不會改變View屬性的值椒涯,只是改變了View的繪制的位置柄沮,比如,一個按鈕在動畫過后废岂,不在原來的位置祖搓,但是觸發(fā)點擊事件的仍然是原來的坐標(biāo)。
屬性動畫(Property Animation):動畫的對象除了傳統(tǒng)的View對象湖苞,還可以是Object對象拯欧,動畫結(jié)束后,Object對象的屬性值被實實在在的改變了
11. 數(shù)據(jù)持久化的四種方式有哪些财骨?
文件存儲:通過java.io.FileInputStream和java.io.FileOutputStream這兩個類來實現(xiàn)對文件的讀寫镐作,java.io.File類則用來構(gòu)造一個具體指向某個文件或者文件夾的對象。
SharedPreferences:SharedPreferences是一種輕量級的數(shù)據(jù)存儲機(jī)制隆箩,他將一些簡單的數(shù)據(jù)類型的數(shù)據(jù)该贾,包括boolean類型,int類型捌臊,float類型杨蛋,long類型以及String類型的數(shù)據(jù),以鍵值對的形式存儲在應(yīng)用程序的私有Preferences目錄(/data/data/<包名>/shared_prefs/)中,這種Preferences機(jī)制廣泛應(yīng)用于存儲應(yīng)用程序中的配置信息逞力。SQLite數(shù)據(jù)庫: 當(dāng)應(yīng)用程序需要處理的數(shù)據(jù)量比較大時曙寡,為了更加合理地存儲、管理寇荧、查詢數(shù)據(jù)卵皂,我們往往使用關(guān)系數(shù)據(jù)庫來存儲數(shù)據(jù)。Android系統(tǒng)的很多用戶數(shù)據(jù)砚亭,如聯(lián)系人信息灯变,通話記錄,短信息等捅膘,都是存儲在SQLite數(shù)據(jù)庫當(dāng)中的添祸,所以利用操作SQLite數(shù)據(jù)庫的API可以同樣方便的訪問和修改這些數(shù)據(jù)。
ContentProvider:主要用于在不同的應(yīng)用程序之間實現(xiàn)數(shù)據(jù)共享的功能寻仗,不同于sharepreference和文件存儲中的兩種全局可讀寫操作模式刃泌,內(nèi)容提供其可以選擇只對哪一部分?jǐn)?shù)據(jù)進(jìn)行共享,從而保證我們程序中的隱私數(shù)據(jù)不會有泄漏的風(fēng)險