金三銀四java高級面試基礎

1. 說下你所知道的設計模式與使用場景

a.建造者模式:

將一個複雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示。使用場景比如最常見的AlertDialog,拿我們開發過程中舉例,比如Camera開發過程中,可能需要設置一個初始化的相機配置,設置攝像頭方向,閃光燈開閉,成像質量等等,這種場景下就可以使用建造者模式裝飾者模式:動態的給一個對象添加一些額外的職責,就增加功能來說,裝飾模式比生成子類更為靈活。裝飾者模式可以在不改變原有類結構的情況下曾強類的功能,比如Java中的BufferedInputStream 包裝FileInputStream,舉個開發中的例子,比如在我們現有網絡框架上需要增加新的功能,那麼再包裝一層即可,裝飾者模式解決了繼承存在的一些問題,比如多層繼承代碼的臃腫,使代碼邏輯更清晰

觀察者模式:

代理模式:

門面模式:

單例模式:

生產者消費者模式:

2. java語言的特點與OOP思想

這個通過對比來描述,比如面向對象和麵向過程的對比,針對這兩種思想的對比,還可以舉個開發中的例子,比如播放器的實現,面向過程的實現方式就是將播放視頻的這個功能分解成多個過程,比如,加載視頻地址,獲取視頻信息,初始化解碼器,選擇合適的解碼器進行解碼,讀取解碼後的幀進行視頻格式轉換和音頻重採樣,然後讀取幀進行播放,這是一個完整的過程,這個過程中不涉及類的概念,而面向對象最大的特點就是類,封裝繼承和多態是核心,同樣的以播放器為例,一面向對象的方式來實現,將會針對每一個功能封裝出一個對象,吧如說Muxer,獲取視頻信息,Decoder,解碼,格式轉換器,視頻播放器,音頻播放器等,每一個功能對應一個對象,由這個對象來完成對應的功能,並且遵循單一職責原則,一個對象只做它相關的事情

3. 說下java中的線程創建方式,線程池的工作原理。

java中有三種創建線程的方式,或者說四種

1.繼承Thread類實現多線程

2.實現Runnable接口

3.實現Callable接口

4.通過線程池

線程池的工作原理:線程池可以減少創建和銷燬線程的次數,從而減少系統資源的消耗,當一個任務提交到線程池時

a. 首先判斷核心線程池中的線程是否已經滿了,如果沒滿,則創建一個核心線程執行任務,否則進入下一步

b. 判斷工作隊列是否已滿,沒有滿則加入工作隊列,否則執行下一步

c. 判斷線程數是否達到了最大值,如果不是,則創建非核心線程執行任務,否則執行飽和策略,默認拋出異常

4. 說下handler原理

Handler,Message,looper和MessageQueue構成了安卓的消息機制,handler創建後可以通過sendMessage將消息加入消息隊列,然後looper不斷的將消息從MessageQueue中取出來,回調到Hander的handleMessage方法,從而實現線程的通信。

從兩種情況來說,第一在UI線程創建Handler,此時我們不需要手動開啟looper,因為在應用啟動時,在ActivityThread的main方法中就創建了一個當前主線程的looper,並開啟了消息隊列,消息隊列是一個無限循環,為什麼無限循環不會ANR?因為可以說,應用的整個生命週期就是運行在這個消息循環中的,安卓是由事件驅動的,Looper.loop不斷的接收處理事件,每一個點擊觸摸或者Activity每一個生命週期都是在Looper.loop的控制之下的,looper.loop一旦結束,應用程序的生命週期也就結束了。我們可以想想什麼情況下會發生ANR,第一,事件沒有得到處理,第二,事件正在處理,但是沒有及時完成,而對事件進行處理的就是looper,所以只能說事件的處理如果阻塞會導致ANR,而不能說looper的無限循環會ANR

另一種情況就是在子線程創建Handler,此時由於這個線程中沒有默認開啟的消息隊列,所以我們需要手動調用looper.prepare(),並通過looper.loop開啟消息

主線程Looper從消息隊列讀取消息,當讀完所有消息時,主線程阻塞。子線程往消息隊列發送消息,並且往管道文件寫數據,主線程即被喚醒,從管道文件讀取數據,主線程被喚醒只是為了讀取消息,當消息讀取完畢,再次睡眠。因此loop的循環並不會對CPU性能有過多的消耗。

5. 內存洩漏的場景和解決辦法

1.非靜態內部類的靜態實例

非靜態內部類會持有外部類的引用,如果非靜態內部類的實例是靜態的,就會長期的維持著外部類的引用,組織被系統回收,解決辦法是使用靜態內部類

2.多線程相關的匿名內部類和非靜態內部類

匿名內部類同樣會持有外部類的引用,如果在線程中執行耗時操作就有可能發生內存洩漏,導致外部類無法被回收,直到耗時任務結束,解決辦法是在頁面退出時結束線程中的任務

3.Handler內存洩漏

Handler導致的內存洩漏也可以被歸納為非靜態內部類導致的,Handler內部message是被存儲在MessageQueue中的,有些message不能馬上被處理,存在的時間會很長,導致handler無法被回收,如果handler是非靜態的,就會導致它的外部類無法被回收,解決辦法是1.使用靜態handler,外部類引用使用弱引用處理2.在退出頁面時移除消息隊列中的消息

4.Context導致內存洩漏

根據場景確定使用Activity的Context還是Application的Context,因為二者生命週期不同,對於不必須使用Activity的Context的場景(Dialog),一律採用Application的Context,單例模式是最常見的發生此洩漏的場景,比如傳入一個Activity的Context被靜態類引用,導致無法回收

5.靜態View導致洩漏

使用靜態View可以避免每次啟動Activity都去讀取並渲染View,但是靜態View會持有Activity的引用,導致無法回收,解決辦法是在Activity銷燬的時候將靜態View設置為null(View一旦被加載到界面中將會持有一個Context對象的引用,在這個例子中,這個context對象是我們的Activity,聲明一個靜態變量引用這個View,也就引用了activity)

6.WebView導致的內存洩漏

WebView只要使用一次,內存就不會被釋放,所以WebView都存在內存洩漏的問題,通常的解決辦法是為WebView單開一個進程,使用AIDL進行通信,根據業務需求在合適的時機釋放掉

7.資源對象未關閉導致

如Cursor,File等,內部往往都使用了緩衝,會造成內存洩漏,一定要確保關閉它並將引用置為null

8.集合中的對象未清理

集合用於保存對象,如果集合越來越大,不進行合理的清理,尤其是入股集合是靜態的

9.Bitmap導致內存洩漏

bitmap是比較佔內存的,所以一定要在不使用的時候及時進行清理,避免靜態變量持有大的bitmap對象

10.監聽器未關閉

很多需要register和unregister的系統服務要在合適的時候進行unregister,手動添加的listener也需要及時移除

6. 如何避免OOM?

1.使用更加輕量的數據結構:如使用ArrayMap/SparseArray替代HashMap,HashMap更耗內存,因為它需要額外的實例對象來記錄Mapping操作,SparseArray更加高效,因為它避免了Key Value的自動裝箱,和裝箱後的解箱操作

2.便面枚舉的使用,可以用靜態常量或者註解@IntDef替代

3.Bitmap優化:

a.尺寸壓縮:通過InSampleSize設置合適的縮放

b.顏色質量:設置合適的format,ARGB_6666/RBG_545/ARGB_4444/ALPHA_6,存在很大差異

c.inBitmap:使用inBitmap屬性可以告知Bitmap解碼器去嘗試使用已經存在的內存區域,新解碼的Bitmap會嘗試去使用之前那張Bitmap在Heap中所佔據的pixel data內存區域,而不是去問內存重新申請一塊區域來存放Bitmap。利用這種特性,即使是上千張的圖片,也只會僅僅只需要佔用屏幕所能夠顯示的圖片數量的內存大小,但複用存在一些限制,具體體現在:在Android 4.4之前只能重用相同大小的Bitmap的內存,而Android 4.4及以後版本則只要後來的Bitmap比之前的小即可。使用inBitmap參數前,每創建一個Bitmap對象都會分配一塊內存供其使用,而使用了inBitmap參數後,多個Bitmap可以複用一塊內存,這樣可以提高性能

4.StringBuilder替代String: 在有些時候,代碼中會需要使用到大量的字符串拼接的操作,這種時候有必要考慮使用StringBuilder來替代頻繁的“+”

5.避免在類似onDraw這樣的方法中創建對象,因為它會迅速佔用大量內存,引起頻繁的GC甚至內存抖動

6.減少內存洩漏也是一種避免OOM的方法

7. 說下Activity的啟動模式,生命週期,兩個Activity跳轉的生命週期,如果一個Activity跳轉另一個Activity再按下Home鍵在回到Activity的生命週期是什麼樣的

啟動模式

Standard模式:Activity可以有多個實例,每次啟動Activity,無論任務棧中是否已經有這個Activity的實例,系統都會創建一個新的Activity實例

SingleTop模式:當一個singleTop模式的Activity已經位於任務棧的棧頂,再去啟動它時,不會再創建新的實例,如果不位於棧頂,就會創建新的實例

SingleTask模式:如果Activity已經位於棧頂,系統不會創建新的Activity實例,和singleTop模式一樣。但Activity已經存在但不位於棧頂時,系統就會把該Activity移到棧頂,並把它上面的activity出棧

SingleInstance模式:singleInstance模式也是單例的,但和singleTask不同,singleTask只是任務棧內單例,系統裡是可以有多個singleTask Activity實例的,而singleInstance Activity在整個系統裡只有一個實例,啟動一singleInstanceActivity時,系統會創建一個新的任務棧,並且這個任務棧只有他一個Activity

生命週期

onCreate onStart onResume onPause onStop onDestroy

兩個Activity跳轉的生命週期

1.啟動A

onCreate - onStart - onResume

2.在A中啟動B

ActivityA onPause

ActivityB onCreate

ActivityB onStart

ActivityB onResume

ActivityA onStop

3.從B中返回A(按物理硬件返回鍵)

ActivityB onPause

ActivityA onRestart

ActivityA onStart

ActivityA onResume

ActivityB onStop

ActivityB onDestroy

4.繼續返回

ActivityA onPause

ActivityA onStop

ActivityA onDestroy

由於篇幅限制只能分享部分出來,需要獲取完整電子版讀者朋友們轉發分享此文,私信本人:【學習】獲取!(感謝各位讀者朋友!!!)


分享到:


相關文章: