java/Android面試題精選
雖然不能說所有的面試題都是必學的技術知識點,但是大家學習都是以找工作為目標,時刻了解用人單位的技術要求,對于自己絕對是有利無害的。希望我所做的每一件事都可以對大家有所幫助。
Java部分
1. 抽象類和接口的區別
一個類只能繼承單個類,但是可以實現多個接口 接口強調特定功能的實現,而抽象類強調所屬關系抽象類中的所有方法并不一定要是抽象的,你可以選擇在抽象類中實現一些基本的方法。而接口要求所有的方法都必須是抽象的
2. Override和Overload的含義和區別
Overload(重載),它可以表現類的多態性,可以是函數里面可以有相同的函數名但是參數名、返回值、類型不能相同;或者說可以改變參數、類型、返回值但是函數名字依然不變。 Override(重寫),在子類繼承父類的時候子類中可以定義某方法與其父類有相同的名稱和參數,當子類在調用這一函數時自動調用子類的方法,而父類相當于被覆蓋(重寫)了。
3. 解析XML的幾種方式的原理與特點:DOM、SAX、PULL
DOM:消耗內存:先把xml文檔都讀到內存中,然后再用DOM API來訪問樹形結構,并獲取數據。這個寫起來很簡單,但是很消耗內存。要是數據過大,手機不夠牛逼,可能手機直接死機。 SAX:解析效率高,占用內存少,基于事件驅動的:更加簡單地說就是對文檔進行順序掃描,當掃描到文檔(document)開始與結束、元素(element)開始與結束、文檔(document)結束等地方時通知事件處理函數,由事件處理函數做相應動作,然后繼續同樣的掃描,直至文檔結束。 PULL:與 SAX 類似,也是基于事件驅動,我們可以調用它的next()方法,來獲取下一個解析事件(就是開始文檔,結束文檔,開始標簽,結束標簽),當處于某個元素時可以調用XmlPullParser的getAttributte()方法來獲取屬性的值,也可調用它的nextText()獲取本節點的值。
4. ArrayList、LinkedList、Vector的區別
ArrayList 和Vector底層是采用數組方式存儲數據,Vector由于使用了synchronized方法(線程安全)所以性能上比ArrayList要差。 LinkedList使用雙向鏈表實現存儲,隨機存取比較慢,但是插入速度快。 HashMap的底層源碼實現:當我們往HashMap中put元素的時候,先根據key的hashCode重新計算hash值,根據hash值得到這個元素在數組中的位置(即下標),如果數組該位置上已經存放有其他元素了,那么在這個位置上的元素將以鏈表的形式存放,新加入的放在鏈頭,最先加入的放在鏈尾。如果數組該位置上沒有元素,就直接將該元素放到此數組中的該位置上。
5. HashMap和 HashTable的區別
HashTable 則是基于 Map接口實現的 HashTable 是線程安全的, HashMap 則是線程不安全的 HashMap可以讓你將空值作為一個表的條目的key或value。
6. 對象的創建有幾種方式
有4種顯式地創建對象的方式: 用new語句創建對象,這是最常用的創建對象的方式。 運用反射手段,調用Java.lang.Class或者java.lang.reflect.Constructor類的newInstance()實例方法。 調用對象的clone()方法。 運用反序列化手段,調用java.io.ObjectInputStream對象的readObject()方法.
Android部分
1. Activity 生命周期
生命周期描述的是一個類 從創建(new出來)到死亡(垃圾回收)的過程中會執行的方法。在這個過程中,會針對不同的生命階段會調用不同的方法 Activity從創建到銷毀有多種狀態,從一種狀態到另一種狀態時會激發相應的回調方法,這些回調方法包括:oncreate ondestroy onstop onstart onresume onpause 其實這些方法都是兩兩對應的,onCreate創建與onDestroy銷毀;onStart可見與onStop不可見;onResume可編輯(即焦點)與onPause;這6個方法是相對應的,那么就只剩下一個onRestart方法了,在Activity被onStop后,但是沒有被onDestroy,在再次啟動此Activity時就調用onRestart(而不再調用onCreate)方法;如果被onDestroy了,則是調用onCreate方法。
2. dvm的進程和Linux的進程, 應用程序的進程是否為同一個概念
Dvm的進程是dalivk虛擬機進程,每個android程序都運行在自己的進程里面,每個android程序系統都會給他分配一個單獨的liunx uid(user id),每個dvm都是linux里面的一個進程.所以說這兩個進程是一個進程.
3. 請解釋下在單線程模型中Message,Handler,Message Queue,Looper之間的關系。
拿主線程來說,主線程啟動時會調用Looper.prepare()方法,會初始化一個Looper,放入Threadlocal中,接著調用Looper.loop()不斷遍歷Message Queue, Handler的創建依賴與當前線程中的Looper,如果當前線程沒有Looper則必須調用Looper.prepare()。Handler , sendMessage到MessageQueue,Looper不斷從MessageQueue中取出消息,回調handleMessage方法。
4. 啟動一個程序,可以主界面點擊圖標進入,也可以從一個程序中跳轉過去,二者有什么區別?
是因為啟動程序(主界面也是一個app),發現了在這個程序中存在一個設置為的activity,所以這個launcher會把icon提出來,放在主界面上。當用戶點擊icon的時候,發出一個Intent:
Intent intent = mActivity.getPackageManager().getLaunchIntentForPackage(packageName);
mActivity.startActivity(intent);
跳過去可以跳到任意允許的頁面,如一個程序可以下載,那么真正下載的頁面可能不是首頁(也有可能是首頁),這時還是構造一個Intent,startActivity.
這個intent中的action可能有多種view,download都有可能。系統會根據第三方程序向系統注冊的功能,為你的Intent選擇可以打開的程序或者頁面。所以唯一的一點。
不同的是從icon的點擊啟動的intent的action是相對單一的,從程序中跳轉或者啟動可能樣式更多一些。本質是相同的。
5.注冊Service需要注意什么
Service還是運行在主線程當中的,所以如果需要執行一些復雜的邏輯操作,最好在服務的內部手動創建子線程進行處理,否則會出現UI線程被阻塞的問題
6. Service與Activity怎么實現通信
方法一: 添加一個繼承Binder的內部類,并添加相應的邏輯方法
重寫Service的onBind方法,返回我們剛剛定義的那個內部類實例
Activity中創建一個ServiceConnection的匿名內部類,并且重寫里面的onServiceConnected方法和onServiceDisconnected方法,這兩個方法分別會在活動與服務成功綁定以及解除綁定的時候調用,在onServiceConnected方法中,我們可以得到一個剛才那個service的binder對象,通過對這個binder對象進行向下轉型,得到我們那個自定義的Binder實例,有了這個實例,做可以調用這個實例里面的具體方法進行需要的操作了
方法二: 通過BroadCast(廣播)的形式 當我們的進度發生變化的時候我們發送一條廣播,然后在Activity的注冊廣播接收器,接收到廣播之后更新視圖
7.ListView卡頓的原因與性能優化,越多越好
重用converView: 通過復用converview來減少不必要的view的創建,另外Infalte操作會把xml文件實例化成相應的View實例,屬于IO操作,是耗時操作。
減少findViewById()操作: 將xml文件中的元素封裝成viewholder靜態類,通過converview的setTag和getTag方法將view與相應的holder對象綁定在一起,避免不必要的findviewbyid操作
避免在 getView 方法中做耗時的操作: 例如加載本地 Image 需要載入內存以及解析 Bitmap ,都是比較耗時的操作,如果用戶快速滑動listview,會因為getview邏輯過于復雜耗時而造成滑動卡頓現象。用戶滑動時候不要加載圖片,待滑動完成再加載,可以使用這個第三方庫glide
Item的布局層次結構盡量簡單,避免布局太深或者不必要的重繪
盡量能保證 Adapter 的 hasStableIds() 返回 true 這樣在 notifyDataSetChanged() 的時候,如果item內容并沒有變化,ListView 將不會重新繪制這個 View,達到優化的目的
在一些場景中,ScollView內會包含多個ListView,可以把listview的高度寫死固定下來。 由于ScollView在快速滑動過程中需要大量計算每一個listview的高度,阻塞了UI線程導致卡頓現象出現,如果我們每一個item的高度都是均勻的,可以通過計算把listview的高度確定下來,避免卡頓現象出現
使用 RecycleView 代替listview: 每個item內容的變動,listview都需要去調用notifyDataSetChanged來更新全部的item,太浪費性能了。RecycleView可以實現當個item的局部刷新,并且引入了增加和刪除的動態效果,在性能上和定制上都有很大的改善
ListView 中元素避免半透明: 半透明繪制需要大量乘法計算,在滑動時不停重繪會造成大量的計算,在比較差的機子上會比較卡。 在設計上能不半透明就不不半透明。實在要弄就把在滑動的時候把半透明設置成不透明,滑動完再重新設置成半透明。
盡量開啟硬件加速: 硬件加速提升巨大,避免使用一些不支持的函數導致含淚關閉某個地方的硬件加速。當然這一條不只是對 ListView。
8. 內存泄漏有哪些場景以及解決方法
類的靜態變量持有大數據對象 靜態變量長期維持到大數據對象的引用,阻止垃圾回收。
非靜態內部類存在靜態實例 非靜態內部類會維持一個到外部類實例的引用,如果非靜態內部類的實例是靜態的,就會間接長期維持著外部類的引用,阻止被回收掉。
資源對象未關閉 資源性對象比如(Cursor,File文件等)往往都用了一些緩沖,我們在不使用的時候,應該及時關閉它們, 以便它們的緩沖及時回收內存。它們的緩沖不僅存在于java虛擬機內,還存在于java虛擬機外。 如果我們僅僅是把它的引用設置為null,而不關閉它們,往往會造成內存泄露。 解決辦法: 比如SQLiteCursor(在析構函數finalize(),如果我們沒有關閉它,它自己會調close()關閉), 如果我們沒有關閉它,系統在回收它時也會關閉它,但是這樣的效率太低了。 因此對于資源性對象在不使用的時候,應該調用它的close()函數,將其關閉掉,然后才置為null. 在我們的程序退出時一定要確保我們的資源性對象已經關閉。 程序中經常會進行查詢數據庫的操作,但是經常會有使用完畢Cursor后沒有關閉的情況。如果我們的查詢結果集比較小, 對內存的消耗不容易被發現,只有在常時間大量操作的情況下才會復現內存問題,這樣就會給以后的測試和問題排查帶來困難和風險,記得try catch后,在finally方法中關閉連接
Handler內存泄漏 Handler作為內部類存在于Activity中,但是Handler生命周期與Activity生命周期往往并不是相同的,比如當Handler對象有Message在排隊,則無法釋放,進而導致本該釋放的Acitivity也沒有辦法進行回收。 解決辦法:
一些不良代碼習慣 有些代碼并不造成內存泄露,但是他們的資源沒有得到重用,頻繁的申請內存和銷毀內存,消耗CPU資源的同時,也引起內存抖動 解決方案 如果需要頻繁的申請內存對象和和釋放對象,可以考慮使用對象池來增加對象的復用。 例如ListView便是采用這種思想,通過復用converview來避免頻繁的GC
9. 如何避免 OOM 問題的出現
使用更加輕量的數據結構 例如,我們可以考慮使用ArrayMap/SparseArray而不是HashMap等傳統數據結構。通常的HashMap的實現方式更加消耗內存,因為它需要一個額外的實例對象來記錄Mapping操作。另外,SparseArray更加高效,在于他們避免了對key與value的自動裝箱(autoboxing),并且避免了裝箱后的解箱。
避免在Android里面使用Enum Android官方培訓課程提到過“Enums often require more than twice as much memory as static constants. You should strictly avoid using enums on Android.”,具體原理請參考《Android性能優化典范(三)》,所以請避免在Android里面使用到枚舉。
減小Bitmap對象的內存占用 Bitmap是一個極容易消耗內存的大胖子,減小創建出來的Bitmap的內存占用可謂是重中之重,,通常來說有以下2個措施: inSampleSize:縮放比例,在把圖片載入內存之前,我們需要先計算出一個合適的縮放比例,避免不必要的大圖載入。 decode format:解碼格式,選擇ARGB_6666/RBG_545/ARGB_4444/ALPHA_6,存在很大差異
Bitmap對象的復用 縮小Bitmap的同時,也需要提高BitMap對象的復用率,避免頻繁創建BitMap對象,復用的方法有以下2個措施 LRUCache : “最近最少使用算法”在Android中有極其普遍的應用。ListView與GridView等顯示大量圖片的控件里,就是使用LRU的機制來緩存處理好的Bitmap,把近期最少使用的數據從緩存中移除,保留使用最頻繁的數據, inBitMap高級特性:利用inBitmap的高級特性提高Android系統在Bitmap分配與釋放執行效率。使用inBitmap屬性可以告知Bitmap解碼器去嘗試使用已經存在的內存區域,新解碼的Bitmap會嘗試去使用之前那張Bitmap在Heap中所占據的pixel data內存區域,而不是去問內存重新申請一塊區域來存放Bitmap。利用這種特性,即使是上千張的圖片,也只會僅僅只需要占用屏幕所能夠顯示的圖片數量的內存大小
使用更小的圖片 在涉及給到資源圖片時,我們需要特別留意這張圖片是否存在可以壓縮的空間,是否可以使用更小的圖片。盡量使用更小的圖片不僅可以減少內存的使用,還能避免出現大量的InflationException。假設有一張很大的圖片被XML文件直接引用,很有可能在初始化視圖時會因為內存不足而發生InflationException,這個問題的根本原因其實是發生了OOM。
StringBuilder 在有些時候,代碼中會需要使用到大量的字符串拼接的操作,這種時候有必要考慮使用StringBuilder來替代頻繁的“+”。避免在onDraw方法里面執行對象的創建 類似onDraw等頻繁調用的方法,一定需要注意避免在這里做創建對象的操作,因為他會迅速增加內存的使用,而且很容易引起頻繁的gc,甚至是內存抖動。
10. Android中的動畫有哪些,區別是什么
逐幀動畫(Drawable Animation): 加載一系列Drawable資源來創建動畫,簡單來說就是播放一系列的圖片來實現動畫效果,可以自定義每張圖片的持續時間
補間動畫(Tween Animation): Tween可以對View對象實現一系列簡單的動畫效果,比如位移,縮放,旋轉,透明度等等。但是它并不會改變View屬性的值,只是改變了View的繪制的位置,比如,一個按鈕在動畫過后,不在原來的位置,但是觸發點擊事件的仍然是原來的坐標。
屬性動畫(Property Animation): 動畫的對象除了傳統的View對象,還可以是Object對象,動畫結束后,Object對象的屬性值被實實在在的改變了
11. 數據持久化的四種方式有哪些?
文件存儲: 通過java.io.FileInputStream和java.io.FileOutputStream這兩個類來實現對文件的讀寫,java.io.File類則用來構造一個具體指向某個文件或者文件夾的對象。
SharedPreferences: SharedPreferences是一種輕量級的數據存儲機制,他將一些簡單的數據類型的數據,包括boolean類型,int類型,float類型,long類型以及String類型的數據,以鍵值對的形式存儲在應用程序的私有Preferences目錄(/data/data/<包名>/shared_prefs/)中,這種Preferences機制廣泛應用于存儲應用程序中的配置信息。SQLite數據庫: 當應用程序需要處理的數據量比較大時,為了更加合理地存儲、管理、查詢數據,我們往往使用關系數據庫來存儲數據。Android系統的很多用戶數據,如聯系人信息,通話記錄,短信息等,都是存儲在SQLite數據庫當中的,所以利用操作SQLite數據庫的API可以同樣方便的訪問和修改這些數據。
ContentProvider: 主要用于在不同的應用程序之間實現數據共享的功能,不同于sharepreference和文件存儲中的兩種全局可讀寫操作模式,內容提供其可以選擇只對哪一部分數據進行共享,從而保證我們程序中的隱私數據不會有泄漏的風險
來自:http://www.jianshu.com/p/ff63bd1e4595