閱讀607 返回首頁    go 小米 go 小米5


Android 2017麵試題整理

似乎自去年下半年以來,大家跳槽的少了,還有有些公司裁員了,前幾年火熱的移動端、前端崗位也越來越少,回歸理性。現在各大公司對移動Android/ios的需求基本要求都是三年以上相關經驗,有過大型互聯網項目經驗,基礎紮實。那麼對於我們從事Android開發的程序員,我們究竟需要掌握哪些技術呢?麵試官究竟會問什麼呢?今天,結合我的麵試經驗,給大家整理一下。

Android常見麵試題整理

以我的經驗,麵試基本都是簡單到原理循序漸進的過程,所以這裏整理的時候也遵循這個思路。

  • standard:默認標準模式,每啟動一個都會創建一個實例。
  • singleTop:棧頂複用,如果在棧頂就調用onNewIntent複用,從onResume()開始。
  • singleTask:棧內複用,本棧內隻要用該類型Activity就會調到棧頂複用,從onResume()開始。
  • singleInstance:單例模式,除了3中特性,係統會單獨給該Activity創建一個棧。

  • 配置改變導致Activity被殺死,橫屏變豎屏:在onStop之前會調用onSaveInstanceState()保存數據在重建Activity之後,會在onStart()之後調用onRestoreInstanceState(),並把保存下來的Bundle傳給onCreate()和它會默認重建Activity當前的視圖,我們可以在onCreate()中,回複自己的數據。
  • 內存不足殺掉Activity,優先級分別是:前台可見,可見非前台,後台。

  • context.startService() ->onCreate()- >onStart()->Service
    running-->(如果調用context.stopService() )->onDestroy() ->Service shut down
    1.如果Service還沒有運行,則調用onCreate()然後調用onStart();
    2.如果Service已經運行,則隻調用onStart(),所以一個Service的onStart方法可能會重複調用多次。
    3.調用stopService的時候直接onDestroy,
    4.如果是調用者自己直接退出而沒有調用stopService的話,Service會一直在後台運行。該Service的調用者再啟動起來後可以通過stopService關閉Service。

  • context.bindService()->onCreate()->onBind()->Service
    running-->onUnbind() -> onDestroy() ->Service stop
    1.onBind將返回給客戶端一個IBind接口實例,IBind允許客戶端回調服務的方法,比如得到Service運行的狀態或其他操作。
    2.這個時候會把調用者和Service綁定在一起,Context退出了,Service就會調用onUnbind->onDestroy相應退出。
    3.所以調用bindService的生命周期為:onCreate --> onBind(隻一次,不可多次綁定) --> onUnbind --> onDestory。

一般從以下幾個方法去想辦法:

  • 提升service優先級
  • 提升service進程優先級
  • onDestroy方法裏重啟service

動畫的基本原理:其實就是利用插值器和估值器,來計算出各個時刻View的屬性,然後通過改變View的屬性來,實現View的動畫效果。
動畫在3.0之前分為兩種動畫:幀動畫和視圖動畫,在3.0之後新增了屬性動畫,麵試官一般問是抓住屬性動畫問。

  • 幀動畫是在xml中定義好一係列圖片之後,使用AnimationDrawable來播放的動畫。
  • View動畫:隻是影像變化,view的實際位置還在原來的地方
  • View的屬性動畫:主要說下插值器和估值器。 1.插值器:作用是根據時間的流逝的百分比來計算屬性改變的百分比 2.估值器:在1的基礎上由這個東西來計算出屬性到底變化了多少數值的類

在說這個問題之前一定要先說明Handler、Loop消息隊列模型包含哪些東西,在說他們之前是怎麼互相調用的,最後說原理。

  • MessageQueue:讀取會自動刪除消息,單鏈表維護,在插入和刪除上有優勢。在其next()中會無限循環,不斷判斷是否有消息,有就返回這條消息並移除。
  • Looper:Looper創建的時候會創建一個MessageQueue,調用loop()方法的時候消息循環開始,loop()也是一個死循環,會不斷調用messageQueue的next(),當有消息就處理,否則阻塞在messageQueue的next()中。當Looper的quit()被調用的時候會調用messageQueue的quit(),此時next()會返回null,然後loop()方法也跟著退出。
  • Handler:在主線程構造一個Handler,然後在其他線程調用sendMessage(),此時主線程的MessageQueue中會插入一條message,然後被Looper使用。

係統的主線程在ActivityThread的main()為入口開啟主線程,其中定義了內部類Activity.H定義了一係列消息類型,包含四大組件的啟動停止。

Activity中最終會走到startActivityForResult()(mMainThread.getApplicationThread()傳入了一個ApplicationThread檢查APT)
->Instrumentation#execStartActivity()和checkStartActivityResult()(這是在啟動了Activity之後判斷Activity是否啟動成功,例如沒有在AM中注冊那麼就會報錯)
->ActivityManagerNative.getDefault().startActivity()(類似AIDL,實現了IAM,實際是由遠端的AMS實現startActivity())
->ActivityStackSupervisor#startActivityMayWait()
->ActivityStack#resumeTopActivityInnerLocked
->ActivityStackSupervisor#realStartActivityLocked()(在這裏調用APT的scheduleLaunchActivity,也是AIDL,不過是在遠端調起了本進程Application線程)
->ApplicationThread#scheduleLaunchActivity()(這是本進程的一個線程,用於作為Service端來接受AMS client端的調起)
->ActivityThread#handleLaunchActivity()(接收內部類H的消息,ApplicationThread線程發送LAUNCH_ACTIVITY消息給H)
->最終在ActivityThread#performLaunchActivity()中實現Activity的啟動完成了以下幾件事:

  • 從傳入的ActivityClientRecord中獲取待啟動的Activity的組件信息
  • 創建類加載器,使用Instrumentation#newActivity()加載Activity對象
  • 調用LoadedApk.makeApplication方法嚐試創建Application,由於單例所以不會重複創建。
  • 創建Context的實現類ContextImpl對象,並通過Activity#attach()完成數據初始化和Context建立聯係,因為Activity是Context的橋接類,
  • 最後就是創建和關聯window,讓Window接收的事件傳給Activity,在Window創建過程中會調用ViewRootImpl的performTraversals()初始化View。
  • Instrumentation#callActivityOnCreate()->Activity#performCreate()->Activity#onCreate().onCreate()中會通過Activity#setContentView()調用PhoneWindow的setContentView()刷新界麵

在說這個之前,要按以下流程來說:onInteceptTouchEvent負責事件攔截 -->dispatchTouchEvent負責事件分發 -->onTouchEvent負責事件處理

這裏寫圖片描述

具體闡述:
android的分發機製:由父控件判斷是否攔截,如果不攔截事件,則繼續分發到子控件,然後一直分發下去。

處理:與分發相反,由子控件先處理事件,如果子控件不處理,則交給父控件處理,一直向上傳遞,直到那個控件處理了觸摸事件

相關方法:Boolean dispatchTouchEvent(MotionEvent ev)接收到觸摸事件是否分發事件到下麵的view,返回true分發觸摸事件Boolean onInterceptTouchEvent(MotionEvent ev)接收到觸摸事件是否攔截事件,返回true攔截,則調用onTouchEvent方法處理事件,返回false,繼續向子控件傳 Boolean onTouchEvent(View v,MotionEvent event)是否響應事件返回true,響應;返回false不響應Boolean onTouch(View v,MotionEvent event)是否響應事件,當view調用了setOnTouchListener方法設置了觸摸監聽器,則事件響應的時候先調用onTouch方法返回true,響應,onTouchEvent方法不執行;返回false時相反 voidrequestDisallowInterceptTouchEvent(Boolean disallowIntercept)請求父控件是否不攔截事件,返回true:不允許父控件的onInterceptTouchEvent調用,false 允許。


其實問問這個問題就是問view的繪製的流程,看你是否去看過源碼。一般流程如下:

  • ViewRootImpl會調用performTraversals(),其內部會調用performMeasure()、performLayout、performDraw()。
  • performMeasure()會調用最外層的ViewGroup的measure()-->onMeasure(),ViewGroup的onMeasure()是抽象方法,但其提供了measureChildren(),這之中會遍曆子View然後循環調用measureChild()這之中會用getChildMeasureSpec()+父View的MeasureSpec+子View的LayoutParam一起獲取本View的MeasureSpec,然後調用子View的measure()到View的onMeasure()-->setMeasureDimension(getDefaultSize(),getDefaultSize()),getDefaultSize()默認返回measureSpec的測量數值,所以繼承View進行自定義的wrap_content需要重寫。
  • 設置子view的顯示,performLayout()會調用最外層的ViewGroup的layout(l,t,r,b),本View在其中使用setFrame()設置本View的四個頂點位置。在onLayout(抽象方法)中確定子View的位置,如LinearLayout會遍曆子View,循環調用setChildFrame()-->子View.layout()。
  • performDraw()會調用最外層ViewGroup的draw():其中會先後調用background.draw()(繪製背景)、onDraw()(繪製自己)、dispatchDraw()(繪製子View)、onDrawScrollBars()(繪製裝飾)。
  • MeasureSpec由2位SpecMode(UNSPECIFIED、EXACTLY(對應精確值和match_parent)、AT_MOST(對應warp_content))和30位SpecSize組成一個int,DecorView的MeasureSpec由窗口大小和其LayoutParams決定,其他View由父View的MeasureSpec和本View的LayoutParams決定。ViewGroup中有getChildMeasureSpec()來獲取子View的MeasureSpec。

注:有以下三種方式獲取measure()的寬高:
1.Activity#onWindowFocusChange()中調用獲取
2.view.post(Runnable)將獲取的代碼投遞到消息隊列的尾部。
3.ViewTreeObservable.

在Activity和Service進行通訊的時候,都會用到了Binder。當屬於同個進程我們可以繼承Binder然後在Activity中對Service進行操作。當不屬於同個進程,那麼要用到AIDL讓係統給我們創建一個Binder,然後在Activity中對遠端的Service進行操作。

係統給我們生成的Binder:

  • Stub類中有:接口方法的id,有該Binder的標識,有asInterface(IBinder)(讓我們在Activity中獲取實現了Binder的接口,接口的實現在Service裏,同進程時候返回Stub否則返回Proxy),有onTransact()這個方法是在不同進程的時候讓Proxy在Activity進行遠端調用實現Activity操作Service。
  • Proxy類是代理,在Activity端,其中有:IBinder mRemote(這就是遠端的Binder),兩個接口的實現方法不過是代理最終還是要在遠端的onTransact()中進行實際操作。

哪一端的Binder是副本,該端就可以被另一端進行操作,因為Binder本體在定義的時候可以操作本端的東西。所以可以在Activity端傳入本端的Binder,讓Service端對其進行操作稱為Listener,可以用RemoteCallbackList這個容器來裝Listener,防止Listener因為經曆過序列化而產生的問題。

當Activity端向遠端進行調用的時候,當前線程會掛起,當方法處理完畢才會喚醒。

如果一個AIDL就用一個Service太奢侈,所以可以使用Binder池的方式,建立一個AIDL其中的方法是返回IBinder,然後根據方法中傳入的參數返回具體的AIDL。

Window理解:

Window用於顯示View和接收各種事件,Window有三種類型:應用Window(每個Activity對應一個Window)、子Window(不能單獨存在,附屬於特定Window)、係統window(Toast和狀態欄)。Window分層級,應用Window在1-99、子Window在1000-1999、係統Window在2000-2999.WindowManager提供了增刪改View三個功能。Window是個抽象概念:每一個Window對應著一個View和ViewRootImpl,Window通過ViewRootImpl來和View建立聯係,View是Window存在的實體,隻能通過WindowManager來訪問Window。

WindowManager理解:

WindowManager的實現是WindowManagerImpl其再委托給WindowManagerGlobal來對Window進行操作,其中有四個List分別儲存對應的View、ViewRootImpl、WindowManger.LayoutParams和正在被刪除的View。Window的實體是存在於遠端的WindowMangerService中,所以增刪改Window在本端是修改上麵的幾個List然後通過ViewRootImpl重繪View,通過WindowSession(每個應用一個)在遠端修改Window。

雙親委托:一個ClassLoader類負責加載這個類所涉及的所有類,在加載的時候會判斷該類是否已經被加載過,然後會遞歸去他父ClassLoader中找。ClassLoader 隔離問題 JVM識別一個類是由:ClassLoader id+PackageName+ClassName。
關於ClassLoader的詳解介紹,請查看ClassLoader詳解

大致原理:apkpatch將兩個apk做一次對比,然後找出不同的部分。可以看到生成的apatch了文件,後綴改成zip再解壓開,裏麵有一個dex文件。通過jadx查看一下源碼,裏麵就是被修複的代碼所在的類文件,這些更改過的類都加上了一個_CF的後綴,並且變動的方法都被加上了一個叫@MethodReplace的annotation,通過clazz和method指定了需要替換的方法。然後客戶端sdk得到補丁文件後就會根據annotation來尋找需要替換的方法。最後由JNI層完成方法的替換。

不足:無法添加新類和新的字段、補丁文件很容易被反編譯、加固平台可能會使熱補丁功能失效。

減少onCreate的時間,那就精簡onCreate裏的代碼。放在onResume裏好了。為了用戶體驗更好一些,把頁麵顯示的View細分一下,放在AsyncTask裏逐步顯示,如果你夠熟練,用handler更好,這樣用戶的看到的就是有層次有步驟的一個個的view的展示,不會是先看到一個黑屏,然後一下顯示所有view。最好作成動畫,效果更自然些。利用多線程的目的就是盡可能的減少onCreate和onReume的時間,使得用戶能盡快看到頁麵,操作頁麵。      但是,很多操作是隻需要一次初始化的,都放在onResume裏每次進入activity都會浪費初始化時間。這個好解決,做一個boolean變量,在onCreate裏標記為true。在onResume裏判斷為true就進行初始化,初始化完成立刻置為false。

其他細節點:
1.減小主線程的阻塞時間 

2.提高Adapter和AdapterView的效率 (1)重用已生成過的Item View (2) 添加ViewHolder (3) 緩存Item的數據 (4)分段顯示

3.優化布局文件 如果我們的布局層次過多,就會影響GPU的繪製,所以要對布局進行優化

這個算麵試中的偏門,不過有時候會被問到,一般會問你使用的場合以及用法。
ContentProvider(內容提供者)是Android的四大組件之一,管理android以結構化方式存放的數據,以相對安全的方式封裝數據(表)並且提供簡易的處理機製和統一的訪問接口供其他程序調用。

URL(統一資源標識符)代表要操作的數據,可以用來標識每個ContentProvider,這樣你就可以通過指定的URI找到想要的ContentProvider,從中獲取或修改數據。 在Android中ContentProvider的格式如下:
這裏寫圖片描述

Android 源碼中的設計模式(你需要知道的設計模式全在這裏)

全麵了解Activity

Service全麵總結

IntentService使用詳解和實例介紹

Fragment 全解析

Android啟動過程圖解

Android 自定義View入門

Android 自定義ViewGroup入門實踐

Android 緩存機製

Android 異步消息處理機製源碼解析

Android View事件分發機製源碼分析

Android SQLite的使用入門

AIDL的使用情況和實例介紹

Java部分

首先,我們看一下這三者的繼承關係:
這裏寫圖片描述
我們可以看出ArrayList、LinkedList、Vector都實現了List的接口。

public class ArrayList extends AbstractList implements List, RandomAccess, Cloneable, Serializable

List 接口的大小可變數組的實現。實現了所有可選列表操作,並允許包括 null 在內的所有元素。除了實現 List 接口外,此類還提供一些方法來操作內部用來存儲列表的數組的大小。(此類大致上等同於 Vector 類,除了此類是不同步的。)

每個 ArrayList 實例都有一個容量。該容量是指用來存儲列表元素的數組的大小。它總是至少等於列表的大小。隨著向 ArrayList 中不斷添加元素,其容量也自動增長。並未指定增長策略的細節,因為這不隻是添加元素會帶來分攤固定時間開銷那樣簡單。

public class LinkedList extends AbstractSequentialList implements List, Deque, Cloneable, Serializable

List 接口的鏈接列表實現。實現所有可選的列表操作,並且允許所有元素(包括 null)。除了實現 List 接口外,LinkedList 類還為在列表的開頭及結尾 get、remove 和 insert 元素提供了統一的命名方法。這些操作允許將鏈接列表用作堆棧、隊列或雙端隊列。

此類實現 Deque 接口,為 add、poll 提供先進先出隊列操作,以及其他堆棧和雙端隊列操作。

所有操作都是按照雙重鏈接列表的需要執行的。在列表中編索引的操作將從開頭或結尾遍曆列表(從靠近指定索引的一端)。

public class Vector extends AbstractList implements List, RandomAccess, Cloneable, Serializable

Vector 類可以實現可增長的對象數組。與數組一樣,它包含可以使用整數索引進行訪問的組件。但是,Vector 的大小可以根據需要增大或縮小,以適應創建 Vector 後進行添加或移除項的操作。

每個向量會試圖通過維護 capacity 和 capacityIncrement 來優化存儲管理。capacity 始終至少應與向量的大小相等;這個值通常比後者大些,因為隨著將組件添加到向量中,其存儲將按 capacityIncrement 的大小增加存儲塊。應用程序可以在插入大量組件前增加向量的容量;這樣就減少了增加的重分配的量。

由 Vector 的 iterator 和 listIterator 方法所返回的迭代器是快速失敗的:如果在迭代器創建後的任意時間從結構上修改了向量(通過迭代器自身的 remove 或 add 方法之外的任何其他方式),則迭代器將拋出 ConcurrentModificationException。因此,麵對並發的修改,迭代器很快就完全失敗,而不是冒著在將來不確定的時間任意發生不確定行為的風險。Vector 的 elements 方法返回的 Enumeration 不是 快速失敗的。

從 Java 2 平台 v1.2 開始,此類改進為可以實現 List 接口,使它成為 Java Collections Framework 的成員。與新 collection 實現不同,Vector 是同步的。

區別

ArrayList 本質上是一個可改變大小的數組.當元素加入時,其大小將會動態地增長.內部的元素可以直接通過get與set方法進行訪問.元素順序存儲 ,隨機訪問很快,刪除非頭尾元素慢,新增元素慢而且費資源 ,較適用於無頻繁增刪的情況 ,比數組效率低,如果不是需要可變數組,可考慮使用數組 ,非線程安全.

LinkedList 是一個雙鏈表,在添加和刪除元素時具有比ArrayList更好的性能.但在get與set方麵弱於ArrayList. 適用於 :沒有大規模的隨機讀取,大量的增加/刪除操作.隨機訪問很慢,增刪操作很快,不耗費多餘資源 ,允許null元素,非線程安全.

Vector (類似於ArrayList)但其是同步的,開銷就比ArrayList要大。如果你的程序本身是線程安全的,那麼使用ArrayList是更好的選擇。 Vector和ArrayList在更多元素添加進來時會請求更大的空間。Vector每次請求其大小的雙倍空間,而ArrayList每次對size增長50%.

HashTable
Hashtable繼承於Dictionary字典,實現Map接口。鍵、值都不能是空對象,可以多次訪問,映射元素的順序相同;線程安全,使用hash算法 ,Hashtable則直接利用key本身的hash碼來做驗證,數據遍曆的方式 Iterator (支持fast-fail)和 Enumeration (不支持fast-fail)
缺省初始長度為11,內部都為抽象方法,需要 它的實現類一一作自己的實現。

HashMap
HashMap繼承於AbstractMap抽象類。鍵和值都可以是空對象。多次訪問,映射元素的順序可能不同。非線程安全 HashMap可以通過下麵的語句進行同步: Map m = Collections.synchronizeMap(hashMap);
檢測是否含有key時,HashMap內部需要將key的hash碼重新計算一邊再檢測。數據遍曆的方式 Iterator (支持fast-fail)。缺省初始長度為16,其內部已經實現了Map所需 要做的大部分工作, 它的子類隻需要實現它的少量方法。

Hashmap繼承於AbstractMap,實現了Map、Cloneable、java.io.Serializable接口。它的key、value都可以為null,映射不是有序的。      
Hashmap不是同步的,如果想要線程安全的HashMap,可以通過Collections類的靜態方法synchronizedMap獲得線程安全的HashMap。

Map map = Collections.synchronizedMap(new HashMap());

注:除了不同步和允許使用 null 之外,HashMap 類與 Hashtable 大致相同。

hashmap數據結構剖析

Hashmap本質是數組加鏈表。通過key的hashCode來計算hash值的,隻要hashCode相同,計算出來的hash值就一樣,然後再計算出數組下標,如果多個key對應到同一個下標,就用鏈表串起來,新插入的在前麵。
這裏寫圖片描述

國內一線互聯網公司內部麵試題庫

以下麵試題來自於百度、小米、樂視、美團、58、獵豹、360、新浪、搜狐內部題庫

熟悉本文中列出的知識點會大大增加通過前兩輪技術麵試的幾率。

歡迎一線公司員工提交內部麵試題庫,歡迎star。

目錄

java

接口的意義-百度

規範、擴展、回調

抽象類的意義-樂視

為其子類提供一個公共的類型
封裝子類中得重複內容
定義抽象方法,子類雖然有不同的實現 但是定義是一致的

內部類的作用-樂視

  1. 內部類可以用多個實例,每個實例都有自己的狀態信息,並且與其他外圍對象的信息相互獨立。
  2. 在單個外圍類中,可以讓多個內部類以不同的方式實現同一個接口,或者繼承同一個類。
  3. 創建內部類對象的時刻並不依賴於外圍類對象的創建。
  4. 內部類並沒有令人迷惑的“is-a”關係,他就是一個獨立的實體。
  5. 內部類提供了更好的封裝,除了該外圍類,其他類都不能訪問

父類的靜態方法能否被子類重寫-獵豹

不能

子類繼承父類後,用相同的靜態方法和非靜態方法,這時非靜態方法覆蓋父類中的方法(即方法重寫),父類的該靜態方法被隱藏(如果對象是父類則調用該隱藏的方法),另外子類可繼承父類的靜態與非靜態方法,至於方法重載我覺得它其中一要素就是在同一類中,不能說父類中的什麼方法與子類裏的什麼方法是方法重載的體現

java排序算法-美團

https://blog.csdn.net/qy1387/article/details/7752973

列舉java的集合和繼承關係-百度-美團

java虛擬機的特性-百度-樂視

Java語言的一個非常重要的特點就是與平台的無關性。而使用Java虛擬機是實現這一特點的關鍵。一般的高級語言如果要在不同的平台上運行,至少需要編譯成不同的目標代碼。而引入Java語言虛擬機後,Java語言在不同平台上運行時不需要重新編譯。Java語言使用模式Java虛擬機屏蔽了與具體平台相關的信息,使得Java語言編譯程序隻需生成在Java虛擬機上運行的目標代碼(字節碼),就可以在多種平台上不加修改地運行。Java虛擬機在執行字節碼時,把字節碼解釋成具體平台上的機器指令執行。

哪些情況下的對象會被垃圾回收機製處理掉-美團-小米

Java 垃圾回收機製最基本的做法是分代回收。內存中的區域被劃分成不同的世代,對象根據其存活的時間被保存在對應世代的區域中。一般的實現是劃分成3個世代:年輕、年老和永久。內存的分配是發生在年輕世代中的。當一個對象存活時間足夠長的時候,它就會被複製到年老世代中。對於不同的世代可以使用不同的垃圾回收算法。進行世代劃分的出發點是對應用中對象存活時間進行研究之後得出的統計規律。一般來說,一個應用中的大部分對象的存活時間都很短。比如局部變量的存活時間就隻在方法的執行過程中。基於這一點,對於年輕世代的垃圾回收算法就可以很有針對性。

進程和線程的區別-獵豹-美團

簡而言之,一個程序至少有一個進程,一個進程至少有一個線程。

線程的劃分尺度小於進程,使得多線程程序的並發性高。

另外,進程在執行過程中擁有獨立的內存單元,而多個線程共享內存,從而極大地提高了程序的運行效率。

線程在執行過程中與進程還是有區別的。每個獨立的線程有一個程序運行的入口、順序執行序列和程序的出口。但是線程不能夠獨立執行,必須依存在應用程序中,由應用程序提供多個線程執行控製。

從邏輯角度來看,多線程的意義在於一個應用程序中,有多個執行部分可以同時執行。但操作係統並沒有將多個線程看做多個獨立的應用,來實現進程的調度和管理以及資源分配。這就是進程和線程的重要區別。

進程是具有一定獨立功能的程序關於某個數據集合上的一次運行活動,進程是係統進行資源分配和調度的一個獨立單位.

線程是進程的一個實體,是CPU調度和分派的基本單位,它是比進程更小的能獨立運行的基本單位.線程自己基本上不擁有係統資源,隻擁有一點在運行中必不可少的資源(如程序計數器,一組寄存器和棧),但是它可與同屬一個進程的其他的線程共享進程所擁有的全部資源.

一個線程可以創建和撤銷另一個線程;同一個進程中的多個線程之間可以並發執行.

進程和線程的主要差別在於它們是不同的操作係統資源管理方式。進程有獨立的地址空間,一個進程崩潰後,在保護模式下不會對其它進程產生影響,而線程隻是一個進程中的不同執行路徑。線程有自己的堆棧和局部變量,但線程之間沒有單獨的地址空間,一個線程死掉就等於整個進程死掉,所以多進程的程序要比多線程的程序健壯,但在進程切換時,耗費資源較大,效率要差一些。但對於一些要求同時進行並且又要共享某些變量的並發操作,隻能用線程,不能用進程。如果有興趣深入的話,我建議你們看看《現代操作係統》或者《操作係統的設計與實現》。對就個問題說得比較清楚。

java中==和equals和hashCode的區別-樂視

https://blog.csdn.net/tiantiandjava/article/details/46988461

常見的排序算法時間複雜度-小米

HashMap的實現原理-美團

  1. HashMap概述: HashMap是基於哈希表的Map接口的非同步實現。此實現提供所有可選的映射操作,並允許使用null值和null鍵。此類不保證映射的順序,特別是它不保證該順序恒久不變。
  2. HashMap的數據結構: 在java編程語言中,最基本的結構就是兩種,一個是數組,另外一個是模擬指針(引用),所有的數據結構都可以用這兩個基本結構來構造的,HashMap也不例外。HashMap實際上是一個“鏈表散列”的數據結構,即數組和鏈表的結合體。

從上圖中可以看出,HashMap底層就是一個數組結構,數組中的每一項又是一個鏈表。當新建一個HashMap的時候,就會初始化一個數組。

狀態機

https://www.jdon.com/designpatterns/designpattern_State.htm

int-char-long各占多少字節數

byte 位數 8 字節數 1

short 16 2

int 32 4

long 64 8

float 32 4

double 64 8

char 16 2

int與integer的區別

https://www.cnblogs.com/shenliang123/archive/2011/10/27/2226903.html

string-stringbuffer-stringbuilder區別-小米-樂視-百度

String 字符串常量

StringBuffer 字符串變量(線程安全)

StringBuilder 字符串變量(非線程安全)

簡要的說, String 類型和 StringBuffer 類型的主要性能區別其實在於 String 是不可變的對象, 因此在每次對 String 類型進行改變的時候其實都等同於生成了一個新的 String 對象,然後將指針指向新的 String 對象,所以經常改變內容的字符串最好不要用String ,因為每次生成對象都會對係統性能產生影響,特別當內存中無引用對象多了以後,JVM 的 GC 就會開始工作,那速度是一定會相當慢的。

而如果是使用 StringBuffer 類則結果就不一樣了,每次結果都會對 StringBuffer 對象本身進行操作,而不是生成新的對象,再改變對象引用。所以在一般情況下我們推薦使用 StringBuffer ,特別是字符串對象經常改變的情況下。而在某些特別情況下, String 對象的字符串拚接其實是被 JVM 解釋成了 StringBuffer 對象的拚接,所以這些時候 String 對象的速度並不會比 StringBuffer 對象慢,而特別是以下的字符串對象生成中, String 效率是遠要比 StringBuffer 快的:

String S1 = "This is only a" + "simple" + " test";
StringBuffer Sb = new StringBuffer("This is only a").append("simple").append("test");

你會很驚訝的發現,生成 String S1 對象的速度簡直太快了,而這個時候 StringBuffer 居然速度上根本一點都不占優勢。其實這是 JVM 的一個把戲,在 JVM 眼裏,這個
String S1 = “This is only a” + “ simple” + “test”; 其實就是:
String S1 = “This is only a simple test”; 所以當然不需要太多的時間了。但大家這裏要注意的是,如果你的字符串是來自另外的 String 對象的話,速度就沒那麼快了,譬如:
String S2 = “This is only a”;
String S3 = “ simple”;
String S4 = “ test”;
String S1 = S2 +S3 + S4;
這時候 JVM 會規規矩矩的按照原來的方式去做

在大部分情況下 StringBuffer > String

StringBuffer

Java.lang.StringBuffer線程安全的可變字符序列。一個類似於 String 的字符串緩衝區,但不能修改。雖然在任意時間點上它都包含某種特定的字符序列,但通過某些方法調用可以改變該序列的長度和內容。

可將字符串緩衝區安全地用於多個線程。可以在必要時對這些方法進行同步,因此任意特定實例上的所有操作就好像是以串行順序發生的,該順序與所涉及的每個線程進行的方法調用順序一致。

StringBuffer 上的主要操作是 append 和 insert 方法,可重載這些方法,以接受任意類型的數據。每個方法都能有效地將給定的數據轉換成字符串,然後將該字符串的字符追加或插入到字符串緩衝區中。append 方法始終將這些字符添加到緩衝區的末端;而 insert 方法則在指定的點添加字符。

例如,如果 z 引用一個當前內容是“start”的字符串緩衝區對象,則此方法調用 z.append("le") 會使字符串緩衝區包含“startle”,而 z.insert(4, "le") 將更改字符串緩衝區,使之包含“starlet”。

在大部分情況下 StringBuilder > StringBuffer

java.lang.StringBuilder

java.lang.StringBuilder一個可變的字符序列是5.0新增的。此類提供一個與 StringBuffer 兼容的 API,但不保證同步。該類被設計用作 StringBuffer 的一個簡易替換,用在字符串緩衝區被單個線程使用的時候(這種情況很普遍)。如果可能,建議優先采用該類,因為在大多數實現中,它比 StringBuffer 要快。兩者的方法基本相同

java多態-樂視

Java多態性理解

Java中多態性的實現

什麼是多態

麵向對象的三大特性:封裝、繼承、多態。從一定角度來看,封裝和繼承幾乎都是為多態而準備的。這是我們最後一個概念,也是最重要的知識點。

多態的定義:指允許不同類的對象對同一消息做出響應。即同一消息可以根據發送對象的不同而采用多種不同的行為方式。(發送消息就是函數調用)

實現多態的技術稱為:動態綁定(dynamic binding),是指在執行期間判斷所引用對象的實
際類型,根據其實際的類型調用其相應的方法。

多態的作用:消除類型之間的耦合關係。

現實中,關於多態的例子不勝枚舉。比方說按下 F1 鍵這個動作,如果當前在 Flash 界麵下彈出的就是 AS 3 的幫助文檔;如果當前在 Word 下彈出的就是 Word 幫助;在 Windows 下彈出的就是 Windows 幫助和支持。同一個事件發生在不同的對象上會產生不同的結果。
下麵是多態存在的三個必要條件,要求大家做夢時都能背出來!

多態存在的三個必要條件
一、要有繼承;
二、要有重寫;
三、父類引用指向子類對象。

多態的好處:

1.可替換性(substitutability)。多態對已存在代碼具有可替換性。例如,多態對圓Circle類工作,對其他任何圓形幾何體,如圓環,也同樣工作。

2.可擴充性(extensibility)。多態對代碼具有可擴充性。增加新的子類不影響已存在類的多態性、繼承性,以及其他特性的運行和操作。實際上新加子類更容易獲得多態功能。例如,在實現了圓錐、半圓錐以及半球體的多態基礎上,很容易增添球體類的多態性。

3.接口性(interface-ability)。多態是超類通過方法簽名,向子類提供了一個共同接口,由子類來完善或者覆蓋它而實現的。如圖8.3 所示。圖中超類Shape規定了兩個實現多態的接口方法,computeArea()以及computeVolume()。子類,如Circle和Sphere為了實現多態,完善或者覆蓋這兩個接口方法。

4.靈活性(flexibility)。它在應用中體現了靈活多樣的操作,提高了使用效率。

5.簡化性(simplicity)。多態簡化對應用軟件的代碼編寫和修改過程,尤其在處理大量對象的運算和操作時,這個特點尤為突出和重要。

Java中多態的實現方式:接口實現,繼承父類進行方法重寫,同一個類中進行方法重載。

什麼導致線程阻塞-58-美團

線程的阻塞

為了解決對共享存儲區的訪問衝突,Java 引入了同步機製,現在讓我們來考察多個線程對共享資源的訪問,顯然同步機製已經不夠了,因為在任意時刻所要求的資源不一定已經準備好了被訪問,反過來,同一時刻準備好了的資源也可能不止一個。為了解決這種情況下的訪問控製問題,Java 引入了對阻塞機製的支持.

阻塞指的是暫停一個線程的執行以等待某個條件發生(如某資源就緒),學過操作係統的同學對它一定已經很熟悉了。Java 提供了大量方法來支持阻塞,下麵讓我們逐一分析。

  1. sleep() 方法:sleep() 允許 指定以毫秒為單位的一段時間作為參數,它使得線程在指定的時間內進入阻塞狀態,不能得到CPU 時間,指定的時間一過,線程重新進入可執行狀態。 典型地,sleep() 被用在等待某個資源就緒的情形:測試發現條件不滿足後,讓線程阻塞一段時間後重新測試,直到條件滿足為止。
  2. suspend() 和 resume() 方法:兩個方法配套使用,suspend()使得線程進入阻塞狀態,並且不會自動恢複,必須其對應的resume() 被調用,才能使得線程重新進入可執行狀態。典型地,suspend() 和 resume() 被用在等待另一個線程產生的結果的情形:測試發現結果還沒有產生後,讓線程阻塞,另一個線程產生了結果後,調用 resume() 使其恢複。
  3. yield() 方法:yield() 使得線程放棄當前分得的 CPU 時間,但是不使線程阻塞,即線程仍處於可執行狀態,隨時可能再次分得 CPU 時間。調用 yield() 的效果等價於調度程序認為該線程已執行了足夠的時間從而轉到另一個線程.
  4. wait() 和 notify() 方法:兩個方法配套使用,wait() 使得線程進入阻塞狀態,它有兩種形式,一種允許 指定以毫秒為單位的一段時間作為參數,另一種沒有參數,前者當對應的 notify() 被調用或者超出指定時間時線程重新進入可執行狀態,後者則必須對應的 notify() 被調用.

初看起來它們與 suspend() 和 resume() 方法對沒有什麼分別,但是事實上它們是截然不同的。區別的核心在於,前麵敘述的所有方法,阻塞時都不會釋放占用的鎖(如果占用了的話),而這一對方法則相反。

上述的核心區別導致了一係列的細節上的區別。

首先,前麵敘述的所有方法都隸屬於 Thread 類,但是這一對卻直接隸屬於 Object 類,也就是說,所有對象都擁有這一對方法。初看起來這十分不可思議,但是實際上卻是很自然的,因為這一對方法阻塞時要釋放占用的鎖,而鎖是任何對象都具有的,調用任意對象的 wait() 方法導致線程阻塞,並且該對象上的鎖被釋放。而調用 任意對象的notify()方法則導致因調用該對象的 wait() 方法而阻塞的線程中隨機選擇的一個解除阻塞(但要等到獲得鎖後才真正可執行)。

其次,前麵敘述的所有方法都可在任何位置調用,但是這一對方法卻必須在 synchronized 方法或塊中調用,理由也很簡單,隻有在synchronized 方法或塊中當前線程才占有鎖,才有鎖可以釋放。同樣的道理,調用這一對方法的對象上的鎖必須為當前線程所擁有,這樣才有鎖可以釋放。因此,這一對方法調用必須放置在這樣的 synchronized 方法或塊中,該方法或塊的上鎖對象就是調用這一對方法的對象。若不滿足這一條件,則程序雖然仍能編譯,但在運行時會出現IllegalMonitorStateException 異常。

wait() 和 notify() 方法的上述特性決定了它們經常和synchronized 方法或塊一起使用,將它們和操作係統的進程間通信機製作一個比較就會發現它們的相似性:synchronized方法或塊提供了類似於操作係統原語的功能,它們的執行不會受到多線程機製的幹擾,而這一對方法則相當於 block 和wakeup 原語(這一對方法均聲明為 synchronized)。它們的結合使得我們可以實現操作係統上一係列精妙的進程間通信的算法(如信號量算法),並用於解決各種複雜的線程間通信問題。

關於 wait() 和 notify() 方法最後再說明兩點:

第一:調用 notify() 方法導致解除阻塞的線程是從因調用該對象的 wait() 方法而阻塞的線程中隨機選取的,我們無法預料哪一個線程將會被選擇,所以編程時要特別小心,避免因這種不確定性而產生問題。

第二:除了 notify(),還有一個方法 notifyAll() 也可起到類似作用,唯一的區別在於,調用 notifyAll() 方法將把因調用該對象的 wait() 方法而阻塞的所有線程一次性全部解除阻塞。當然,隻有獲得鎖的那一個線程才能進入可執行狀態。

談到阻塞,就不能不談一談死鎖,略一分析就能發現,suspend() 方法和不指定超時期限的 wait() 方法的調用都可能產生死鎖。遺憾的是,Java 並不在語言級別上支持死鎖的避免,我們在編程中必須小心地避免死鎖。

以上我們對 Java 中實現線程阻塞的各種方法作了一番分析,我們重點分析了 wait() 和 notify() 方法,因為它們的功能最強大,使用也最靈活,但是這也導致了它們的效率較低,較容易出錯。實際使用中我們應該靈活使用各種方法,以便更好地達到我們的目的。

抽象類接口區別-360

  1. 默認的方法實現
    抽象類可以有默認的方法實現完全是抽象的。接口根本不存在方法的實現

  2. 實現
    子類使用extends關鍵字來繼承抽象類。如果子類不是抽象類的話,它需要提供抽象類中所有聲明的方法的實現。
    子類使用關鍵字implements來實現接口。它需要提供接口中所有聲明的方法的實現

  3. 構造器
    抽象類可以有構造器
    接口不能有構造器

  4. 與正常Java類的區別
    除了你不能實例化抽象類之外,它和普通Java類沒有任何區
    接口是完全不同的類型

  5. 訪問修飾符
    抽象方法可以有public、protected和default這些修飾符
    接口方法默認修飾符是public。你不可以使用其它修飾符。

  6. main方法
    抽象方法可以有main方法並且我們可以運行它
    接口沒有main方法,因此我們不能運行它。

  7. 多繼承
    抽象類在java語言中所表示的是一種繼承關係,一個子類隻能存在一個父類,但是可以存在多個接口。

  8. 速度
    它比接口速度要快
    接口是稍微有點慢的,因為它需要時間去尋找在類中實現的方法。

  9. 添加新方法
    如果你往抽象類中添加新的方法,你可以給它提供默認的實現。因此你不需要改變你現在的代碼。
    如果你往接口中添加方法,那麼你必須改變實現該接口的類。

容器類之間的區別-樂視-美團

https://www.cnblogs.com/yuanermen/archive/2009/08/05/1539917.html
https://alexyyek.github.io/2015/04/06/Collection/
https://tianmaying.com/tutorial/java_collection

內部類

https://www.cnblogs.com/chenssy/p/3388487.html

hashmap和hashtable的區別-樂視-小米

https://www.233.com/ncre2/JAVA/jichu/20100717/084230917.html

ArrayMap對比HashMap

https://lvable.com/?p=217

Android

如何導入外部數據庫

把原數據庫包括在項目源碼的 res/raw

android係統下數據庫應該存放在 /data/data/com.*.*(package name)/ 目錄下,所以我們需要做的是把已有的數據庫傳入那個目錄下.操作方法是用FileInputStream讀取原數據庫,再用FileOutputStream把讀取到的東西寫入到那個目錄.

本地廣播和全局廣播有什麼差別

因廣播數據在本應用範圍內傳播,不用擔心隱私數據泄露的問題。
不用擔心別的應用偽造廣播,造成安全隱患。
相比在係統內發送全局廣播,它更高效。

intentService作用是什麼,AIDL解決了什麼問題-小米

生成一個默認的且與主線程互相獨立的工作者線程來執行所有傳送至onStartCommand() 方法的Intetnt。

生成一個工作隊列來傳送Intent對象給你的onHandleIntent()方法,同一時刻隻傳送一個Intent對象,這樣一來,你就不必擔心多線程的問題。在所有的請求(Intent)都被執行完以後會自動停止服務,所以,你不需要自己去調用stopSelf()方法來停止。

該服務提供了一個onBind()方法的默認實現,它返回null

提供了一個onStartCommand()方法的默認實現,它將Intent先傳送至工作隊列,然後從工作隊列中每次取出一個傳送至onHandleIntent()方法,在該方法中對Intent對相應的處理。

AIDL (Android Interface Definition Language) 是一種IDL 語言,用於生成可以在Android設備上兩個進程之間進行進程間通信(interprocess communication, IPC)的代碼。如果在一個進程中(例如Activity)要調用另一個進程中(例如Service)對象的操作,就可以使用AIDL生成可序列化的參數。
AIDL IPC機製是麵向接口的,像COM或Corba一樣,但是更加輕量級。它是使用代理類在客戶端和實現端傳遞數據。

Activity/Window/View三者的差別,fragment的特點-360

Activity像一個工匠(控製單元),Window像窗戶(承載模型),View像窗花(顯示視圖)
LayoutInflater像剪刀,Xml配置像窗花圖紙。

  1. 在Activity中調用attach,創建了一個Window
  2. 創建的window是其子類PhoneWindow,在attach中創建PhoneWindow
  3. 在Activity中調用setContentView(R.layout.xxx)
  4. 其中實際上是調用的getWindow().setContentView()
  5. 調用PhoneWindow中的setContentView方法
  6. 創建ParentView:
作為ViewGroup的子類,實際是創建的DecorView(作為FramLayout的子類)
  7. 將指定的R.layout.xxx進行填充
通過布局填充器進行填充【其中的parent指的就是DecorView】
  8. 調用到ViewGroup
  9. 調用ViewGroup的removeAllView(),先將所有的view移除掉
  10. 添加新的view:addView()

fragment 特點

  • Fragment可以作為Activity界麵的一部分組成出現;
  • 可以在一個Activity中同時出現多個Fragment,並且一個Fragment也可以在多個Activity中使用;
  • 在Activity運行過程中,可以添加、移除或者替換Fragment;
  • Fragment可以響應自己的輸入事件,並且有自己的生命周期,它們的生命周期會受宿主Activity的生命周期影響。

描述一次網絡請求的流程-新浪

Handler,Thread和HandlerThread的差別-小米

https://blog.csdn.net/guolin_blog/article/details/9991569

https://droidyue.com/blog/2015/11/08/make-use-of-handlerthread/

從Android中Thread(java.lang.Thread -> java.lang.Object)描述可以看出,Android的Thread沒有對Java的Thread做任何封裝,但是Android提供了一個繼承自Thread的類HandlerThread(android.os.HandlerThread -> java.lang.Thread),這個類對Java的Thread做了很多便利Android係統的封裝。

android.os.Handler可以通過Looper對象實例化,並運行於另外的線程中,Android提供了讓Handler運行於其它線程的線程實現,也是就HandlerThread。HandlerThread對象start後可以獲得其Looper對象,並且使用這個Looper對象實例Handler。

低版本SDK實現高版本api-小米

自己實現或@TargetApi annotation

Ubuntu編譯安卓係統-百度

  1. 進入源碼根目錄
  2. . build/envsetup.sh
  3. lunch
  4. full(編譯全部)
  5. userdebug(選擇編譯版本)
  6. make -j8(開啟8個線程編譯)

LaunchMode應用場景-百度-小米-樂視

standard,創建一個新的Activity。

singleTop,棧頂不是該類型的Activity,創建一個新的Activity。否則,onNewIntent。

singleTask,回退棧中沒有該類型的Activity,創建Activity,否則,onNewIntent+ClearTop。

注意:

  1. 設置了"singleTask"啟動模式的Activity,它在啟動的時候,會先在係統中查找屬性值affinity等於它的屬性值taskAffinity的Task存在; 如果存在這樣的Task,它就會在這個Task中啟動,否則就會在新的任務棧中啟動。因此, 如果我們想要設置了"singleTask"啟動模式的Activity在新的任務中啟動,就要為它設置一個獨立的taskAffinity屬性值。
  2. 如果設置了"singleTask"啟動模式的Activity不是在新的任務中啟動時,它會在已有的任務中查看是否已經存在相應的Activity實例, 如果存在,就會把位於這個Activity實例上麵的Activity全部結束掉,即最終這個Activity 實例會位於任務的Stack頂端中。
  3. 在一個任務棧中隻有一個”singleTask”啟動模式的Activity存在。他的上麵可以有其他的Activity。這點與singleInstance是有區別的。

singleInstance,回退棧中,隻有這一個Activity,沒有其他Activity。

singleTop適合接收通知啟動的內容顯示頁麵。

例如,某個新聞客戶端的新聞內容頁麵,如果收到10個新聞

最後更新:2017-09-18 22:32:58

  上一篇:go  DRDS異構索引測試
  下一篇:go  如何配置通過ODBC方式連接Deepgreen數據庫