android 面试汇总二
- Animation
Q:Android中有哪幾種類型的動畫?
- 技術點:動畫類型
- 參考回答: 常見三類動畫
- View動畫(View Animation)/補間動畫(Tween animation):對View進行平移、縮放、旋轉和透明度變化的動畫,不能真正的改變view的位置。應用如布局動畫、Activity切換動畫
- 逐幀動畫(Drawable Animation):是View動畫的一種,它會按照順序播放一組預先定義好的圖片
- 屬性動畫(Property Animation):對該類對象進行動畫操作,真正改變了對象的屬性
Q:幀動畫在使用時需要注意什么?
- 技術點:幀動畫
- 參考回答:使用禎動畫要注意不能使用尺寸過大的圖片,否則容易造成OOM
Q:View動畫和屬性動畫的區(qū)別?
- 技術點:View動畫、屬性動畫
- 參考回答:
Q:View動畫為何不能真正改變View的位置?而屬性動畫為何可以?
- 技術點:View動畫
- 參考回答:View動畫改變的只是View的顯示,而沒有改變View的響應區(qū)域;而屬性動畫會通過反射技術來獲取和執(zhí)行屬性的get、set方法,從而改變了對象位置的屬性值。
Q:屬性動畫插值器和估值器的作用?
- 技術點:屬性動畫
- 參考回答:
- 插值器(Interpolator):根據(jù)時間流逝的百分比計算出當前屬性值改變的百分比。確定了動畫效果變化的模式,如勻速變化、加速變化等等。View動畫和屬性動畫均可使用。常用的系統(tǒng)內(nèi)置插值器:
- 線性插值器(LinearInterpolator):勻速動畫
- 加速減速插值器(AccelerateDecelerateInterpolator):動畫兩頭慢中間快
- 減速插值器(DecelerateInterpolator):動畫越來越慢
- 類型估值器(TypeEvaluator):根據(jù)當前屬性改變的百分比計算出改變后的屬性值。針對于屬性動畫,View動畫不需要類型估值器。常用的系統(tǒng)內(nèi)置的估值器:
- 整形估值器(IntEvaluator)
- 浮點型估值器(FloatEvaluator)
- Color屬性估值器(ArgbEvaluator)
- 插值器(Interpolator):根據(jù)時間流逝的百分比計算出當前屬性值改變的百分比。確定了動畫效果變化的模式,如勻速變化、加速變化等等。View動畫和屬性動畫均可使用。常用的系統(tǒng)內(nèi)置插值器:
- Window
Q:Activity、View、Window三者之間的關系?
- 技術點:Activity、View、Window聯(lián)系
- 思路:圍繞Window是Activity和View的橋梁展開
- 參考回答:在Activity啟動過程其中的attach()方法中初始化了PhoneWindow,而PhoneWindow是Window的唯一實現(xiàn)類,然后Activity通過setContentView將View設置到了PhoneWindow上,而View通過WindowManager的addView()、removeView()、updateViewLayout()對View進行管理。
Q:Window有哪幾種類型?
- 技術點:Window類型
- 參考回答:Window有三種類型:
- 應用Window:對應一個Activity。
- 子Window:不能單獨存在,需附屬特定的父Window。如Dialog。
- 系統(tǒng)Window: 需申明權限才能創(chuàng)建。如Toast。
Q:Activity創(chuàng)建和Dialog創(chuàng)建過程的異同?
- 技術點:Window創(chuàng)建
- 參考回答:Dialog的Window創(chuàng)建過程:
- 創(chuàng)建WindowDialog。和Activity類似,同樣是通過PolicyManager.makeNewWindow() 來實現(xiàn)。
- 初始化DecorView并將Dialog的視圖添加到DecorView中去。和Activity類似,同樣是通過Window.setContentView() 來實現(xiàn)。
- 將DecorView添加到Window中顯示。和Activity一樣,都是在自身要出現(xiàn)在前臺時才會將添加Window。
- Dialog.show() 方法:完成DecorView的顯示。
- WindowManager.remoteViewImmediate() 方法:當Dialog被dismiss時移除DecorView。
- Handler
Q:談談消息機制Hander?作用?有哪些要素?流程是怎樣的?
- 技術點:消息機制
- 參考回答:
- 作用:跨線程通信。當子線程中進行耗時操作后需要更新UI時,通過Handler將有關UI的操作切換到主線程中執(zhí)行。
- 四要素:
- Message(消息):需要被傳遞的消息,其中包含了消息ID,消息處理對象以及處理的數(shù)據(jù)等,由MessageQueue統(tǒng)一列隊,最終由Handler處理。
- MessageQueue(消息隊列):用來存放Handler發(fā)送過來的消息,內(nèi)部通過單鏈表的數(shù)據(jù)結構來維護消息列表,等待Looper的抽取。
- Handler(處理者):負責Message的發(fā)送及處理。通過 Handler.sendMessage() 向消息池發(fā)送各種消息事件;通過 Handler.handleMessage() 處理相應的消息事件。
- Looper(消息泵):通過Looper.loop()不斷地從MessageQueue中抽取Message,按分發(fā)機制將消息分發(fā)給目標處理者。
-
具體流程如圖
- Handler.sendMessage()發(fā)送消息時,會通過MessageQueue.enqueueMessage()向MessageQueue中添加一條消息;
- 通過Looper.loop()開啟循環(huán)后,不斷輪詢調(diào)用MessageQueue.next();
- 調(diào)用目標Handler.dispatchMessage()去傳遞消息,目標Handler收到消息后調(diào)用Handler.handlerMessage()處理消息。
Q:為什么系統(tǒng)不建議在子線程訪問UI?
- 技術點:UI線程、子線程
- 參考回答:系統(tǒng)不建議在子線程訪問UI的原因是,UI控件非線程安全,在多線程中并發(fā)訪問可能會導致UI控件處于不可預期的狀態(tài)。而不對UI控件的訪問加上鎖機制的原因有:
- 上鎖會讓UI控件變得復雜和低效
- 上鎖后會阻塞某些進程的執(zhí)行
Q:一個Thread可以有幾個Looper?幾個Handler?
- 技術點:Looper、Handler
- 參考回答:一個Thread只能有一個Looper,可以有多個Handler
- 引申:更多數(shù)量關系:Looper有一個MessageQueue,可以處理來自多個Handler的Message;MessageQueue有一組待處理的Message,這些Message可來自不同的Handler;Message中記錄了負責發(fā)送和處理消息的Handler;Handler中有Looper和MessageQueue;
Q:如何將一個Thread線程變成Looper線程?Looper線程有哪些特點?
- 技術點:Looper
- 參考回答:通過Looper.prepare()可將一個Thread線程轉換成Looper線程。Looper線程和普通Thread不同,它通過MessageQueue來存放消息和事件、Looper.loop()進行消息輪詢。
Q:可以在子線程直接new一個Handler嗎?那該怎么做?
- 技術點:Handler
- 參考回答:不同于主線程直接new一個Handler,由于子線程的Looper需要手動去創(chuàng)建,在創(chuàng)建Handler時需要多一些方法:
Q:Message可以如何創(chuàng)建?哪種效果更好,為什么?
- 技術點:Message
- 參考回答:創(chuàng)建Message對象的幾種方式:
- Message msg = new Message();
- Message msg = Message.obtain();
- Message msg = handler1.obtainMessage();
后兩種方法都是從整個Messge池中返回一個新的Message實例,能有效避免重復Message創(chuàng)建對象,因此更鼓勵這種方式創(chuàng)建Message
Q:這里的ThreadLocal有什么作用?
- 技術點:ThreadLocal
- 參考回答:ThreadLocal類可實現(xiàn)線程本地存儲的功能,把共享數(shù)據(jù)的可見范圍限制在同一個線程之內(nèi),無須同步就能保證線程之間不出現(xiàn)數(shù)據(jù)爭用的問題,這里可理解為ThreadLocal幫助Handler找到本線程的Looper。
- 底層數(shù)據(jù)結構:每個線程的Thread對象中都有一個ThreadLocalMap對象,它存儲了一組以ThreadLocal.threadLocalHashCode為key、以本地線程變量為value的鍵值對,而ThreadLocal對象就是當前線程的ThreadLocalMap的訪問入口,也就包含了一個獨一無二的threadLocalHashCode值,通過這個值就可以在線程鍵值值對中找回對應的本地線程變量。
Q:主線程中Looper的輪詢死循環(huán)為何沒有阻塞主線程?
- 技術點:Looper
- 參考回答:Android是依靠事件驅動的,通過Loop.loop()不斷進行消息循環(huán),可以說Activity的生命周期都是運行在 Looper.loop()的控制之下,一旦退出消息循環(huán),應用也就退出了。而所謂的導致ANR多是因為某個事件在主線程中處理時間太耗時,因此只能說是對某個消息的處理阻塞了Looper.loop(),反之則不然。
Q:使用Hanlder的postDealy()后消息隊列會發(fā)生什么變化?
- 技術點:Handler
- 參考回答:post delay的Message并不是先等待一定時間再放入到MessageQueue中,而是直接進入并阻塞當前線程,然后將其delay的時間和隊頭的進行比較,按照觸發(fā)時間進行排序,如果觸發(fā)時間更近則放入隊頭,保證隊頭的時間最小、隊尾的時間最大。此時,如果隊頭的Message正是被delay的,則將當前線程堵塞一段時間,直到等待足夠時間再喚醒執(zhí)行該Message,否則喚醒后直接執(zhí)行。
- 線程
Q:Android中還了解哪些方便線程切換的類?
- 技術點:線程通信
- 參考回答:對Handler進一步的封裝的幾個類:
- AsyncTask:底層封裝了線程池和Handler,便于執(zhí)行后臺任務以及在子線程中進行UI操作。
- HandlerThread:一種具有消息循環(huán)的線程,其內(nèi)部可使用Handler。
- IntentService:是一種異步、會自動停止的服務,內(nèi)部采用HandlerThread。
- 引申:更多是對消息機制的理解
Q:AsyncTask相比Handler有什么優(yōu)點?不足呢?
- 技術點:AsyncTask、Handler
- 參考回答:
- Handler機制存在的問題:多任務同時執(zhí)行時不易精確控制線程。
- 引入AsyncTask的好處:創(chuàng)建異步任務更簡單,直接繼承它可方便實現(xiàn)后臺異步任務的執(zhí)行和進度的回調(diào)更新UI,而無需編寫任務線程和Handler實例就能完成相同的任務。
Q:使用AsyncTask需要注意什么?
- 技術點:AsyncTask
- 參考回答:
- 不要直接調(diào)用onPreExecute()、doInBackground()、onProgressUpdate()、onPostExecute()和onCancelled()方法
- 一個異步對象只能調(diào)用一次execute()方法
- 引申:談談AsyncTask初始化、五個核心方法如何配合進而體現(xiàn)Handler的作用
Q:AsyncTask中使用的線程池大小?
- 技術點:AsyncTask
- 參考回答:在AsyncTask內(nèi)部實現(xiàn)有兩個線程池:
- SerialExecutor:用于任務的排隊,默認是串行的線程池,在3.0以前核心線程數(shù)為5、線程池大小為128,而3.0以后變?yōu)橥粫r間只能處理一個任務
- THREAD_POOL_EXECUTOR:用于真正執(zhí)行任務。
- 引申:談談對線程池的理解
Q:HandlerThread有什么特點?
- 技術點:HandlerThread
- 參考回答:HandlerThread是一個線程類,它繼承自Thread。與普通Thread不同,HandlerThread具有消息循環(huán)的效果,這是因為它內(nèi)部HandlerThread.run()方法中有Looper,能通過Looper.prepare()來創(chuàng)建消息隊列,并通過Looper.loop()來開啟消息循環(huán)。
Q:快速實現(xiàn)子線程使用Handler
- 技術點:HandlerThread
- 思路:不同于之前手動在子線程創(chuàng)建Looper再構建Handler的想法,這里從HandlerThread角度去快速實現(xiàn)在子線程使用Handler
- 參考回答:HandlerThread實現(xiàn)方法
- 實例化一個HandlerThread對象,參數(shù)是該線程的名稱;
- 通過 HandlerThread.start()開啟線程;
- 實例化一個Handler并傳入HandlerThread中的looper對象,使得與HandlerThread綁定;
- 利用Handler即可執(zhí)行異步任務;
- 當不需要HandlerThread時,通過HandlerThread.quit()/quitSafely()方法來終止線程的執(zhí)行。
Q:IntentService的特點?
- 技術點:IntentService
- 思路:和普通線程和普通Service比較突出其特點
- 參考回答: 不同于線程,IntentService是服務,優(yōu)先級比線程高,更不容易被系統(tǒng)殺死,因此較適合執(zhí)行一些高優(yōu)先級的后臺任務;不同于普通Service,IntentService可自動創(chuàng)建子線程來執(zhí)行任務,且任務執(zhí)行完畢后自動退出。
Q:為何不用bindService方式創(chuàng)建IntentService?
- 技術點:IntentService
- 思路:從底層實現(xiàn)出發(fā)
- 參考回答:IntentService的工作原理是,在IntentService的onCreate()里會創(chuàng)建一個HandlerThread,并利用其內(nèi)部的Looper實例化一個ServiceHandler對象;而這個ServiceHandler用于處理消息的handleMessage()方法會去調(diào)用IntentService的onHandleIntent(),這也是為什么可在該方法中處理后臺任務的邏輯;當有Intent任務請求時會把Intent封裝到Message,然后ServiceHandler會把消息發(fā)送出,而發(fā)送消息是在onStartCommand()完成的,只能通過startService()才可走該生命周期方法,因此不能通過bindService創(chuàng)建IntentService。
Q:線程池的好處、原理、類型?
- 技術點:線程池
- 參考回答:
- (1)線程池的好處:
- 重用線程池中的線程,避免線程的創(chuàng)建和銷毀帶來的性能消耗;
- 有效控制線程池的最大并發(fā)數(shù),避免大量的線程之間因互相搶占系統(tǒng)資源而導致阻塞現(xiàn)象;
- 進行線程管理,提供定時/循環(huán)間隔執(zhí)行等功能
- (2)線程池的分類:
- FixThreadPool:線程數(shù)量固定的線程池,所有線程都是核心線程,當線程空閑時不會被回收;能快速響應外界請求。
- CachedThreadPool:線程數(shù)量不定的線程池(最大線程數(shù)為Integer.MAX_VALUE),只有非核心線程,空閑線程有超時機制,超時回收;適合于執(zhí)行大量的耗時較少的任務
- ScheduledThreadPool:核心線程數(shù)量固定,非核心線程數(shù)量不定;可進行定時任務和固定周期的任務。
- SingleThreadExecutor:只有一個核心線程,可確保所有的任務都在同一個線程中按順序執(zhí)行;好處是無需處理線程同步問題。
- (3)線程池的原理:實際上通過ThreadPoolExecutor并通過一系列參數(shù)來配置各種各樣的線程池,具體的參數(shù)有:
- corePoolSize核心線程數(shù):一般會在線程中一直存活
- maximumPoolSize最大線程數(shù):當活動線程數(shù)達到這個數(shù)值后,后續(xù)的任務將會被阻塞
- keepAliveTime非核心線程超時時間:超過這個時長,閑置的非核心線程就會被回收
- unit:用于指定keepAliveTime參數(shù)的時間單位
- workQueue任務隊列:通過線程池的execute()方法提交的Runnable對象會存儲在這個參數(shù)中。
- threadFactory:線程工廠,可創(chuàng)建新線程
- handler:在線程池無法執(zhí)行新任務時進行調(diào)度
- 引申:使用Executors各個方法創(chuàng)建線程池的弊端
Q:ThreadPoolExecutor的工作策略?
- 技術點:線程池
- 參考回答:ThreadPoolExecutor的默認工作策略:
- 若程池中的線程數(shù)量未達到核心線程數(shù),則會直接啟動一個核心線程執(zhí)行任務。
- 若線程池中的線程數(shù)量已達到或者超過核心線程數(shù)量,則任務會被插入到任務列表等待執(zhí)行。
- 若任務無法插入到任務列表中,往往由于任務列表已滿,此時如果
- 線程數(shù)量未達到線程池最大線程數(shù),則會啟動一個非核心線程執(zhí)行任務;
- 線程數(shù)量已達到線程池規(guī)定的最大值,則拒絕執(zhí)行此任務,ThreadPoolExecutor會調(diào)用RejectedExecutionHandler的rejectedExecution方法來通知調(diào)用者。
- 若任務無法插入到任務列表中,往往由于任務列表已滿,此時如果
- 引申:ThreadPoolExecutor的拒絕策略
Q:什么是ANR?什么情況會出現(xiàn)ANR?如何避免?在不看代碼的情況下如何快速定位出現(xiàn)ANR問題所在?
- 技術點:ANR
- 思路:
- 參考回答:
- ANR(Application Not Responding,應用無響應):當操作在一段時間內(nèi)系統(tǒng)無法處理時,會在系統(tǒng)層面會彈出ANR對話框
- 產(chǎn)生ANR可能是因為5s內(nèi)無響應用戶輸入事件、10s內(nèi)未結束BroadcastReceiver、20s內(nèi)未結束Service
- 想要避免ANR就不要在主線程做耗時操作,而是通過開子線程,方法比如繼承Thread或實現(xiàn)Runnable接口、使用AsyncTask、IntentService、HandlerThread等
- 引申:快讀定位ANR方法:使用命令導出ANR日志,并分析關鍵信息,詳見如何分析ANR
- Bitmap
Q:加載圖片的時候需要注意什么?
- 技術點:Bitmap高效加載
- 參考回答:
- 直接加載大容量的高清Bitmap很容易出現(xiàn)顯示不完整、內(nèi)存溢出OOM的問題,所以最好按一定的采樣率將圖片縮小后再加載進來
- 為減少流量消耗,可對圖片采用內(nèi)存緩存策略,又為了避免圖片占用過多內(nèi)存導致內(nèi)存溢出,最好以軟引用方式持有圖片
- 如果還需要網(wǎng)上下載圖片,注意要開子線程去做下載的耗時操作
Q:LRU算法的原理?
- 技術點:LRU算法
- 參考回答:為減少流量消耗,可采用緩存策略。常用的緩存算法是LRU(Least Recently Used):
- 核心思想:當緩存滿時, 會優(yōu)先淘汰那些近期最少使用的緩存對象。主要是兩種方式:
- LruCache(內(nèi)存緩存):LruCache類是一個線程安全的泛型類:內(nèi)部采用一個LinkedHashMap以強引用的方式存儲外界的緩存對象,并提供get和put方法來完成緩存的獲取和添加操作,當緩存滿時會移除較早使用的緩存對象,再添加新的緩存對象。
- DiskLruCache(磁盤緩存): 通過將緩存對象寫入文件系統(tǒng)從而實現(xiàn)緩存效果
- 核心思想:當緩存滿時, 會優(yōu)先淘汰那些近期最少使用的緩存對象。主要是兩種方式:
- 引申:感興趣可了解具體實現(xiàn)算法
- 性能優(yōu)化
Q:項目中如何做性能優(yōu)化的?
- 技術點:性能優(yōu)化實例
- 思路:舉例說明項目注意了哪些方面的性能優(yōu)化,如布局優(yōu)化、繪制優(yōu)化、內(nèi)存泄漏優(yōu)化、 響應速度優(yōu)化、列表優(yōu)化、Bitmap優(yōu)化、 線程優(yōu)化......
Q:了解哪些性能優(yōu)化的工具?
- 技術點:性能優(yōu)化工具
- 思路:做項目時是否使用過的系統(tǒng)自帶的性能優(yōu)化工具?公司是否有自己的性能優(yōu)化工具?實現(xiàn)原理怎樣的?
Q:布局上如何優(yōu)化?
- 技術點:布局優(yōu)化
- 參考回答:布局優(yōu)化的核心就是盡量減少布局文件的層級,常見的方式有:
- 多嵌套情況下可使用RelativeLayout減少嵌套。
- 布局層級相同的情況下使用LinearLayout,它比RelativeLayout更高效。
- 使用<include>標簽重用布局、<merge>標簽減少層級、<ViewStub>標簽懶加載。
Q:內(nèi)存泄漏是什么?為什么會發(fā)生?常見哪些內(nèi)存泄漏的例子?都是怎么解決的?
- 技術點:內(nèi)存泄漏
- 參考回答:內(nèi)存泄漏(Memory Leak)是指程序在申請內(nèi)存后,無法釋放已申請的內(nèi)存空間。簡單地說,發(fā)生內(nèi)存泄漏是由于長周期對象持有對短周期對象的引用,使得短周期對象不能被及時回收。常見的幾個例子和解決辦法:
- 單例模式導致的內(nèi)存泄漏:單例傳入?yún)?shù)this來自Activity,使得持有對Activity的引用。
- 解決辦法:傳參context.getApplicationContext()
- Handler導致的內(nèi)存泄漏:Message持有對Handler的引用,而非靜態(tài)內(nèi)部類的Handler又隱式持有對外部類Activity的引用,使得引用關系會保持至消息得到處理,從而阻止了Activity的回收。
- 解決辦法:使用靜態(tài)內(nèi)部類+WeakReference弱引用;當外部類結束生命周期時清空消息隊列。
- 線程導致的內(nèi)存泄漏:AsyncTask/Runnable以匿名內(nèi)部類的方式存在,會隱式持有對所在Activity的引用。
- 解決辦法:將AsyncTask和Runnable設為靜態(tài)內(nèi)部類或獨立出來;在線程內(nèi)部采用弱引用保存Context引用
- 資源未關閉導致的內(nèi)存泄漏:未及時注銷資源導致內(nèi)存泄漏,如BraodcastReceiver、File、Cursor、Stream、Bitmap等。
- 解決辦法:在Activity銷毀的時候要及時關閉或者注銷。
- BraodcastReceiver:調(diào)用unregisterReceiver()注銷;
- Cursor,Stream、File:調(diào)用close()關閉;
- 動畫:在Activity.onDestroy()中調(diào)用Animator.cancel()停止動畫
- 解決辦法:在Activity銷毀的時候要及時關閉或者注銷。
- 單例模式導致的內(nèi)存泄漏:單例傳入?yún)?shù)this來自Activity,使得持有對Activity的引用。
- 引申:談談項目中是如何注意內(nèi)存泄漏的問題
Q:內(nèi)存泄漏和內(nèi)存溢出的區(qū)別
- 技術點:內(nèi)存泄漏、內(nèi)存溢出
- 參考回答:
- 內(nèi)存泄漏(Memory Leak)是指程序在申請內(nèi)存后,無法釋放已申請的內(nèi)存空間。是造成應用程序OOM的主要原因之一。
- 內(nèi)存溢出(out of memory)是指程序在申請內(nèi)存時,沒有足夠的內(nèi)存空間供其使用。
Q:什么情況會導致內(nèi)存溢出?
- 技術點:內(nèi)存溢出
- 參考回答:內(nèi)存泄漏是導致內(nèi)存溢出的主要原因;直接加載大圖片也易造成內(nèi)存溢出
- 引申:談談如何避免內(nèi)存溢出(如何避免內(nèi)存泄漏、避免直接加載大圖片)
-
開源框架(略)
-
谷歌新動態(tài)
Q:是否了解和使用過谷歌推出的新技術?
Q:有了解剛發(fā)布的Androidx.0的特性嗎?
Q:Kotlin對Java做了哪些優(yōu)化?
- 可能意圖:了解候選者對谷歌&安卓的關注度、共同探討對新技術的看法、學習主動性、平時學習習慣
- 思路:谷歌的安卓官方網(wǎng)站(中文版):https://developer.android.google.cn,了解最新動態(tài)
1.2 Java
- 基礎
Q:面向對象編程的四大特性及其含義?
- 技術點:面向對象編程特點
- 思路:分條簡述每個特性的含義
- 參考回答:
- 抽象:對現(xiàn)實世界的事物進行概括,抽象為在計算機虛擬世界中有意義的實體
- 封裝:將某事物的屬性和行為包裝到對象中,構成一個不可分割的獨立實體,數(shù)據(jù)被保護在抽象數(shù)據(jù)類型的內(nèi)部,并且盡可能地隱藏內(nèi)部的細節(jié),只保留一些對外接口使之與外部發(fā)生聯(lián)系
- 繼承:子類繼承父類,不僅可以有父類原有的方法和屬性,也可以增加自己的或者重寫父類的方法及屬性
- 多態(tài):允許不同類的對象對同一消息做出各自的響應
Q:String、StringBuffer和StringBuilder的區(qū)別?
- 技術點:String
- 參考回答:
- String是字符串常量,而StringBuffer、StringBuilder都是字符串變量,即String對象一創(chuàng)建后不可更改,而后兩者的對象是可更改的:
- StringBuffer是線程安全的,而StringBuilder是非線程安全的,這是由于StringBuffer對方法加了同步鎖或者對調(diào)用的方法加了同步鎖
- String更適用于少量的字符串操作的情況,StringBuilder適用于單線程下在字符緩沖區(qū)進行大量操作的情況,StringBuffer適用于多線程下在字符緩沖區(qū)進行大量操作的情況
Q:String a=""和String a=new String("")的的關系和異同?
- 技術點:String
- 參考回答:
- 通過String a=""直接賦值的方式得到的是一個字符串常量,存在于常量池;注意,相同內(nèi)容的字符串在常量池中只有一個,即如果池已包含內(nèi)容相等的字符串會返回池中的字符串,反之會將該字符串放入池中
- 通過new String("")創(chuàng)建的字符串不是常量是實例對象,會在堆內(nèi)存開辟空間并存放數(shù)據(jù),且每個實例對象都有自己的地址空間
- 引申:對于用String a=""和String a=new String("")兩種方式定義的字符串,判斷使用equals()、"=="比較結果是什么
Q:Object的equal()和==的區(qū)別?
- 技術點:equal()、==
- 參考回答:
- equals():是Object的公有方法,具體含義取決于如何重寫,比如String的equals()比較的是兩個字符串的內(nèi)容是否相同
- "==" :對于基本數(shù)據(jù)類型來說,比較的是兩個變量值是夠是否相等,對于引用類型來說,比較的是兩個對象的內(nèi)存地址是否相同
- 引申:對于用String a=""和String a=new String("")兩種方式定義的字符串,判斷使用equals()、"=="比較結果是什么
Q:裝箱、拆箱什么含義?
- 技術點:裝箱、拆箱
- 參考回答:裝箱就是自動將基本數(shù)據(jù)類型轉換為包裝器類型,拆箱就是自動將包裝器類型轉換為基本數(shù)據(jù)類型
Q:int和Integer的區(qū)別?
- 技術點:基本數(shù)據(jù)類型、引用類型
- 參考回答:
- Integer是int的包裝類,int則是java的一種基本數(shù)據(jù)類型
- Integer變量必須實例化后才能使用,而int變量不需要
- Integer實際是對象的引用,當new一個Integer時,實際上是生成一個指針指向此對象;而int則是直接存儲數(shù)據(jù)值
- Integer的默認值是null,int的默認值是0
Q:遇見過哪些運行時異常?異常處理機制知道哪些?
- 技術點:Java異常機制
- 思路:對Throwable異常進行分類說明每種異常的特點和常見問題,簡述幾種常見異常處理機制,詳見Java基礎之異常機制
- 參考回答:
- (1) Throwable繼承層次結構,可見分成兩大類Error和Exception:
- Error(錯誤):指程序無法恢復的異常情況,表示運行應用程序中較嚴重的問題;發(fā)生于虛擬機自身、或者在虛擬機試圖執(zhí)行應用時,如Virtual MachineError(Java虛擬機運行錯誤)、NoClassDefFoundError(類定義錯誤);屬于不可查異常,即不強制程序員必須處理,即使不處理也不會出現(xiàn)語法錯誤。
- Exception(異常):指程序有可能恢復的異常情況,表示程序本身可以處理的異常。又分兩大類:
- RuntimeException(運行時異常):由程序自身的問題導致產(chǎn)生的異常;如NullPointerException(空指針異常)、IndexOutOfBoundsException(下標越界異常);屬于不可查異常。
- 非運行時異常:由程序外部的問題引起的異常;除了RuntimeException以外的異常,如FileNotFoundException(文件不存在異常);屬于可查異常,即強制程序員必須進行處理,如果不進行處理則會出現(xiàn)語法錯誤。
- (2)常見的異常處理機制有:
- 捕捉異常:由系統(tǒng)自動拋出異常,即try捕獲異常->catch處理異常->finally 最終處理
- 拋出異常:在方法中將異常對象顯性地拋出,之后異常會沿著調(diào)用層次向上拋出,交由調(diào)用它的方法來處理。配合throws聲明拋出的異常和throw拋出異常
- 自定義異常:繼承Execption類或其子類
Q:什么是反射,有什么作用和應用?
- 技術點:反射
- 思路:簡述反射的定義、功能和應用,詳見Java基礎之泛型&反射
- 參考回答:
- 含義:在運行狀態(tài)中,對于任意一個類都能知道它的所有屬性和方法,對于任何一個對象都能夠調(diào)用它的任何一個方法和屬性。
- 功能:動態(tài)性,體現(xiàn)在:在運行時判斷任意一個類所具有的屬性和方法; 在運行時判斷任意一個對象所屬的類;在運行時構造任意一個類的對象;在運行時調(diào)用任意一個對象的方法;生成動態(tài)代理
- 應用:反射&泛型
- 引申:是否在項目中使用過反射機制,有什么優(yōu)缺點
Q:什么是內(nèi)部類?有什么作用?靜態(tài)內(nèi)部類和非靜態(tài)內(nèi)部類的區(qū)別?
- 技術點:內(nèi)部類
- 思路:
- 參考回答:內(nèi)部類就是定義在另外一個類里面的類。它隱藏在外部類中,封裝性更強,不允許除外部類外的其他類訪問它;但它可直接訪問外部類的成員。靜態(tài)內(nèi)部類和非靜態(tài)內(nèi)部類的區(qū)別有:
- 靜態(tài)內(nèi)部類是指被聲明為static的內(nèi)部類,可不依賴外部類實例化;而非靜態(tài)內(nèi)部類需要通過生成外部類來間接生成。
- 靜態(tài)內(nèi)部類只能訪問外部類的靜態(tài)成員變量和靜態(tài)方法,而非靜態(tài)內(nèi)部類由于持有對外部類的引用,可以訪問外部類的所用成員
- 引申:談談匿名內(nèi)部類
Q:final、finally、finalize()分別表示什么含義
- 技術點:final、finally、finalize()
- 參考回答:
- final關鍵字表示不可更改,具體體現(xiàn)在:
- final修飾的變量必須要初始化,且賦初值后不能再重新賦值
- final修飾的方法不能被子類重寫
- final修飾的類不能被繼承
- finally:和try、catch成套使用進行異常處理,無論是否捕獲或處理異常,finally塊里的語句都會被執(zhí)行,在以下4種特殊情況下,finally塊才不會被執(zhí)行:
- 在finally語句塊中發(fā)生了異常
- 在前面的代碼中用了System.exit()退出程序
- 程序所在的線程死亡
- 關閉CPU
- finalize():是Object中的方法,當垃圾回收器將回收對象從內(nèi)存中清除出去之前會調(diào)用finalize(),但此時并不代表該回收對象一定會“死亡”,還有機會“逃脫”
- final關鍵字表示不可更改,具體體現(xiàn)在:
Q:重寫和重載的區(qū)別?
- 技術點:重寫、重載
- 參考回答:重寫表示子類重寫父類的方法;重載表示有多個同名函數(shù)同時存在,區(qū)別在于有不同的參數(shù)個數(shù)或類型
- 引申:談談動態(tài)分派和靜態(tài)分派
Q:抽象類和接口的異同?
- 技術點:抽象類、接口
- 參考回答:
- 使用上的區(qū)別:一個類只能繼承一個抽象類卻可以實現(xiàn)多個接口
- 設計上的區(qū)別:接口是對行為的抽象,無需有子類的前提,是自上而下的設計理念;抽象類是對類的抽象,建立于相似子類之上,是自下而上的設計理念
Q:為什么匿名內(nèi)部類中使用局部變量要用final修飾?
- 技術點:匿名內(nèi)部類
- 參考回答:一方面,由于方法中的局部變量的生命周期很短,一旦方法結束變量就要被銷毀,為了保證在內(nèi)部類中能找到外部局部變量,通過final關鍵字可得到一個外部變量的引用;另一方面,通過final關鍵字也不會在內(nèi)部類去做修改該變量的值,保護了數(shù)據(jù)的一致性。
Q:Object有哪些公有方法?
- 技術點:Object
- 思路:列舉常見的幾個公有方法
- 參考回答:
- equals(): 和==作用相似
- hashCode():用于哈希查找,重寫了equals()一般都要重寫該方法
- getClass(): 獲取Class對象
- wait():讓當前線程進入等待狀態(tài),并釋放它所持有的鎖
- notify()¬ifyAll(): 喚醒一個(所有)正處于等待狀態(tài)的線程
- toString():轉換成字符串
- 引申:equals()和==的不同、在synchronized 同步代碼塊里wait()和notify()¬ifyAll()如何配合、hashCode()和equals()的關系、獲取Class對象還有什么方法
- 集合
Q:Java集合框架中有哪些類?都有什么特點
- 技術點:集合框架
- 思路:分條解釋每種類的特點
- 參考回答:可將Java集合框架大致可分為Set、List、Queue 和Map四種體系
- Set:代表無序、不可重復的集合,常見的類如HashSet、TreeSet
- List:代表有序、可重復的集合,常見的類如動態(tài)數(shù)組ArrayList、雙向鏈表LinkedList、可變數(shù)組Vector
- Map:代表具有映射關系的集合,常見的類如HashMap、LinkedHashMap、TreeMap
- Queue:代表一種隊列集合
Q:集合、數(shù)組、泛型的關系,并比較
- 技術點:集合、數(shù)組、泛型
- 參考回答:
- (1)集合和數(shù)組的區(qū)別:
- 數(shù)組元素可以是基本類型,也可以是對象;數(shù)組長度限定;數(shù)組只能存儲一種類型的數(shù)據(jù)元素
- 集合元素只能是對象;集合長度可變;集合可存儲不同種的數(shù)據(jù)元素
- (2)泛型相比與集合的好處在于它安全簡單。具體體現(xiàn)在提供編譯時的強類型檢查,而不用等到運行;可避免類類型強制轉換
Q:ArrayList和LinkList的區(qū)別?
- 技術點:List對比
- 參考回答:
- ArrayList的底層結構是數(shù)組,可用索引實現(xiàn)快速查找;是動態(tài)數(shù)組,相比于數(shù)組容量可實現(xiàn)動態(tài)增長
- LinkedList底層結構是鏈表,增刪速度快;是一個雙向循環(huán)鏈表,也可以被當作堆棧、隊列或雙端隊列
Q:ArrayList和Vector的區(qū)別?
- 技術點:List對比
- 參考回答:
- ArrayList非線程安全,建議在單線程中才使用ArrayList,而在多線程中可以選擇Vector或者CopyOnWriteArrayList;默認初始容量為10,每次擴容為原來的1.5倍
- Vector使用了synchronized關鍵字,是線程安全的,比ArrayList開銷更大,訪問更慢;默認初始容量為10,默認每次擴容為原來的2倍,可通過capacityIncrement屬性設置
Q:HashSet和TreeSet的區(qū)別?
- 技術點:Set對比
- 參考回答:
- HashSet不能保證元素的排列順序;使用Hash算法來存儲集合中的元素,有良好的存取和查找性能;通過equal()判斷兩個元素是否相等,并兩個元素的hashCode()返回值也相等
- TreeSet是SortedSet接口的實現(xiàn)類,根據(jù)元素實際值的大小進行排序;采用紅黑樹的數(shù)據(jù)結構來存儲集合元素;支持兩種排序方法:自然排序(默認情況)和定制排序。前者通過實現(xiàn)Comparable接口中的compareTo()比較兩個元素之間大小關系,然后按升序排列;后者通過實現(xiàn)Comparator接口中的compare()比較兩個元素之間大小關系,實現(xiàn)定制排列
Q:HashMap和Hashtable的區(qū)別?
- 技術點:Map對比
- 參考回答:
- HashMap基于AbstractMap類,實現(xiàn)了Map、Cloneable(能被克隆)、Serializable(支持序列化)接口; 非線程安全;允許存在一個為null的key和任意個為null的value;采用鏈表散列的數(shù)據(jù)結構,即數(shù)組和鏈表的結合;初始容量為16,填充因子默認為0.75,擴容時是當前容量翻倍,即2capacity
- Hashtable基于Map接口和Dictionary類;線程安全,開銷比HashMap大,如果多線程訪問一個Map對象,使用Hashtable更好;不允許使用null作為key和value;底層基于哈希表結構;初始容量為11,填充因子默認為0.75,擴容時是容量翻倍+1,即2capacity+1
Q:HashMap在put、get元素的過程?體現(xiàn)了什么數(shù)據(jù)結構?
- 技術點:HashMap
- 參考回答:
- 向Hashmap中put元素時,首先判斷key是否為空,為空則直接調(diào)用putForNullKey(),不為空則計算key的hash值得到該元素在數(shù)組中的下標值;如果數(shù)組在該位置處沒有元素,就直接保存;如果有,還要比較是否存在相同的key,存在的話就覆蓋原來key的value,否則將該元素保存在鏈頭,先保存的在鏈尾。
- 從Hashmap中get元素時,計算key的hash值找到在數(shù)組中的對應的下標值,返回該key對應的value即可,如果有沖突就遍歷該位置鏈表尋找key相同的元素并返回對應的value
- 由此可看出HashMap采用鏈表散列的數(shù)據(jù)結構,即數(shù)組和鏈表的結合,在Java8后又結合了紅黑樹,當鏈表元素超過8個將鏈表轉換為紅黑樹
Q:如何解決Hash沖突?
- 技術點:Hash沖突
- 參考回答:
- 開放定址法:常見的線性探測方式,在沖突發(fā)生時,順序查看表中下一單元,直到找出一個空單元或查遍全表
- 鏈地址法:將有沖突數(shù)組位置生出鏈表
- 建立公共溢出區(qū):將哈希表分為基本表和溢出表兩部分,和基本表發(fā)生沖突的元素一律填入溢出表
- 再哈希法:構造多個不同的哈希函數(shù),有沖突使用下一個哈希函數(shù)計算hash值
Q:如何保證HashMap線程安全?什么原理?
- 技術點:ConcurrentHashMap
- 思路:這里回答一種辦法,使用ConcurrentHashMap
- 參考回答:ConcurrentHashMap是線程安全的HashMap,它采取鎖分段技術,將數(shù)據(jù)分成一段一段的存儲,然后給每一段數(shù)據(jù)配一把鎖,當一個線程占用鎖訪問其中一個段數(shù)據(jù)的時候,其他段的數(shù)據(jù)也能被其他線程訪問。在JDK1.8中對ConcurrentHashmap做了兩個改進:
- 取消segments字段,直接采用transient volatile HashEntry<K,V>[] table保存數(shù)據(jù),將數(shù)組元素作為鎖,對每一行數(shù)據(jù)進行加鎖,可減少并發(fā)沖突的概率
- 數(shù)據(jù)結構由“數(shù)組+單向鏈表”變?yōu)椤皵?shù)組+單向鏈表+紅黑樹”,使得查詢的時間復雜度可以降低到O(logN),改進一定的性能。
- 引申:LinkHashMap線程安全的底層實現(xiàn)
Q:HashMap是有序的嗎?如何實現(xiàn)有序?
- 技術點:LinkHashMap
- 思路:這里回答一種辦法,使用LinkedHashMap
- 參考回答:HashMap是無序的,而LinkedHashMap是有序的HashMap,默認為插入順序,還可以是訪問順序,基本原理是其內(nèi)部通過Entry維護了一個雙向鏈表,負責維護Map的迭代順序
- 引申:LinkHashMap有序的底層實現(xiàn)
Q:HashMap是如何擴容的?如何避免擴容?
- 技術點:HashMap
- 參考回答:
- HashMap幾個默認值,初始容量為16、填充因子默認為0.75、擴容時容量翻倍。也就是說當HashMap中元素個數(shù)超過16*0.75=12時會把數(shù)組的大小擴展為2*16=32,然后重新計算每個元素在數(shù)組中的位置
- 由于每次擴容還需要重新計算元素Hash值,損耗性能,所以建議在使用HashMap時,最好先估算Map的大小,設置初始值,避免頻繁擴容
Q:hashcode()的作用,與equal()有什么區(qū)別?
- 技術點:Hash值
- 參考回答:hashCode()用于計算對象的Hash值,確認對象在散列存儲結構中的存儲地址。和equal()的區(qū)別:
- equals()比較兩個對象的地址值是否相等 ;hashCode()得到的是對象的存儲位置,可能不同對象會得到相同值
- 有兩個對象,若equals()相等,則hashcode()一定相等;hashcode()不等,則equals()一定不相等;hashcode()相等,equals()可能相等、可能不等
- 使用equals()比較兩個對象是否相等效率較低,最快辦法是先用hashCode()比較,如果hashCode()不相等,則這兩個對象肯定不相等;如果hashCode()相等,此時再用equal()比較,如果equal()也相等,則這兩個對象的確相等,反之
- 并發(fā)
Q:同步和非同步、阻塞和非阻塞的概念
- 技術點:同步、阻塞
- 參考回答:
- 同步和異步體現(xiàn)的是消息的通知機制:所謂同步,方法A調(diào)用方法B后必須等到方法B返回結果才能繼續(xù)后面的操作;所謂異步,方法A調(diào)用方法B后可讓方法B在調(diào)用結束后通過回調(diào)等方式通知方法A
- 阻塞和非阻塞側重于等待消息時的狀態(tài):所謂阻塞,就是在結果返回之前讓當前線程掛起;所謂非阻塞,就是在等待時可做其他事情,通過輪詢?nèi)ピ儐柺欠褚逊祷亟Y果
Q:Thread的join()有什么作用?
- 技術點:線程相關方法
- 參考回答:Thread的join()的含義是等待該線程終止,即將掛起調(diào)用線程的執(zhí)行,直到被調(diào)用的對象完成它的執(zhí)行。比如存在兩個線程t1和t2,下述代碼表示先啟動t1,直到t1的任務結束,才輪到t2啟動。
Q:線程的有哪些狀態(tài)?
- 技術點:線程狀態(tài)
- 思路:可分條解釋每種狀態(tài)的特點以及如何轉換。詳見要點提煉| 理解JVM之內(nèi)存模型&線程
- 參考回答:在任意一個時間點,一個線程只能有且只有其中的一種狀態(tài):
- 新建(New):線程創(chuàng)建后尚未啟動
- 運行(Runable):包括正在執(zhí)行(Running)和等待著CPU為它分配執(zhí)行時間(Ready)兩種
- 無限期等待(Waiting):該線程不會被分配CPU執(zhí)行時間,要等待被其他線程顯式地喚醒。以下方法會讓線程陷入無限期等待狀態(tài):
- 沒有設置Timeout參數(shù)的Object.wait()
- 沒有設置Timeout參數(shù)的Thread.join()
- LockSupport.park()
- 限期等待(Timed Waiting):該線程不會被分配CPU執(zhí)行時間,但在一定時間后會被系統(tǒng)自動喚醒。以下方法會讓線程進入限期等待狀態(tài):
- Thread.sleep()
- 設置了Timeout參數(shù)的Object.wai()
- 設置了Timeout參數(shù)的Thread.join()
- LockSupport.parkNanos()
- LockSupport.parkUntil()
- 阻塞(Blocked):線程被阻塞。和等待狀態(tài)不同的是,阻塞狀態(tài)表示在等待獲取到一個排他鎖,在另外一個線程放棄這個鎖的時候發(fā)生;而等待狀態(tài)表示在等待一段時間或者喚醒動作的發(fā)生,在程序等待進入同步區(qū)域的時候發(fā)生。
- 結束(Terminated):線程已經(jīng)結束執(zhí)行
Q:什么是線程安全?保障線程安全有哪些手段?
- 技術點:線程安全
- 思路:詳見要點提煉| 理解JVM之線程安全&鎖優(yōu)化
- 參考回答:線程安全就是當多個線程訪問一個對象時,如果不用考慮這些線程在運行時環(huán)境下的調(diào)度和交替執(zhí)行,也不需要進行額外的同步,或者在調(diào)用方進行任何其他的協(xié)調(diào)操作,調(diào)用這個對象的行為都可以獲得正確的結果,那這個對象是線程安全的。保證線程安全可從多線程三特性出發(fā):
- 原子性(Atomicity):單個或多個操作是要么全部執(zhí)行,要么都不執(zhí)行
- Lock:保證同時只有一個線程能拿到鎖,并執(zhí)行申請鎖和釋放鎖的代碼
- synchronized:對線程加獨占鎖,被它修飾的類/方法/變量只允許一個線程訪問
- 可見性(Visibility):當一個線程修改了共享變量的值,其他線程能夠立即得知這個修改
- volatile:保證新值能立即同步到主內(nèi)存,且每次使用前立即從主內(nèi)存刷新;
- synchronized:在釋放鎖之前會將工作內(nèi)存新值更新到主存中
- 有序性(Ordering):程序代碼按照指令順序執(zhí)行
- volatile: 本身就包含了禁止指令重排序的語義
- synchronized:保證一個變量在同一個時刻只允許一條線程對其進行l(wèi)ock操作,使得持有同一個鎖的兩個同步塊只能串行地進入
- 原子性(Atomicity):單個或多個操作是要么全部執(zhí)行,要么都不執(zhí)行
Q:ReentrantLock和synchronized的區(qū)別?
- 技術點:線程安全(ReentrantLock、synchronized)
- 思路:詳見要點提煉| 理解JVM之線程安全&鎖優(yōu)化
- 參考回答: ReentrantLock與synchronized的不同在于ReentrantLock:
- 等待可中斷:當持有鎖的線程長期不釋放鎖的時候,正在等待的線程可以選擇放棄等待,改為處理其他事情。
- 公平鎖:多個線程在等待同一個鎖時,必須按照申請鎖的時間順序來依次獲得鎖。而synchronized是非公平的,即在鎖被釋放時,任何一個等待鎖的線程都有機會獲得鎖。ReentrantLock默認情況下也是非公平的,但可以通過帶布爾值的構造函數(shù)改用公平鎖。
- 鎖綁定多個條件:一個ReentrantLock對象可以通過多次調(diào)用newCondition()同時綁定多個Condition對象。而在synchronized中,鎖對象wait()和notify()或notifyAl()只能實現(xiàn)一個隱含的條件,若要和多于一個的條件關聯(lián)不得不額外地添加一個鎖。
Q:synchronized和volatile的區(qū)別?
- 技術點:線程安全(synchronized、volatile)
- 參考回答:
- synchronized能保證操作的原子性,而volatile不可以,假設線程A和線程B同時讀取到變量a值,A修改a后將值更新到主內(nèi)存,同時B也修改a值會覆蓋A的修改操作
- synchronized可修飾變量、方法和類,而volatile只能修飾變量
- synchronized可能會造成線程阻塞,而volatile不會造成線程的阻塞
Q:synchronized同步代碼塊還有同步方法本質(zhì)上鎖住的是誰?為什么?
- 技術點:線程安全(synchronized)
- 參考回答:本質(zhì)上鎖住的是對象。在java虛擬機中,每個對象和類在邏輯上都和一個監(jiān)視器相關聯(lián),synchronized本質(zhì)上是對一個對象監(jiān)視器的獲取。當執(zhí)行同步代碼塊或同步方法時,執(zhí)行方法的線程必須先獲得該對象的監(jiān)視器,才能進入同步代碼塊或同步方法;而沒有獲取到的線程將會進入阻塞隊列,直到成功獲取對象監(jiān)視器的線程執(zhí)行結束并釋放鎖后,才會喚醒阻塞隊列的線程,使其重新嘗試對對象監(jiān)視器的獲取。
Q:sleep()和wait()的區(qū)別?
- 技術點:sleep()、wait()
- 參考回答:
- sleep()來自Thread類;wait()來自Object類
- sleep()用于線程控制自身流程;而wait()用于線程間通信,配合notify()/notifyAll()在同步代碼塊或同步方法里使用
- sleep()的線程不會釋放對象鎖;wait()會釋放對象鎖進入等待狀態(tài),使得其他線程能使用同步代碼塊或同步方法
- Java新動態(tài)
Q:是否了解Java1.x的特性嗎?
Q:談談對面向過程編程、面向對象編程還有面向切面編程的理解
- 可能意圖:了解候選者對Java和其他語言的關注度和看法、學習主動性、平時學習習慣
- 思路:Oracle技術網(wǎng)(Java):https://www.oracle.com/technetwork/cn/java/index.html 、開源中國:https://www.oschina.net ,了解最新動態(tài)
知識來源:minmin_1123
鏈接:https://www.jianshu.com/p/2dd855aa1938
轉載于:https://www.cnblogs.com/loaderman/p/9761069.html
總結
以上是生活随笔為你收集整理的android 面试汇总二的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【图像处理】——灰度变换心得(cv2.n
- 下一篇: 小象机器学习(邹博老师)学习笔记