阿里電話面試一些反饋

前段時間,在我參加一個活動期間,竟然接到阿里的電話面試,我又沒刷新簡歷,也沒去看機會,不知是如何有我的信息,難道之前簡歷投遞過會留存在他們系統中?當時也完全沒準備,既然是電話面試,那就既來之則安之,試試就試試。
結果當然是不好的, 很多問題都答不來,但我也不氣餒,既然自己水平有限,可以抓緊時間去學習,去彌補自己不足。我有個愛好就是喜歡收集,我在事后反思的時候,覺得應該把這些題目給記下來,回去再解答,看看自己到底哪里是有問題,哪方面不足,不足改進就是了。
事后根據回憶,對方問了以下幾個問題:

1,假設讓你設計一個圖片加載器,你會怎么設計?

ImageLoader的工作原理:在顯示圖片的時候,它會先在內存中查找,如果沒有找到,就去本地查找,如果還沒有,就開一個新的線程去下載這張圖片,下載成功會把圖片同時緩存到內存本地去。

imageloader

Freso的原理:設計一個Image Pipeline的概念,它負責先后檢查內存,磁盤文件,如果都沒有就老老實實從網絡下載圖片。

magepipeline

大致流程如下:

  1. 檢查內存緩存,如有,返回
  2. 后臺線程開始后續工作
  3. 檢查是否在未解碼內存緩存中。如有,解碼,變換,返回,然后緩存到內存緩存中。
  4. 檢查是否在磁盤緩存中,如果有,變換,返回。緩存到未解碼緩存和內存緩存中。
  5. 從網絡或者本地加載。加載完成后,解碼,變換,返回。存到各個緩存中。

2,簡單講講Activity、Window、View三者的關系?

觀察Activity源碼:Activity.attch() -> PolicyManager -> Policy -> PhoneWindow -> mLayoutInflater.inflate()&mContentParent.addView()
這只是一個簡單的跟蹤過程描述。通過跟蹤源代碼,就可以很清晰的看出他們三者的關系。
Activity是整個模型的控制單元,Window屬于承載模型,負責承載視圖,View是視圖顯示模型。

View是Android中的視圖呈現方式,但是View不能單獨存在,它必須附著在Window這個抽象的概念上面,因此有視圖的地方就有Window。那些地方有視圖呢?Android中可以提供視圖的地方有Activity,DIalog,Toast,除此之外,還有一些依托Window而實現的視圖,比如PopupWindow,Meun,他們也是視圖,有視圖的地方就有Window,因此Activity,Dialog,Toast等視圖都對應著一個Window。

那View是怎樣綁定在Window上的呢?還要介紹下Window和View之間的紐帶:ViewRoot。ViewRoot對應于ViewRootImpl類,它是連接WindowManager和Decorview的紐帶,View的三大流程(measure,layout,draw)均是通過ViewRoot來完成的。在ActivityThread中,當Activity對象被創建完畢后,會將DecorVidew添加到Window中,同時會創建ViewRootImpl對象,并將ViewRootImpl對象和DecorView建立關聯。

一個比喻總結下Activity Window View三只之間的關系:Activity像一個工匠(控制單元),Window像窗戶(承載模型),View像窗花(顯示視圖)。

1)一個Activity構造的時候會初始化一個Window,準確的說是PhoneWindow。
2)這個PhoneWindow有一個“ViewRoot”,引號是說其實這個“ViewRoot”是一個View或者說ViewGroup,是最初始的根視圖。
3)“ViewRoot”通過addView方法來一個個的添加View。比如TextView,Button等
4)這些View的事件監聽,是由WindowManagerService來接受消息,并且回調Activity函數。比如onClickListener,onKeyDown等。

3,簡單講講Fragment的生命周期?

Fragment

這張圖是Fragment生命周期和Activity生命周期對比圖,可以看到兩者還是有很多相似的地方,比如都有onCreate(),onStart(),onPause(),onDestroy()等等,因為Fragment是被托管到Activity中的,所以多了兩個onAttach()和onDetach()。這里講講與Activity生命周期不一樣的方法。
onAttach()
Fragment和Activity建立關聯的時候調用,被附加到Activity中去。
onCreate()
系統會在創建Fragment時調用此方法。可以初始化一段資源文件等等。
onCreateView()
系統會在Fragment首次繪制其用戶界面時調用此方法。 要想為Fragment繪制 UI,從該方法中返回的 View 必須是Fragment布局的根視圖。如果Fragment未提供 UI,您可以返回 null。
onViewCreated()
在Fragment被繪制后,調用此方法,可以初始化控件資源。
onActivityCreated()
當onCreate(),onCreateView(),onViewCreated()方法執行完后調用,也就是Activity被渲染繪制出來后。
onPause()

系統將此方法作為用戶離開Fragment的第一個信號(但并不總是意味著此Fragment會被銷毀)進行調用。 通常可以在此方法內確認在當前用戶會話結束后仍然有效的任何更改(因為用戶可能不會返回)。
onDestroyView()
Fragment中的布局被移除時調用。
onDetach()
Fragment和Activity解除關聯的時候調用。
但需要注一點是:除了onCreateView,其他的所有方法如果你重寫了,必須調用父類對于該方法的實現。

4,自定義View是一種什么樣的流程?

當 Activity 接收到焦點的時候,它會被請求繪制布局,該請求由 Android framework 處理.繪制是從根節點開始,對布局樹進行 measure 和 draw。整個 View 樹的繪圖流程在ViewRoot.java類的performTraversals()函數展開,該函數所做 的工作可簡單概況為是否需要重新計算視圖大小(measure)、是否需要重新安置視圖的位置(layout)、以及是否需要重繪(draw),流程圖如下:

自定義View

5,安卓中事件機制如何?

1、基礎知識
(1) 所有 Touch 事件都被封裝成了 MotionEvent 對象,包括 Touch 的位置、時間、歷史記錄以及第幾個手指(多指觸摸)等。
(2) 事件類型分為 ACTION_DOWN, ACTION_UP, ACTION_MOVE, ACTION_POINTER_DOWN, ACTION_POINTER_UP, ACTION_CANCEL,每個事件都是以 ACTION_DOWN 開始 ACTION_UP 結束。
(3) 對事件的處理包括三類,分別為傳遞——dispatchTouchEvent()函數、攔截——onInterceptTouchEvent()函數、消費——onTouchEvent()函數和 OnTouchListener。

2、傳遞流程
(1) 事件從 Activity.dispatchTouchEvent()開始傳遞,只要沒有被停止或攔截,從最上層的 View(ViewGroup)開始一直往下(子 View)傳遞。子 View 可以通過 onTouchEvent()對事件進行處理。
(2) 事件由父 View(ViewGroup)傳遞給子 View,ViewGroup 可以通過 onInterceptTouchEvent()對事件做攔截,停止其往下傳遞。
(3) 如果事件從上往下傳遞過程中一直沒有被停止,且最底層子 View 沒有消費事件,事件會反向往上傳遞,這時父 View(ViewGroup)可以進行消費,如果還是沒有被消費的話,最后會到 Activity 的 onTouchEvent()函數。
(4) 如果 View 沒有對 ACTION_DOWN 進行消費,之后的其他事件不會傳遞過來。
(5) OnTouchListener 優先于 onTouchEvent()對事件進行消費。
上面的消費即表示相應函數返回值為 true。
(1) View 不處理事件流程圖


不處理

(2) View 處理事件流程圖


處理

6,安卓的啟動模式有了解嗎,singleTop,singleTask,singleInstance各在哪些場景使用居多。

  • standard:標準模式,系統默認模式。每次啟動一個Activity都會重新創建一個新的實例,不管這個實例是否已經存在。在這個模式下,誰啟動了Activity,那么這個Activity就運行在啟動它的那個Activity所在棧中。
  • singleTop:棧頂復用模式。在這種模式下,如果新的Activity已經位于任務棧頂,那么此Activity不會被重新創建,同時它的onNewIntent方法會被回調,通過此方法的參數我們可以取出當前的請求信息
  • singleTask:棧內復用模式。這是一種單例模式,在這種模式下,只要Activity在一個棧中存在,那么多次啟動此Activity都不會創建實例,和singleTop是一樣,系統也會調用onNewIntent。還有一點,就是singleTask有clearTop的效果,會導致棧內已有的Activity全部出棧。
  • singleInstance:單一實例模式。這是一種加強的singleTask模式,它除了具有singleTask的所有特性以外,還加強了一點,那就是具有此模式的Activity只能單獨位于一個任務棧中,比如Activity A是singleInstance模式,當A啟動后,系統會為它創建一個新的任務棧,然后A獨自在這個新的任務棧中,由于棧內復用的特性,后續均不會創建新的Activity,除非這個獨特的任務棧被系統銷毀。整個手機操作系統里面只有一個實例存在。不同的應用去打開這個activity 共享公用的同一個activity。他會運行在自己單獨,獨立的任務棧里面,并且任務棧里面只有他一個實例存在。

standard使用場景:
郵件客戶端,在新建一個郵件的時候,適合新建一個新的實例

singleTop使用場景:
消息推送,通知欄彈出Notification,點擊Notification跳轉到指定Activity,使用singleTop避免生成重復的頁面。
登錄的時候,登錄成功跳轉到主頁,按下兩次登錄按鈕,使用singleTask避免生成兩個主頁。
從activity A啟動了個service進行耗時操作,或者某種監聽,這個時候你home鍵了,service收集到信息,要返回activityA。

singleTask使用場景:
提供給第三方應用調用的頁面,做瀏覽器、微博之類的應用,瀏覽器的主界面等等。
程序的主界面,進入多層嵌套之后,一鍵退回,之前打開的Activity全部出棧。

singleInstance使用場景:
呼叫來電界面,打電話、發短信功能。
鬧鈴提醒,將鬧鈴提醒與鬧鈴設置分離。

7,安卓內存泄露你會怎么處理,如何排查

集合類泄漏
集合類如果僅僅有添加元素的方法,而沒有相應的刪除機制,導致內存被占用。如果這個集合類是全局性的變量 (比如類中的靜態屬性,全局性的 map 等即有靜態引用或 final 一直指向它),那么沒有相應的刪除機制,很可能導致集合所占用的內存只增不減。

單例造成的內存泄漏
由于單例的靜態特性使得其生命周期跟應用的生命周期一樣長,所以如果使用不恰當的話,很容易造成內存泄漏。

非靜態內部類創建靜態實例造成的內存泄漏
匿名內部類
Handler 造成的內存泄漏
Handler 的使用造成的內存泄漏問題應該說是最為常見了,很多時候我們為了避免 ANR 而不在主線程進行耗時操作,在處理網絡任務或者封裝一些請求回調等api都借助Handler來處理,但 Handler 不是萬能的,對于 Handler 的使用代碼編寫一不規范即有可能造成內存泄漏。另外,我們知道 Handler、Message 和 MessageQueue 都是相互關聯在一起的,萬一Handler發送的 Message 尚未被處理,則該 Message 及發送它的 Handler 對象將被線程 MessageQueue 一直持有。
由于 Handler 屬于 TLS(Thread Local Storage) 變量, 生命周期和 Activity 是不一致的。因此這種實現方式一般很難保證跟 View 或者 Activity 的生命周期保持一致,故很容易導致無法正確釋放。

盡量避免使用 static 成員變量
避免 override finalize()
1、finalize 方法被執行的時間不確定,不能依賴與它來釋放緊缺的資源。時間不確定的原因是:
虛擬機調用GC的時間不確定
Finalize daemon線程被調度到的時間不確定

2、finalize 方法只會被執行一次,即使對象被復活,如果已經執行過了 finalize 方法,再次被 GC 時也不會再執行了,原因是:含有 finalize 方法的 object 是在 new 的時候由虛擬機生成了一個 finalize reference 在來引用到該Object的,而在 finalize 方法執行的時候,該 object 所對應的 finalize Reference 會被釋放掉,即使在這個時候把該 object 復活(即用強引用引用住該 object ),再第二次被 GC 的時候由于沒有了 finalize reference 與之對應,所以 finalize 方法不會再執行。

3、含有Finalize方法的object需要至少經過兩輪GC才有可能被釋放。
詳情見這里 深入分析過dalvik的代碼
資源未關閉造成的內存泄漏

對于使用了BraodcastReceiver,ContentObserver,File,游標 Cursor,Stream,Bitmap等資源的使用,應該在Activity銷毀時及時關閉或者注銷,否則這些資源將不會被回收,造成內存泄漏。
一些不良代碼造成的內存壓力

有些代碼并不造成內存泄露,但是它們,或是對沒使用的內存沒進行有效及時的釋放,或是沒有有效的利用已有的對象而是頻繁的申請新內存。

8,反射(Reflection)和注解(Annotation )的作用,知道其中的原理嗎,講講。

什么是反射
Java類是被Java虛擬機加載,如果Java類不被Java虛擬機加載,就不能正常運行,正常情況下,我們運行所有程序在編譯期時候就已經把那個類被加載了。
Java的反射機制是在編譯時并不確定是哪個類被加載了,而是在程序運行的時候才被加載、探知、自審。使用的是在編譯期并不知道的類,這就是Java反射的特點。

反射的作用
Java反射機制它知道類的基本結構,這種對Java類結構探知的能力,我們稱為Java類的“自審”。如eclipse中,一按點,編譯工具就會自動的把該對象能夠使用的所有的方法和屬性全部都列出來,供用戶進行選擇,這就是利用反射機制,做到代碼的智能提示。

舉個例子:假如有兩個程序員,一個程序員在寫程序的時需要使用第二個程序員所寫的類,但第二個程序員并沒完成他所寫的類。那么第一個程序員的代碼是不能通過編譯的。此時,利用Java反射的機制,就可以讓第一個程序員在沒有得到第二個程序員所寫的類的時候,來完成自身代碼的編譯。

反射使用場景
1.工廠模式:Factory類中用反射的話,添加了一個新的類之后,就不需要再修改工廠類Factory了
2.數據庫JDBC中通過Class.forName(Driver).來獲得數據庫連接驅動
3.分析類文件:畢竟能得到類中的方法等等
4.訪問一些不能訪問的變量或屬性:破解別人代碼

什么是注解
注解是 Java 5 的一個新特性。注解是插入你代碼中的一種注釋或者說是一種元數據(meta data)。這些注解信息可以在編譯期使用預編譯工具進行處理(pre-compiler tools),也可以在運行期使用 Java反射機制進行處理。

Annotation 對程式運行沒有影響,它的目的在對編譯器或分析工具說明程式的某些資訊,您可以在包、類、方法、成員等加上Annotation,每一個Annotation 對應于一個實際的Annotation 型態,您可以從java.lang.Override、java.lang.Deprecated、java.lang.SuppressWarnings 這三個J2SE 5.0 中標準的Annotation 型態開始了解Annotation 的作用。

限定Override 父類方法@Override
java.lang.Override 是J2SE 5.0 中標準的Annotation 型態之一,它對編譯器說明某個方法必須是重新定義父類別中的方法,編譯器得知這項資訊后,在編譯程式時如果發現被@Override 標示的方法并非重新定義父類別中的方法,就會回報錯誤。
java.lang.Override 是個Marker annotation,簡單的說就是用于標示的Annotation,Annotation 名稱本身即表示了要給工具程式的資訊,例如Override 這個名稱告知編譯器,被@Override 標示的方法必須是重新定義父類別中的同名方法。

標示方法為Deprecated @Deprecated
java.lang.Deprecated 也是個Marker annotation,簡單的說就是用于標示,Annotation 名稱本身即包括了要給工具程式的資訊,例如Deprecated 這個名稱在告知編譯器,被@Deprecated 標示的方法是一個不建議被使用的方法,如果有開發人員不小心使用了被@Deprecated 標示的方法,編譯器要提出提示提醒開發人員。

抑制編譯器提示@SuppressWarnings
java.lang.SuppressWarnings 是J2SE 5.0 中標準的Annotation 型態之一,它對編譯器說明某個方法中若有提示訊息,則加以抑制,不用在編譯完成后出現提示,不過事實上這個功能在Sun JDK 5.0 中沒有實現出來。

注解使用場景
1,設計一個原始碼分析工具,分析代碼等
2,日志信息打

9,進程和線程的關系,在Android中activity A和activity B這兩者是不是在同一個線程中

進程一般是指一個執行單元,在PC和移動設備上指的是一個程序或者一個應用,一個進程可以包含多個線程,因此進程和線程是包含和被包含的關系,最簡單的情況下,一個進程中可以只有一個線程,即主線程,在Android中主線程也叫做UI線程,只有在UI線程中才能操作界面元素,但不能執行耗時操作任務。

當某個應用組件啟動且該應用沒有運行其他任何組件時,Android 系統會使用單個執行線程為應用啟動新的 Linux 進程。默認情況下,同一應用的所有組件在相同的進程和線程(稱為“主”線程)中運行。 如果某個應用組件啟動且該應用已存在進程(因為存在該應用的其他組件),則該組件會在此進程內啟動并使用相同的執行線程。 但是,您可以安排應用中的其他組件在單獨的進程中運行,并為任何進程創建額外的線程。

進程
Android 系統將盡量長時間地保持應用進程,但為了新建進程或運行更重要的進程,最終需要移除舊進程來回收內存。 為了確定保留或終止哪些進程,系統會根據進程中正在運行的組件以及這些組件的狀態,將每個進程放入“重要性層次結構”中。 必要時,系統會首先消除重要性最低的進程,然后是重要性略遜的進程,依此類推,以回收系統資源。

線程
應用啟動時,系統會為應用創建一個名為“主線程”的執行線程。 此線程非常重要,因為它負責將事件分派給相應的用戶界面小部件,其中包括繪圖事件。 此外,它也是應用與 Android UI 工具包組件(來自 android.widget 和 android.view 軟件包的組件)進行交互的線程。因此,主線程有時也稱為 UI 線程。
系統不會為每個組件實例創建單獨的線程。運行于同一進程的所有組件均在 UI 線程中實例化,并且對每個組件的系統調用均由該線程進行分派。 因此,響應系統回調的方法(例如,報告用戶操作的 onKeyDown() 或生命周期回調方法)始終在進程的 UI 線程中運行。
例如,當用戶觸摸屏幕上的按鈕時,應用的 UI 線程會將觸摸事件分派給小部件,而小部件反過來又設置其按下狀態,并將失效請求發布到事件隊列中。 UI 線程從隊列中取消該請求并通知小部件應該重繪自身。
activity A和activity B這兩者是在同一個線程,因為都需要被加載到UI主線程中,是四大組件之一。

10,hashmap的原理如何。hashcode和equal這兩者有什么區別嗎

1. 什么時候會使用HashMap?他有什么特點?
是基于Map接口的實現,存儲鍵值對時,它可以接收null的鍵值,是非同步的,HashMap存儲著Entry(hash, key, value, next)對象。

2. 你知道HashMap的工作原理嗎?
通過hash的方法,通過put和get存儲和獲取對象。存儲對象時,我們將K/V傳給put方法時,它調用hashCode計算hash從而得到bucket位置,進一步存儲,HashMap會根據當前bucket的占用情況自動調整容量(超過Load Facotr則resize為原來的2倍)。獲取對象時,我們將K傳給get,它調用hashCode計算hash從而得到bucket位置,并進一步調用equals()方法確定鍵值對。如果發生碰撞的時候,Hashmap通過鏈表將產生碰撞沖突的元素組織起來,在Java 8中,如果一個bucket中碰撞沖突的元素超過某個限制(默認是8),則使用紅黑樹來替換鏈表,從而提高速度。

3. 你知道get和put的原理嗎?equals()和hashCode()的都有什么作用?
通過對key的hashCode()進行hashing,并計算下標( n-1 & hash),從而獲得buckets的位置。如果產生碰撞,則利用key.equals()方法去鏈表或樹中去查找對應的節點

4. 你知道hash的實現嗎?為什么要這樣實現?
在Java 1.8的實現中,是通過hashCode()的高16位異或低16位實現的:(h = k.hashCode()) ^ (h >>> 16),主要是從速度、功效、質量來考慮的,這么做可以在bucket的n比較小的時候,也能保證考慮到高低bit都參與到hash的計算中,同時不會有太大的開銷。

5. 如果HashMap的大小超過了負載因子(load factor)定義的容量,怎么辦?
如果超過了負載因子(默認0.75),則會重新resize一個原來長度兩倍的HashMap,并且重新調用hash方法。

**為什么String, Interger這樣的wrapper類適合作為鍵? **

String, Interger這樣的wrapper類作為HashMap的鍵是再適合不過了,而且String最為常用。因為String是不可變的,也是final的,而且已經重寫了equals()和hashCode()方法了。其他的wrapper類也有這個特點。不可變性是必要的,因為為了要計算hashCode(),就要防止鍵值改變,如果鍵值在放入時和獲取時返回不同的hashcode的話,那么就不能從HashMap中找到你想要的對象。不可變性還有其他的優點如線程安全。如果你可以僅僅通過將某個field聲明成final就能保證hashCode是不變的,那么請這么做吧。因為獲取對象的時候要用到equals()和hashCode()方法,那么鍵對象正確的重寫這兩個方法是非常重要的。如果兩個不相等的對象返回不同的hashcode的話,那么碰撞的幾率就會小些,這樣就能提高HashMap的性能。

我們可以使用自定義的對象作為鍵嗎?

這是前一個問題的延伸。當然你可能使用任何對象作為鍵,只要它遵守了equals()和hashCode()方法的定義規則,并且當對象插入到Map中之后將不會再改變了。如果這個自定義對象時不可變的,那么它已經滿足了作為鍵的條件,因為當它創建之后就已經不能改變了。

我們可以使用CocurrentHashMap來代替Hashtable嗎?

這是另外一個很熱門的面試題,因為ConcurrentHashMap越來越多人用了。我們知道Hashtable是synchronized的,但是ConcurrentHashMap同步性能更好,因為它僅僅根據同步級別對map的一部分進行上鎖。ConcurrentHashMap當然可以代替HashTable,但是HashTable提供更強的線程安全性。看看這篇博客查看Hashtable和ConcurrentHashMap的區別。

hashcode和equal這兩者有什么區別?

在Java中任何一個對象都具備equals(Object obj)和hashcode()這兩個方法,因為他們是在Object類中定義的。
equals(Object obj)方法用來判斷兩個對象是否“相同”,如果“相同”則返回true,否則返回false。
hashcode()方法返回一個int數,在Object類中的默認實現是“將該對象的內部地址轉換成一個整數返回”。
1、如果兩個對象equals,Java運行時環境會認為他們的hashcode一定相等。
2、如果兩個對象不equals,他們的hashcode有可能相等。
3、如果兩個對象hashcode相等,他們不一定equals。
4、如果兩個對象hashcode不相等,他們一定不equals。

11,講講設計模式六大原則

  • 單一原則。就是說一個類只有一個明確的職責,不易過多職責封裝在一個類里面。
  • 開閉原則。對于修改是關閉的,對于擴展的開放的,這樣主要目的是為了提高可擴展性。
  • 依賴倒置原則。高層不依賴于低層,兩者都依賴于抽象,抽象不依賴于細節實現,具體實現依賴于抽象,也就是說要面向接口編程。
  • 里氏替換原則。也就說子類運行的功能,父類也能運行,強調繼承的重要性
  • 迪米特原則。一個類要了解另外一個類最少的內容,強調低耦合,耦合分解
  • 接口隔離原則。一個類不要繼承不需要的接口,接口可拆分,不要冗余在一個總接口中,實現自己所需接口即可。

12,動態代理有用過嗎,用過的話,你覺得在哪些場景比較合適。

代理:設計模式
代理是一種常用的設計模式,其目的就是為其他對象提供一個代理以控制對某個對象的訪問。代理類負責為委托類預處理消息,過濾消息并轉發消息,以及進行消息被委托類執行后的后續處理。

代理模式

為了保持行為的一致性,代理類和委托類通常會實現相同的接口,所以在訪問者看來兩者沒有絲毫的區別。通過代理類這中間一層,能有效控制對委托類對象的直接訪問,也可以很好地隱藏和保護委托類對象,同時也為實施不同控制策略預留了空間,從而在設計上獲得了更大的靈活性。Java 動態代理機制以巧妙的方式近乎完美地實踐了代理模式的設計理念。

代理的實現分為:

  • 靜態代理:代理類是在編譯時就實現好的。也就是說 Java 編譯完成后代理類是一個實際的 class 文件。
  • 動態代理:代理類是在運行時生成的。也就是說 Java 編譯完之后并沒有實際的 class 文件,而是在運行時動態生成的類字節碼,并加載到JVM中。

一個典型的動態代理創建對象過程可分為以下四個步驟:
1、通過實現InvocationHandler接口創建自己的調用處理器 IvocationHandler handler = new InvocationHandlerImpl(...);
2、通過為Proxy類指定ClassLoader對象和一組interface創建動態代理類
Class clazz = Proxy.getProxyClass(classLoader,new Class[]{...});
3、通過反射機制獲取動態代理類的構造函數,其參數類型是調用處理器接口類型
Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});
4、通過構造函數創建代理類實例,此時需將調用處理器對象作為參數被傳入
Interface Proxy = (Interface)constructor.newInstance(new Object[] (handler));

為了簡化對象創建過程,Proxy類中的newInstance方法封裝了2~4,只需兩步即可完成代理對象的創建。
生成的ProxySubject繼承Proxy類實現Subject接口,實現的Subject的方法實際調用處理器的invoke方法,而invoke方法利用反射調用的是被代理對象的的方法(Object result=method.invoke(proxied,args))

Java 實現動態代理的缺點:因為 Java 的單繼承特性(每個代理類都繼承了 Proxy 類),只能針對接口創建代理類,不能針對類創建代理類。

13,有什么擅長的腳本語言嗎,比如Python、Ruby

這里可以自己回答了,說擅長的語言即可。

14,了解JVM嗎,講講Java內存機制

Java 虛擬機(Java virtual machine,JVM)是運行 Java 程序必不可少的機制。JVM實現了Java語言最重要的特征:即平臺無關性。
編譯后的 Java 程序指令并不直接在硬件系統的 CPU 上執行,而是由 JVM 執行。JVM屏蔽了與具體平臺相關的信息,使Java語言編譯程序只需要生成在JVM上運行的目標字節碼(.class),就可以在多種平臺上不加修改地運行。Java 虛擬機在執行字節碼時,把字節碼解釋成具體平臺上的機器指令執行。因此實現java平臺無關性。它是 Java 程序能在多平臺間進行無縫移植的可靠保證,同時也是 Java 程序的安全檢驗引擎(還進行安全檢查)。


JVM運行圖

15,你做過最有成就感的事是什么,解決什么樣的問題

大家可以講講平時在工作中解決了什么樣的問題, 碰到什么困難,又是如何解決的,讓你印象深刻的幾個點就可以了。

16,安卓中消息循環機制如何?

Handler 、 Looper 、Message 這三者都與Android異步消息處理線程相關的概念。那么什么叫異步消息處理線程呢?異步消息處理線程啟動后會進入一個無限的循環體之中,每循環一次,從其內部的消息隊列中取出一個消息,然后回調相應的消息處理函數,執行完成一個消息后則繼續循環。若消息隊列為空,線程則會阻塞等待。
那么Android消息機制主要是指Handler的運行機制,Handler運行需要底層的MessageQueue和Looper支撐。其中MessageQueue采用的是單鏈表的結構,Looper可以叫做消息循環。由于MessageQueue只是一個消息存儲單元,不能去處理消息,而Looper就是專門來處理消息的,Looper會以無限循環的形式去查找是否有新消息,如果有的話,就處理,否則就一直等待著。
Handler創建的時候會采用當前線程的Looper來構造消息循環系統,需要注意的是,線程默認是沒有Looper的,如果需要使用Handler就必須為線程創建Looper,因為默認的UI主線程,也就是ActivityThread,ActivityThread被創建的時候就會初始化Looper,這也是在主線程中默認可以使用Handler的原因。

最后,希望本文能對正在找工作的小伙伴提供一點點幫助吧。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,076評論 25 708
  • java 接口的意義-百度 規范、擴展、回調 抽象類的意義-樂視 為其子類提供一個公共的類型封裝子類中得重復內容定...
    交流電1582閱讀 2,268評論 0 11
  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法,內部類的語法,繼承相關的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,754評論 18 399
  • 此生多念,便如不念 你念,花開 多念,花敗 你念回眸一世,便執著一世 你念紅塵三千,便茫然三千 你念一生,一生若蓮...
    羽月痕閱讀 280評論 1 2
  • 目錄樂器小白如何入門尤克里里? 上一篇樂器小白如何入門尤克里里?今天我們來調音和識譜 <沒想到尤克里里入門知識分享...
    蘭小四閱讀 11,441評論 28 126