1. 程式人生 > >你應該擁有的面試題及答案

你應該擁有的面試題及答案

Q:String、StringBuffer和StringBuilder的區別?

String一旦初始化就不可以改變,StringBuffer和StringBuilder可變
StringBuffer是執行緒安全的,StringBuilder不是執行緒安全的(效率高)

Q:String a="“和String a=new String(”")的的關係和異同?

String str = “a”; 這個只是一個引用,記憶體中如果有“a"的話,str就指向它;如果沒有,才建立它;
如果你以後還用到"a"這個字串的話並且是這樣用:
String str1 = “a”; String str2 = “a”; String str2 = “a”; 這4個變數都共享一個字串"a"。
而String str = new String(“a”);是根據"a"這個String物件再次構造一個String物件,將新構造出來的String物件的引用賦給str。

Q:Object的equal()和==的區別?

基本型別之間比較是用==,引用型別的相等使用的是equals
==比較的是地址是否相同,equals比較的是值是否相同

String a="hello"; 
String b="hello"; 
String c=new String("hello");
System.out.println(a==b);//true
System.out.println(a==c);//false

System.out.println(a.equal(c))//true

為什麼會出現這樣的結果呢?

分析:這三個引用指向的的物件的內容都是相同的,從邏輯上面來說都是相等的。但是確實出現了上面的這種結果。那是因為==比的是物件的地址,對於同一個常量來說,它的記憶體地址都是一樣的,而c是指向新開除來記憶體,所以a= =c是false,而a.equal(b)比的是物件的內容,與物件所在的地址無關,所以a.equal©是true。

注意:

物件的equal方法內也是呼叫了==,String的equal方法可以進行比較是因為重寫了equal方法,如果不重寫效果和= = 是一樣的。

那為什麼在重寫equals方法時都要重寫hashCode方法呢:
equals與hashcode間的關係是這樣的:

1、如果兩個物件相同(即用equals比較返回true),那麼它們的hashCode值一定要相同;
2、如果兩個物件的hashCode相同,它們並不一定相同(即用equals比較返回false)

從別人那借來的理解:

由於為了提高程式的效率才實現了hashcode方法,先進行hashcode的比較,如果不同,那沒就不必在進行equals的比較了,這樣就大大減少了equals比較的次數,這對比需要比較的數量很大的效率提高是很明顯的,一個很好的例子就是在集合中的使用;
我們都知道java中的List集合是有序的,因此是可以重複的,而set集合是無序的,因此是不能重複的,那麼怎麼能保證不能被放入重複的元素呢,但靠equals方法一樣比較的話,如果原來集合中以後又10000個元素了,那麼放入10001個元素,難道要將前面的所有元素都進行比較,看看是否有重複,歐碼噶的,這個效率可想而知,因此hashcode就應遇而生了,java就採用了hash表,利用雜湊演算法(也叫雜湊演算法),就是將物件資料根據該物件的特徵使用特定的演算法將其定義到一個地址上,那麼在後面定義進來的資料只要看對應的hashcode地址上是否有值,那麼就用equals比較,如果沒有則直接插入,只要就大大減少了equals的使用次數,執行效率就大大提高了。
繼續上面的話題,為什麼必須要重寫hashcode方法,其實簡單的說就是為了保證同一個物件,保證在equals相同的情況下hashcode值必定相同,如果重寫了equals而未重寫hashcode方法,可能就會出現兩個沒有關係的物件equals相同的(因為equal都是根據物件的特徵進行重寫的),但hashcode確實不相同的。

Q:裝箱、拆箱什麼含義?

裝箱:值型別轉為引用型別,拆箱則與之相反

Q:int和Integer的區別?

Integer是int的包裝類,int的初值為0,Integer的初值為null
注意:

1.無論如何,Integer與new
Integer不會相等。不會經歷拆箱過程,new出來的物件存放在堆,而非new的Integer常量則在常量池(在方法區),他們的記憶體地址不一樣,所以為false。
2.兩個都是非new出來的Integer,如果數在-128到127之間,則是true,否則為false。因為java在編譯Integer
i2 = 128的時候,被翻譯成:Integer i2 =
Integer.valueOf(128);而valueOf()函式會對-128到127之間的數進行快取。
3.兩個都是new出來的,都為false。還是記憶體地址不一樣。
4.int和Integer(無論new否)比,都為true,因為會把Integer自動拆箱為int再去比。

例:

int i = 128;
Integer i2 = 128;
Integer i3 = new Integer(128);
System.out.println(i == i2); //Integer會自動拆箱為int,所以為true
System.out.println(i == i3); //true,理由同上

System.out.println(i2 == i3); //false,不是同一個物件記憶體地址不同,看1

Integer i4 = 127;//編譯時被翻譯成:Integer i4 = Integer.valueOf(127);
Integer i5 = 127;
System.out.println(i4 == i5);//true,兩個都是非new出來的Integer,如果數在-128到127之間會進行快取
Integer i6 = 128;
Integer i7 = 128;
System.out.println(i6 == i7);//false

Integer i9 = new Integer(128);
Integer i10 = new Integer(128);
System.out.println(i9 == i10);  //false兩個new出來,地址肯定不同

Q:final、finally、finalize()分別表示什麼含義?

final是修飾變數或者類或者方法的,修飾的變數是常量,修飾的類不能被繼承,修飾的方法不可以被重寫
finally是try…catch…finally後的必須執行的一個程式碼塊

注意:關於finally和return的說法:

碰到try語句中的return,先把值儲存到一個地方,然後尋找finally語句,如果語句中有新的演算法,就從那個空間取出這個值進行運算,finally中有return的話就就把“新值”覆蓋那個空間的“舊值”,並最終返回;如果finally中沒有return就直接將那個空間中的“舊值”取出來然後返還回去。

Q:重寫和過載的區別?

重寫必須是繼承關係下才可能發生的,重寫的方法名,引數必須相同,返回值也必須相同
過載是在一個類中,方法名相同,引數(個數或者型別)不同,返回值沒有要求可相同可不同

override(重寫)   
1、方法名、引數、返回值相同。   
2、子類方法不能縮小父類方法的訪問許可權。   
3、子類方法不能丟擲比父類方法更多的異常(但子類方法可以不丟擲異常)。
4、存在於父類和子類之間。   
5、方法被定義為final不能被重寫。  
overload(過載)   
1、引數型別、個數、順序至少有一個不相同。
2、不能過載只有返回值不同的方法名。   
3、存在於父類和子類、同類中。

Q:抽象類和介面的異同?

1、抽象類和介面都不能直接例項化,如果要例項化,抽象類變數必須指向實現所有抽象方法的子類物件,介面變數必須指向實現所有介面方法的類物件。
2、抽象類要被子類繼承,介面要被類實現。
3、介面只能做方法申明,抽象類中可以做方法申明,也可以做方法實現
4、接口裡定義的變數只能是公共的靜態的常量,抽象類中的變數是普通變數。
5、抽象類裡的抽象方法必須全部被子類所實現,如果子類不能全部實現父類抽象方法,那麼該子類只能是抽象類。同樣,一個實現介面的時候,如不能全部實現介面方法,那麼該類也只能為抽象類。
6、抽象方法只能申明,不能實現,介面是設計的結果 ,抽象類是重構的結果
7、抽象類裡可以沒有抽象方法
8、如果一個類裡有抽象方法,那麼這個類只能是抽象類
9、抽象方法要被實現,所以不能是靜態的,也不能是私有的。
10、介面可繼承介面,並可多繼承介面,但類只能單根繼承。

Q:為什麼匿名內部類中使用區域性變數要用final修飾?

  1. 這裡所說的“匿名內部類”主要是指在其外部類的成員方法內定義,同時完成例項化的類,若其訪問該成員方法中的區域性變數,區域性變數必須要被final修飾。
  2. 原因是編譯程式實現上的困難:內部類物件的生命週期會超過區域性變數的生命週期。區域性變數的生命週期:當該方法被呼叫時,該方法中的區域性變數在棧中被建立,當方法呼叫結束時,退棧,這些區域性變數全部死亡。而內部類物件生命週期與其它類一樣:自建立一個匿名內部類物件,系統為該物件分配記憶體,直到沒有引用變數指向分配給該物件的記憶體,它才會死亡(被JVM垃圾回收)。所以完全可能出現的一種情況是:成員方法已呼叫結束,區域性變數已死亡,但匿名內部類的物件仍然活著。
  3. 如果匿名內部類的物件訪問了同一個方法中的區域性變數,就要求只要匿名內部類物件還活著,那麼棧中的那些它要所訪問的區域性變數就不能“死亡”。
  4. 解決方法:匿名內部類物件可以訪問同一個方法中被定義為final型別的區域性變數。定義為final後,編譯程式的實現方法:對於匿名內部類物件要訪問的所有final型別區域性變數,都拷貝成為該物件中的一個數據成員,也就是說這個final修飾的變數在堆中了,之前的區域性變數在棧中(不是自己過來的,是拷貝過來的)。這樣,即使棧中區域性變數已死亡,但被定義為final型別的區域性變數的值永遠不變,因而匿名內部類物件在區域性變數死亡後,照樣可以訪問final型別的區域性變數,因為它自己拷貝了一份,且與原區域性變數的值始終一致。

Q:什麼是反射,有什麼作用和應用?

JAVA反射機制是在執行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個物件,都能夠呼叫它的任意一個方法和屬性;這種動態獲取的資訊以及動態呼叫物件的方法的功能稱為java語言的反射機制。要想解剖一個類,必須先要獲取到該類的位元組碼檔案物件。而解剖使用的就是Class類中的方法.所以先要獲取到每一個位元組碼檔案對應的Class型別的物件.

Q:什麼是內部類?有什麼作用?靜態內部類和非靜態內部類的區別?

內部類分為四種:成員內部類、區域性內部類、匿名內部類和靜態內部類。

成員內部類:

成員內部類是最普通的內部類,它的定義為位於另一個類的內部。成員內部類可以無條件訪問外部類的所有成員屬性和成員方法(包括private成員和靜態成員)

注意:當成員內部類擁有和外部類同名的成員變數或者方法時,會發生隱藏現象,即預設情況下訪問的是成員內部類的成員。如果要訪問外部類的同名成員,需要以下面的形式進行訪問:

外部類.this.成員變數
外部類.this.成員方法

在外部類中如果要訪問成員內部類的成員,必須先建立一個成員內部類的物件,再通過指向這個物件的引用來訪問

成員內部類是依附外部類而存在的,也就是說,如果要建立成員內部類的物件,前提是必須存在一個外部類的物件。建立成員內部類物件的一般方式如下:

 Outter outter = new Outter();
 Outter.Inner inner = outter.new Inner();  //必須通過Outter物件來建立

區域性內部類:

區域性內部類是定義在一個方法或者一個作用域裡面的類,它和成員內部類的區別在於區域性內部類的訪問僅限於方法內或者該作用域內。區域性內部類就像是方法裡面的一個區域性變數一樣,是不能有public、protected、private以及static修飾符的。

匿名內部類:

匿名內部類也是不能有訪問修飾符和static修飾符的。匿名內部類是唯一一種沒有構造器的類。正因為其沒有構造器,所以匿名內部類的使用範圍非常有限,大部分匿名內部類用於介面回撥。匿名內部類在編譯的時候由系統自動起名為Outter$1.class。一般來說,匿名內部類用於繼承其他類或是實現介面,並不需要增加額外的方法,只是對繼承方法的實現或是重寫。

靜態內部類:

也是定義在另一個類裡面的類,只不過在類的前面多了一個關鍵字static。
靜態內部類是不需要依賴於外部類的,這點和類的靜態成員屬性有點類似,並且它不能使用外部類的非static成員變數或者方法,這點很好理解,因為在沒有外部類的物件的情況下,可以建立靜態內部類的物件,如果允許訪問外部類的非static成員就會產生矛盾,因為外部類的非static成員必須依附於具體的物件。

BroadCastReceiver的相關問題

Q:廣播有幾種形式?什麼特點?

普通廣播:普通廣播是一種完全非同步執行的廣播,在廣播發出之後,所有的廣播接收器幾乎都會在同一時刻接收到這條廣播訊息,因此它們接收的先後是隨機的。另外,接收器不能截斷普通廣播。

有序廣播:有序廣播是一種同步執行的廣播,在廣播發出之後,同一時刻只會有一個廣播接收器能夠收到這條廣播訊息,當這個廣播接收器中的邏輯執行完畢後,廣播才會繼續傳遞,所以此時的廣播接收器是有先後順序的,且優先順序(priority)高的廣播接收器會先收到廣播訊息。有序廣播可以被接收器截斷使得後面的接收器無法收到它。

本地廣播:前面兩個廣播都屬於系統全域性廣播,即發出的廣播可被其他應用程式接收到,且我們也可接收到其他任何應用程式傳送的廣播。為了能夠簡單地解決全域性廣播可能帶來的安全性問題,Android引入了一套本地廣播機制,使用這個機制發出的廣播只能夠在應用程式的內部進行傳遞,並且廣播接收器也只能接收本應用程式發出的廣播。

粘性廣播:
通過Context.sendStickyBroadcast()方法可傳送粘性(sticky)廣播,這種廣播會一直滯留,當有匹配該廣播的接收器被註冊後,該接收器就會收到此條廣播。注意,傳送粘性廣播還需要BROADCAST_STICKY許可權:
<uses-permission android:name="android.permission.BROADCAST_STICKY"/>
sendStickyBroadcast()只保留最後一條廣播,並且一直保留下去,這樣即使已經有廣播接收器處理了該廣播,一旦又有匹配的廣播接收器被註冊,該粘性廣播仍會被接收。如果只想處理一遍該廣播,可通過removeStickyBroadcast()方法來實現。接收粘性廣播的過程和普通廣播是一樣的,就不多介紹了。

Q:廣播的兩種註冊形式?區別在哪?

有兩種註冊方法:
一種在活動裡通過程式碼動態註冊,另一種在配置檔案裡靜態註冊。其實仔細觀察,兩種方式都是完成了對接收器以及它能接收的廣播值這兩個值的定義。
這兩種註冊方法一個區別是:
動態註冊的接收器必須要在程式啟動之後才能接收到廣播,而靜態註冊的接收器即便程式未啟動也能接收到廣播,比如想接收到手機開機完成後系統發出的廣播就只能用靜態註冊了。

自定義接收器類並繼承BroadcastReceiver,然後具體實現onReceive()方法。
注意:

  • 1、BroadcastReceiver生命週期只有十秒左右,因此在onReceive()不要做一些耗時的操作,應該傳送給service,由service來完成;
  • 2、還有onReceive()不要開啟子執行緒。
  • 3、本地廣播是無法通過靜態註冊的方式來接收的,因為靜態註冊主要就是為了讓程式在未啟動的情況下也能收到廣播,而傳送本地廣播時,應用程式肯定已經啟動了,也完全不需要使用靜態註冊的功能。

Q 程序和執行緒的區別:

程序:是資源(CPU、記憶體等)分配的基本單位,它是程式執行時的一個例項。程式執行時系統就會建立一個程序,併為它分配資源,然後把該程序放入程序就緒佇列,程序排程器選中它的時候就會為它分配CPU時間,程式開始真正執行。

執行緒:是程式執行時的最小單位,它是程序的一個執行流,是CPU排程和分派的基本單位,一個程序可以由很多個執行緒組成,執行緒間共享程序的所有資源,每個執行緒有自己的堆疊和區域性變數。執行緒由CPU獨立排程執行,在多CPU環境下就允許多個執行緒同時執行。同樣多執行緒也可以實現併發操作,每個請求分配一個執行緒來處理。

執行緒和程序各自有什麼區別和優劣呢?

  • 程序是資源分配的最小單位,執行緒是程式執行的最小單位。

  • 程序有自己的獨立地址空間,每啟動一個程序,系統就會為它分配地址空間,建立資料表來維護程式碼段、堆疊段和資料段,這種操作非常昂貴。而執行緒是共享程序中的資料的,使用相同的地址空間,因此CPU切換一個執行緒的花費遠比程序要小很多,同時建立一個執行緒的開銷也比程序要小很多。

  • 執行緒之間的通訊更方便,同一程序下的執行緒共享全域性變數、靜態變數等資料,而程序之間的通訊需要以通訊的方式(IPC)進行。不過如何處理好同步與互斥是編寫多執行緒程式的難點。

  • 但是多程序程式更健壯,多執行緒程式只要有一個執行緒死掉,整個程序也死掉了,而一個程序死掉並不會對另外一個程序造成影響,因為程序有自己獨立的地址空間。

Q:同步和非同步、阻塞和非阻塞的概念

同步和非同步關注的是訊息通訊機制
1、同步:發出一個呼叫時,在沒有得到結果之前,該呼叫就不返回,一旦呼叫返回就得到返回值。
2、非同步:呼叫發出之後,這個呼叫就直接返回了,所以沒有返回結果,也就是說,當一個一步過程呼叫發出後,呼叫者不會立即得到結果,而是在呼叫發出後,被呼叫者,通過狀態,通知勒通知呼叫者,或通過回撥函式處理這個呼叫。

例如:
打電話給書店老闆,問有沒有某某書,他說我查一下,等查好了告訴你結果–>同步
打電話給書店老闆,問有沒有某某書,他說我查一下,查到了給你打電話—->非同步

阻塞和非阻塞關注的是程式等待呼叫結果(訊息,返回值)時的狀態
1、阻塞:指呼叫結果返回之前,當前執行緒會被掛起,呼叫執行緒只有在得到結果後才能返回
2、非阻塞:指在不能立刻得到結果之前,該呼叫不會阻塞當前執行緒

例如:
還是打電話給書店老闆,問有沒有某某書,如果你是阻塞式呼叫,你會一直把自己“掛起”,直到得到有沒有這本書的結果,
如果是非阻塞式呼叫,你不管老闆有沒有告訴你,你自己先一邊玩去了

Q:執行緒的有哪些狀態?

1.新建狀態(New):

當用new操作符建立一個執行緒時, 例如new Thread,執行緒還沒有開始執行,此時執行緒處在新建狀態。
當一個執行緒處於新生狀態時,程式還沒有開始執行執行緒中的程式碼.

2.就緒狀態(Runnable)

一個新建立的執行緒並不自動開始執行,要執行執行緒,必須呼叫執行緒的start()方法。當執行緒物件呼叫start()方法即啟動了執行緒,start()方法建立執行緒執行的系統資源,並排程執行緒執行run()方法。當start()方法返回後,執行緒就處於就緒狀態。 
處於就緒狀態的執行緒並不一定立即執行run()方法,執行緒還必須同其他執行緒競爭CPU時間,只有獲得CPU時間才可以執行執行緒。因為在單CPU的計算機系統中,不可能同時執行多個執行緒,一個時刻僅有一個執行緒處於執行狀態。因此此時可能有多個執行緒處於就緒狀態。對多個處於就緒狀態的執行緒是由Java執行時系統的執行緒排程程式(thread scheduler)來排程的。

3.執行狀態(Running)

當執行緒獲得CPU時間後,它才進入執行狀態,真正開始執行run()方法.

4. 阻塞狀態(Blocked)

執行緒執行過程中,可能由於各種原因進入阻塞狀態: 
1.執行緒通過呼叫sleep方法進入睡眠狀態; 
2.執行緒呼叫一個在I/O上被阻塞的操作,即該操作在輸入輸出操作完成之前不會返回到它的呼叫者; 
3.執行緒試圖得到一個鎖,而該鎖正被其他執行緒持有; 
4.執行緒在等待某個觸發條件; 
所謂阻塞狀態是正在執行的執行緒沒有執行結束,暫時讓出CPU,這時其他處於就緒狀態的執行緒就可以獲得CPU時間,進入執行狀態。

5. 死亡狀態(Dead)

有兩個原因會導致執行緒死亡: 
1) run方法正常退出而自然死亡, 
2) 一個未捕獲的異常終止了run方法而使執行緒猝死。 

執行緒的狀態圖

Q:什麼是執行緒安全?保障執行緒安全有哪些手段?

首先需要理解執行緒安全的兩個方面:執行控制和記憶體可見。
執行控制的目的是控制程式碼執行(順序)及是否可以併發執行。
記憶體可見控制的是執行緒執行結果在記憶體中對其它執行緒的可見性。根據Java記憶體模型的實現,執行緒在具體執行時,會先拷貝主存資料到執行緒本地(CPU快取),操作完成後再把結果從執行緒本地刷到主存。

就是多執行緒訪問同一程式碼,不會產生不確定結果。(比如死鎖) 
如何保證呢:
1、使用執行緒安全的類 
2、使用synchronized同步程式碼塊,或者用Lock鎖 
3、多執行緒併發情況下,執行緒共享的變數改為方法區域性級變數

Q:ReentrantLock和synchronized的區別?

(這個問題本人不會,直接百度的)
區別一:API層面

synchronized使用 synchronized既可以修飾方法,也可以修飾程式碼塊。 synchronized修飾方法時,如下所示:

synchronized修飾一個方法時,這個方法叫同步方法。

 public synchronized void test() {
    //方法體 
    }

synchroized修飾程式碼塊時,包含兩部分:鎖物件的引用和這個鎖保護的程式碼塊。

synchronized(Object) {
//括號中表示需要鎖的物件.
//執行緒執行的時候會對Object上鎖
}

ReentrantLock使用

public class test(){
    private Lock lock = new ReentrantLock();

    public void testMethod()
    {
        try
        {
            lock.lock();
            ```
            //省略

        }
        finally
        {
            lock.unlock();
        }
    }
}

區別二:等待可中斷
引用周志明的《深入理解Java虛擬機器》Page 392

等待可中斷是指當持有鎖的執行緒長期不釋放鎖的時候,正在等待的執行緒可以選擇放棄等待,改為處理其他事情。可等待特性對處理執行時間非常長的同步快很有幫助。

具體來說,假如業務程式碼中有兩個執行緒,Thread1 Thread2。假設 Thread1 獲取了物件object的鎖,Thread2將等待Thread1釋放object的鎖。
1、使用synchronized。如果Thread1不釋放,Thread2將一直等待,不能被中斷。synchronized也可以說是Java提供的原子性內建鎖機制。內部鎖扮演了互斥鎖(mutual exclusion lock ,mutex)的角色,一個執行緒引用鎖的時候,別的執行緒阻塞等待。
2、使用ReentrantLock。如果Thread1不釋放,Thread2等待了很長時間以後,可以中斷等待,轉而去做別的事情。

區別三:公平鎖
引用周志明的《深入理解Java虛擬機器》Page 392

公平鎖是指多個執行緒在等待同一個鎖時,必須按照申請的時間順序來依次獲得鎖;而非公平鎖則不能保證這一點。非公平鎖在鎖被釋放時,任何一個等待鎖的執行緒都有機會獲得鎖。

synchronized的鎖是非公平鎖,ReentrantLock預設情況下也是非公平鎖,但可以通過帶布林值的建構函式要求使用公平鎖

Q:sleep()和wait()的區別?

sleep wait
釋放鎖資源 釋放鎖資源
thread的方法 object的方法
到時間自己醒 必須使用notify喚醒

Q :join和yield的區別

1、 A.join,在API中的解釋是,堵塞當前執行緒B,直到A執行完畢並死掉,再執行B。
2、A.yield,A讓出位置,給B執行,B執行結束A再執行。跟join意思正好相反!

Q:volatile和synchronized區別

  • 1)volatile本質是在告訴jvm當前變數在暫存器中的值是不確定的,需要從主存中讀取,synchronized則是鎖定當前變數,只有當前執行緒可以訪問該變數,其他執行緒被阻塞住.
  • 2)volatile僅能使用在變數級別,synchronized則可以使用在變數,方法.
  • 3)volatile僅能實現變數的修改可見性,而synchronized則可以保證變數的修改可見性和原子性.
  • 4)volatile不會造成執行緒的阻塞,而synchronized可能會造成執行緒的阻塞.
  • 5、當一個域的值依賴於它之前的值時,volatile就無法工作了,如n=n+1,n++等。如果某個域的值受到其他域的值的限制,那麼volatile也無法工作,如Range類的lower和upper邊界,必須遵循lower<=upper的限制。
  • 6、使用volatile而不是synchronized的唯一安全的情況是類中只有一個可變的域。

Q:開啟一個執行緒的方法有哪些?銷燬一個執行緒的方法呢?

  • 開啟執行緒

1.第一種方法是將類宣告為 Thread 的子類。
該子類應重寫 Thread 類的 run 方法,然後在run方法裡填寫相應的邏輯程式碼。

class ThreadDemo1 extends Thread{
    @Override
    public void run() {
        for(int i =0;i<5;i++){
            System.out.println("Hello Thread"+i);
        }
    }
}
public class CreateThreadDemo {
    public static void main(String[] args) {
        ThreadDemo1 threadDemo1 = new ThreadDemo1();
        threadDemo1.start();
    }
}

2.第二種方法是實現Runnable介面,並編寫run方法
相比繼承Thread類建立執行緒的好處是以實現介面的方式建立執行緒可以對類進行更好的擴充套件,該類可以繼承其他類來擴充套件自身需求,相比第一種方式更加靈活,擴充套件性強。

class ThreadDemo implements Runnable{
    @Override
    public void run() {
        for(int i =0;i<5;i++){
            System.out.println("Hello Thread"+i);
        }
    }
}
public class CreateThreadDemo {
    public static void main(String[] args) {
        Thread threadDemo1 = new Thread(new ThreadDemo());
        threadDemo1.start();
    }
}

3.實現Callable介面建立執行緒
與Runnable介面的不同之處在於:如果你想要線上程執行完畢之後得到帶有返回值的執行緒則實現Callable介面。

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

class ThreadDemo implements Callable<String>{
    @Override
    public String call() throws Exception {             
        return "Hello Callable";
    }
}
public class CreateThreadDemo {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        //利用執行緒池的方式建立執行緒
        ExecutorService exec = Executors.newCachedThreadPool();
        Future<String> submit = exec.submit(new ThreadDemo());
        //獲得執行緒返回值
        String string = submit.get();
        System.out.println(string);
    }
}
  • 停止執行緒
  1. 使用退出標誌,使執行緒正常退出,也就是當run方法完成後執行緒終止。
  2. 使用stop方法強行終止,但是不推薦這個方法,因為stop和suspend及resume一樣都是過期作廢的方法。
  3. 使用interrupt方法中斷執行緒。

⚠️注意:interrupt()方法的使用效果並不像for+break語句那樣,馬上就停止迴圈。呼叫interrupt方法是在當前執行緒中打了一個停止標誌,並不是真的停止執行緒。

public class MyThread extends Thread {
    public void run(){
        super.run();
        for(int i=0; i<500000; i++){
	    //判斷執行緒是否終止,終止自己呼叫break,否則不會終止
            if(this.interrupted()) {
                System.out.println("執行緒已經終止, for迴圈不再執行");
                break;
            }
            System.out.println("i="+(i+1));
        }
    }
}

public class Run {
    public static void main(String args[]){
        Thread thread = new MyThread();
        thread.start();
        try {
            Thread.sleep(2000);
            thread.interrupt();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

如果for迴圈下還有邏輯,這種情況依然會執行,那麼怎麼處理能讓其不執行呢?
public class MyThread extends Thread {
    public void run(){
        super.run();
        try {
            for(int i=0; i<500000; i++){
                if(this.interrupted()) {
                    System.out.println("執行緒已經終止, for迴圈不再執行");
			//通過異常來終止程式的進行
                        throw new InterruptedException();
                }
                System.out.println("i="+(i+1));
            }

            System.out.println("這是for迴圈外面的語句,也會被執行");
        } catch (InterruptedException e) {
            System.out.println("進入MyThread.java類中的catch了。。。");
            e.printStackTrace();
        }
    }
}

這裡之所以丟擲異常並捕獲是考慮到對一個處於wait或者sleep狀態的執行緒使用interrupt是會丟擲異常的,所以需要事先捕獲,主程序程式碼如下:
MyThread t=new MyThread("A");
		t.start();
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		t.interrupt();
}

在子執行緒中彈toast和建立dialog會出現下面的bug

System.err: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()

解決辦法:

Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
    @Override
    public void run() {
		//。。。
           }
});

為什麼不能在子執行緒中更新UI

從異常資訊可以知道:
異常是從android.view.ViewRootImpl的checkThread方法丟擲的。而ViewRootImpl是介面ViewRoot的實現類。

ViewRootImpl的checkThread方法的原始碼如下:

void checkThread() {
    if (mThread != Thread.currentThread()) {      
        throw new CalledFromWrongThreadException("Only the original thread that created a view hierarchy can touch its views.");
    }       
}

其中mThread是主執行緒(UI執行緒或者MainThread執行緒),在應用程式啟動的時候,就已經被初始化了。

由此我們可以得出結論:

在訪問UI的時候,ViewRoot會去檢查當前是哪個執行緒訪問的UI,如果不是主執行緒,就會丟擲異常:Only the original
thread that created a view hierarchy can touch its views。

OOm是否可以try catch

只有在一種情況下,這樣做是可行的:
在try語句中聲明瞭很大的物件,導致OOM,並且可以確認OOM是由try語句中的物件宣告導致的,那麼在catch語句中,可以釋放掉這些物件,解決OOM的問題,繼續執行剩餘語句。
但是這通常不是合適的做法。
Java中管理記憶體除了顯式地catch OOM之外還有更多有效的方法:比如SoftReference, WeakReference, 硬碟快取等。
在JVM用光記憶體之前,會多次觸發GC,這些GC會降低程式執行的效率。
如果OOM的原因不是try語句中的物件(比如記憶體洩漏),那麼在catch語句中會繼續丟擲OOM

Q:專案中如何做效能優化的?

佈局優化:儘量減少佈局檔案的層級。

  • 多巢狀情況下可使用RelativeLayout減少巢狀。
  • 佈局層級相同的情況下使用LinearLayout,它比RelativeLayout更高效。
  • 使用標籤重用佈局、標籤減少層級、標籤懶載入。
    繪製優化:避免onDraw()中執行大量的操作
  • 避免建立新的區域性物件,因為onDraw()可能被多次呼叫而產生大量的臨時物件,導致佔用過多記憶體、系統頻繁gc,降低了執行效率。
  • 避免做耗時任務,以及大量迴圈操作。

記憶體洩漏優化:

  • 靜態變數引起的記憶體洩漏:在dalvik虛擬機器中,static變數所指向的記憶體引用,如果不把它設定為null,GC是永遠不會回收這個物件的,————>解決辦法:傳參context.getApplicationContext()Context
  • 單例模式引起的記憶體洩漏詳情:單例傳入引數this來自Activity,使得持有對Activity的引用。————>解決辦法:傳參context.getApplicationContext()。
  • 屬性動畫導致的記憶體洩漏:詳情:沒有在onDestroy()中停止無限迴圈的屬性動畫,使得View持有了Activity。————>解決辦法:在Activity.onDestroy()中呼叫Animator.cancel()停止動畫。
  • Handler導致的記憶體洩漏:當Handler中有正在處理或者未被處理的訊息時,去選擇銷燬Activity,這個時候handler會持有這個Activity類的引用,導致不能回收所以會造成記憶體洩漏(前提是這個Handler是非靜態內部類或者匿名內部類的時候)———>解決辦法:1、靜態內部類+弱飲用(弱引用的物件擁有短暫的生命週期。在垃圾回收器執行緒掃描時,一旦發現了只具有弱引用的物件,不管當前記憶體空間足夠與否,都會回收它的記憶體)靜態內部類不持有外部類的引用。2、當外部類生命週期結束時,清空handler的訊息佇列,即 mHandler.removeCallbacksAndMessages(null);
  • 執行緒導致的記憶體洩漏: run方法不結束 Activity被銷燬的時候會導致記憶體洩漏———>靜態內部類+弱引用的方式。
  • AsyncTask引起的記憶體洩漏:使用靜態內部類
  • 資源沒有關閉導致的記憶體洩漏:未及時登出資源導致記憶體洩漏,如BraodcastReceiver、File、Cursor、Stream、Bitmap等。
    解決辦法:在Activity銷燬的時候要及時關閉或者登出。Broadcastreceiver:呼叫unregisterReceiver()登出;Cursor,stream,file:呼叫close()關閉;Bitmap:呼叫recycle()釋放記憶體(2.3版本後無需手動)。
  • Adapter導致的記憶體洩漏:沒有使用convertView每次重新例項化Item,給GC造成壓力
    在Java中,非靜態內部類 & 匿名內部類都預設持有 外部類的引用

Q:記憶體洩漏和記憶體溢位的區別?

1、記憶體溢位 out of memory,是指程式在申請記憶體時,沒有足夠的記憶體空間供其使用,出現out of memory;比如申請了一個integer,但給它存了long才能存下的數,那就是記憶體溢位。
2、記憶體洩露 memory leak,是指程式在申請記憶體後,無法釋放已申請的記憶體空間,一次記憶體洩露危害可以忽略,但記憶體洩露堆積後果很嚴重,無論多少記憶體,遲早會被佔光。

Q:Java中引用有幾種型別?在Android中常用於什麼情景?

強引用(任何時候都不回收)—》軟引用(記憶體不夠才回收)—〉弱引用(發現就回收)—》虛引用

Q:類載入的全過程是怎樣的?什麼是雙親委派模型?

載入—》連結(驗證、準備、解析)—〉初始化—》使用—〉解除安裝

雙親委派模型: 如果一個類載入器收到類載入的請求,他首先不會自己去嘗試載入這個類,而是把請求委託給父類載入器去完成,依次向上,因此所有的類載入請求最終都被傳到頂層的類載入器中,只有當父類載入器在他的搜尋範圍內沒找到所需的類時,子載入器才嘗試自己去載入該類

Q:IntentService的特點?

繼承自Service,內部有Thread即handlerThread(具有訊息迴圈的效果)

  • 相比於執行緒:由於是服務,優先順序比執行緒高,更不容易被系統殺死。因此較適合執行一些高優先順序的後臺任務。
  • 相比於普通Service:可自動建立子執行緒來執行任務,且任務執行完畢後自動退出

Q:為何不用bindService方式建立IntentService?

IntentService本身就是非同步的,本身就不能確定是否在activity銷燬後還是否執行,如果用bind的話,activity銷燬的時候,IntentService還在執行任務的話就很矛盾了。

Q:執行緒池的好處、原理、型別?

好處:

  • 重用執行緒池中的執行緒,避免執行緒的建立和銷燬帶來的效能消耗;
  • 有效控制執行緒池的最大併發數,避免大量的執行緒之間因互相搶佔系統資源而導致阻塞現象;
  • 進行執行緒管理,提供定時/迴圈間隔執行等功能。

ThreadPoolExecutor執行緒池的分類:

  • FixThreadPool:
    • 含義:執行緒數量固定的執行緒池,所有執行緒都是核心執行緒,當執行緒空閒時不會被回收。
    • 特點:能快速響應外界請求。
  • CachedThreadPool:
    • 含義:執行緒數量不定的執行緒池(最大執行緒數為Integer.MAX_VALUE),只有非核心執行緒,空閒執行緒有超時機制,超時回收。
    • 特點:適合於執行大量的耗時較少的任務
  • ScheduledThreadPool:
    • 含義:核心執行緒數量固定,非核心執行緒數量不定。
    • 特點:定時任務和固定週期的任務。
  • SingleThreadExecutor:
    • 含義:只有一個核心執行緒,可確保所有的任務都在同一個執行緒中按順序執行。
    • 特點:無需處理執行緒同步問題

Q:ThreadPoolExecutor的工作策略?

  • 若程池中的執行緒數量未達到核心執行緒數,則會直接啟動一個核心執行緒執行任務。
  • 若執行緒池中的執行緒數量已達到或者超過核心執行緒數量,則任務會被插入到任務列表等待執行。
    • 若任務無法插入到任務列表中,往往由於任務列表已滿,此時如果
      • 執行緒數量未達到執行緒池最大執行緒數,則會啟動一個非核心執行緒執行任務;
      • 執行緒數量已達到執行緒池規定的最大值,則拒絕執行此任務,ThreadPoolExecutor會呼叫RejectedExecutionHandler的rejectedExecution方法來通知呼叫者。

Q:什麼是ANR?什麼情況會出現ANR?如何避免?在不看程式碼的情況下如何快速定位出現ANR問題所在?

Application not Response 在主執行緒做耗時操作,避免在主執行緒中進行耗時操作

Q :使用AsyncTask應該注意什麼?

1、Task的例項必須在UI thread中建立;
2、execute方法必須在UI thread中呼叫;
3、不要手動的呼叫onPreExecute(), onPostExecute(Result),doInBackground(Params…), onProgressUpdate(Progress…)這幾個方法;
4、該task只能被執行一次,否則多次呼叫時將會出現異常;

Q:談談訊息機制Hander?作用?有哪些要素?流程是怎樣的?

Handler訊息傳遞機制,主要用來執行緒間通訊

  • Message(訊息):需要被傳遞的訊息,其中包含了訊息ID,訊息處理物件以及處理的資料等,由MessageQueue統一列隊,最終由Handler處理。
  • MessageQueue(訊息佇列):用來存放Handler傳送過來的訊息,內部通過單鏈表的資料結構來維護訊息列表,等待Looper的抽取。
  • Handler(處理者):負責Message的傳送及處理。
    • Handler.sendMessage():向訊息池傳送各種訊息事件。
    • Handler.handleMessage() :處理相應的訊息事件。
  • Looper(訊息泵):通過Looper.loop()不斷地從MessageQueue中抽取Message,按分發機制將訊息分發給目標處理者。

Q:為什麼系統不建議在子執行緒訪問UI?

系統不建議在子執行緒訪問UI的原因:UI控制元件非執行緒安全,在多執行緒中併發訪問可能會導致UI控制元件處於不可預期的狀態。而不對UI控制元件的訪問加上鎖機制的原因有:

  • 上鎖會讓UI控制元件變得複雜和低效
  • 上鎖後會阻塞某些程序的執行

Q:一個Thread可以有幾個Looper?幾個Handler?

  • 一個Thread只能有一個Looper,可以有多個Handler;
  • Looper有一個MessageQueue,可以處理來自多個Handler的Message;

Q:如何將一個Thread執行緒變成Looper執行緒?

Looper.prepare();//為子執行緒建立Looper
Looper.loop(); //開啟訊息輪詢

Q:可以在子執行緒直接new一個Handler嗎?那該怎麼做?

在子執行緒建立Handler例項之前必須先建立Looper例項,否則會拋RuntimeException。

//在子執行緒中使用
Handler class LooperThread extends Thread { 
	public Handler mHandler; 
	public void run() {
		//1、建立Looper
		 Looper.prepare(); 
		//2、建立handler
		 mHandler = new Handler() {	
			 public void handleMessage(Message msg) { 
				// process incoming messages here
				 }
			 }; 
		//3、開啟訊息輪詢
		Looper.loop(); 
		}
	 }

Q:Message可以如何建立?哪種效果更好,為什麼?

可以直接new一個也可以通過Message.obtain()獲得,後者更好,訊息池幫我門管理訊息

Q:這裡的ThreadLocal有什麼作用?

是幫助Handler獲得當前執行緒的Looper

Q:主執行緒中Looper的輪詢死迴圈為何沒有阻塞主執行緒?

這是因為主執行緒本來就是跑在一個這樣的迴圈裡,虛擬碼是這樣的:

..... main (... ) {
	 Looper.prepare();//主執行緒跑起來了
    	   ... ... ... 
    	 Looper.loop();//開啟死迴圈了
       }

看到沒,其實主執行緒本身就是一個死迴圈,當這個死迴圈停止,app也就退出了。可以這樣認為:
你在app裡所用的操作,比如點選按鈕、點選返回鍵、HOME鍵等,還有發出一個廣播,收到一個廣播這種非介面相關的,都是事件(當做Message),主執行緒就是一直在處理這些Message才使得app動了起來。如果恰巧沒有事件了呢?主執行緒不就阻塞了?不就ANR了嗎?這還不好說!?會不會ANR還不是主執行緒(或者說android自身)說了算。
最後一句話總結:主執行緒一直沒死,就是因為一直有其他模組發出的、我們看不到的Message放進主執行緒的MessageQueue,主執行緒一直在忙著處理這些Message。

Q:Android中有哪幾種類型的動畫?

補間動畫:平移動畫、縮放動畫、旋轉動畫和透明度動畫

注意: View動畫的View移動只是視覺效果,並不能真正的改變view的位置。

屬性的取值可能是數值、百分數、百分數p,各自含義是:
* 50:以View左上角為原點沿座標軸正方向偏移50px。
* 50%:以View左上角為原點沿座標軸正方向偏移View寬/高度的50%。
* 50%p:以View左上角為原點沿座標軸正方向偏移父(parent)控制元件寬/高度的50%。

逐幀動畫:幀動畫也是View動畫的一種,它會按照順序播放一組預先定義好的圖片。對應類AnimationDrawable。

  • 該xml檔案建立在res/drawable/ 下。
  • 根節點,屬性android:oneshot表示是否執行一次;子節點 下可設定輪播的圖片資源id和持續時間
    使用禎動畫要注意不能使用尺寸過大的圖片,否則容易造成OOM( 記憶體溢位)錯誤。

Q:幀動畫在使用時需要注意什麼?

圖片不能太大

Q:View動畫和屬性動畫的區別?

從別處借來的圖片

ObjectAnimator與 ValueAnimator類的區別:

  • ValueAnimator 類是先改變值,然後 手動 賦值給物件的屬性從而實現動畫;是間接對物件屬性進行操作;
  • ObjectAnimator 類是先改變值,然後 自動 賦值給物件的屬性從而實現動畫;是直接對物件屬性進行操作

Q:瞭解哪些Drawable?適用場景?

StateListDrawable 對應的根結點
ShapeDrawable 對應的根結點

Q:dp、dpi、px的區別?

dp:dip密度無關畫素=螢幕對角線畫素數 / 螢幕對角線長度;dpi=160時,1dp=1px;
dpi:每英寸多少畫素
px:畫素
px = dp * (dpi / 160) 如:我將一個控制元件設定長度為1dp,那麼在160dpi上該控制元件長度為1px,在240dpi的螢幕上該控制元件的長度為1*240/160=1.5個畫素點。

在這裡插入圖片描述

Q:res目錄和assets目錄的區別?

assets res/raw res/drawable
是否被壓縮 NO NO YES(失真壓縮)
能否獲取子目錄下的資源 YES NO NO

assets目錄下的檔案不會被編譯成二進位制,直接被打包到apk中。assets目錄中的檔案不會在R.java中建立索引。assets目錄下的檔案需藉助AssetManager訪問。assets目錄下可以建立自己的子目錄。

res/raw目錄下的檔案不會被編譯成二進位制。由於res目錄下的所有東西都會在R.java中建立索引,可以用Resources.openRawResource方法讀取raw中的檔案(getResources().openRawResource(R.raw.rawtext))。raw目錄下不允許建立子目錄。

Drawable可通過getIntrinsicWidth()和getIntrinsicHeight()獲取其內部寬/高。

注意:並不是所有Drawable都有內部寬/高。

  • 圖片所形成的Drawable的內部寬/高就是圖片的寬/高。
  • 顏色所形成的Drawable沒有內部寬/高的概念。

Q:MotionEvent是什麼?包含幾種事件?什麼條件下會產生?

MotionEvent是手指觸控手機螢幕所產生的一系列事件

主要包括以下三種事件
ACTION_DOWN:手指剛接觸螢幕
ACTION_MOVE:手指在螢幕上滑動
ACTION_UP:手指在螢幕上鬆開的一瞬間

Q:scrollTo()和scrollBy()的區別?

scrollTo()一步到位,直接滑到終點
scrollBy()沒有終點,每次都滑動相應的距離

Q:Scroller中最重要的兩個方法是什麼?主要目的是?

使用Scroller並不能得到滑動的效果,它只是“儲存“了View滾動的資料;
使View滑動還是要藉助scrollTo和scrollBy兩個方法來實現的。

1、computeScrollOffset 返回true表示滑動沒有結束,false表示結束了。
2、scroller.startScroll(0,0,-400,-200); 設定滑動的起始點和滑動的距離,在250毫秒內完成滑動

(自定義View時重寫computeScroll方法,在這個裡面呼叫scroller的computeScrollOffset方法來判斷滑動是否結束,並呼叫scrollTo或scrollBy方法實現View的滑動效果)

Q:談一談View的事件分發機制?

  • 1、就是對MotionEvent事件分發的過程。即當一個MotionEvent產生了以後,系統需要將這個點選事件傳遞到一個具體的View上。
  • 2、點選事件的傳遞順序:Activity(Window) -> ViewGroup -> View
  • 3、需要的三個主要方法:
    dispatchTouchEvent:進行事件的分發(傳遞)。返回值是 boolean 型別,受當前onTouchEvent和下級view的dispatchTouchEvent影響
    onInterceptTouchEvent:對事件進行攔截。該方法只在ViewGroup中有,View(不包含 ViewGroup)是沒有的。一旦攔截,則執行ViewGroup的onTouchEvent,在ViewGroup中處理事件,而不接著分發給View。且只調用一次,所以後面的事件都會交給ViewGroup處理。
    onTouchEvent:進行事件處理。

這裡說的很淺,具體去百度吧,有很多哦的部落格分析View的事件分發機制

Q:如何解決View的滑動衝突?

攔截:點選事件都先經過父容器的攔截處理,如果父容器需要此事件就攔截,否則就不攔截。攔截的話需要重寫父容器的onInterceptTouchEvent方法,在內部做出相應的攔截
指父容器不攔截任何事件,而將所有的事件都傳遞給子容器,如果子容器需要此事件就直接消耗,否則就交由父容器進行處理。

Q:談一談View的工作原理?

onMeasure測量View的大小—>onLayout佈局——>onDraw繪製
在onLayout後可以使用getWidth獲取寬,之前和onLayout中使用getMeasureWidth獲取寬

Q:MeasureSpec是什麼?有什麼作用?

一個MeasureSpec封裝了父佈局傳遞給子佈局的佈局要求,每個MeasureSpec代表了一組寬度和高度的要求。一個MeasureSpec由大小和模式組成

它有三種模式:
UNSPECIFIED(未指定),父元素部隊自元素施加任何束縛,子元素可以得到任意想要的大小;
EXACTLY(完全),父元素決定自元素的確切大小,子元素將被限定在給定的邊界裡而忽略它本身大小;
AT_MOST(至多),子元素至多達到指定大小的值。

Q:自定義View/ViewGroup需要注意什麼?

(借用大神們終結的兩個圖片)
在這裡插入圖片描述

在這裡插入圖片描述

Q:SurfaceView和View的區別?

View SurfaceView
適用於主動更新 適用於被動重新整理
在主執行緒中進行畫面更新 通常通過一個子執行緒來進行畫面更新
繪圖中沒有使用雙緩衝機制 在底層實現中就實現了雙緩衝機制

Q:invalidate()和postInvalidate()的區別?

前者在主執行緒去重新整理View,後者可以在子執行緒重新整理View

擴充套件:
invalidate()會去執行onDraw();
requestLayout()回去呼叫整個繪圖的過程即onMeasure()—>onLayout()—>onDraw()

Q:什麼是序列化?Serializable介面和Parcelable介面的區別?為何推薦使用後者?

序列化的作用:
1、永久性儲存物件,
2、儲存物件的位元組序列倒本地檔案,
3、通過序列化物件,在網路和程序中傳遞物件。

Serializable在序列化的時候會產生很多的臨時變數,會引起頻繁的GC,Parcelable的效能比較高,但是Parcelable不能將資料儲存在磁碟的情況下使用,因為不能保證資料的持續性在外界變化的情況下。

Q:Android中提供哪些資料持久儲存的方法?

檔案儲存、本地儲存即sharedPreferences、網路儲存以及資料庫儲存

Q:Java中的I/O流讀寫怎麼做?

在Android中寫入和讀取檔案的方法,和 Java中實現I/O的程式是一樣的,Context類中提供了openFileInput()和openFileOutput()方法來開啟資料檔案裡的檔案IO流。

Q:SharePreferences適用情形?使用中需要注意什麼?

SharePreferences是一種輕型的資料儲存方式,常用來儲存一些簡單的配置資訊;
一定注意,不用SharedPreferences物件去儲存或修改資料,而是通過Editor物件。但獲取資料時需要呼叫SharedPreferences物件的get某某某()方法了。

Q:瞭解SQLite中的事務處理嗎?是如何做的?

使用SQLiteDatabase的beginTransaction()方法可以開啟一個事務,程式執行到endTransaction() 方法時會檢查事務的標誌是否為成功,如果為成功則提交事務,否則回滾事務。當應用需要提交事務,必須在程式執行到endTransaction()方法之前使用setTransactionSuccessful() 方法設定事務的標誌為成功,如果不呼叫setTransactionSuccessful() 方法,預設會回滾事務。

db.beginTransaction(); // 開啟事務
try {
    db.execSQL("insert into person (name, age) values(?,?)", new Object[]{"張三", 4});

    db.execSQL("update person set name=? where personid=?", new Object[]{"李四", 1});

    db.setTransactionSuccessful(); // 標記事務完成
} finally {
    db.endTransaction(); // 結束事務
    db.close();
}

Q:談一談Service的生命週期?

1、開啟服務:onCreate()、onStartCommand()、onDestory()
2、繫結服務:onCreate()、onBind()、onUnbind()、onDestory()

Q:Service的兩種啟動方式?區別在哪?

  • 1、開啟服務不依賴於開啟他的物件,繫結服務依賴他所繫結的物件,共存亡;繫結服務可以進行通訊;
  • 2、服務啟動了之後會一直保持執行狀態,直到stopService()或stopSelf()方法被呼叫,服務停止並回調onDestroy()。另外,無論呼叫多少次startService()方法,只需呼叫一次stopService()或stopSelf()方法,服務就會停止了,繫結的服務直到呼叫unbindService()方法服務會停止;(不管執行了多少次的startService(),stopService()或stopSelf()只需要執行一次)。
  • 3、onStartCommand()方法每呼叫一次startService()就執行一次,但是onBind()方法不管呼叫多少次bindService()都只被呼叫一次。

Q:一個Activty先start一個Service後,再bind時會回撥什麼方法?此時如何做才能回撥Service的destory()方法?

因為start一個服務時已經執行過了onCreate()所以在繫結的時候只會執行onBind()方法。
這個時候如果選擇停止服務則不會執行onDestory()如果在解綁會執行onUnbind()和onDestory();
但是如果先解綁在停止服務則解綁的時候只會執行onUnbind()停止服務的時候會執行onDestory().

Q:Service如何和Activity進行通訊?

只有繫結的服務才可以進行通訊,通過Binder 、AIDL

Q:用過哪些系統Service?

WindowManager管理開啟視窗的程式
layoutInflate獲取xml定義的View
activitymanager管理應用程式的系統狀態

Q:是否能在Service進行耗時操作?如果非要可以怎麼做?

Service是執行在主執行緒的,如果做耗時操作會出現ANR,如果要做耗時操作可以開啟個執行緒。

Q:前臺服務是什麼?和普通服務的不同?如何去開啟一個前臺服務?

1、前臺程序:正在和使用者互動的程序
2、可見程序:程式介面能夠被使用者看見,卻不在前臺與使用者互動的程序
3、服務程序:服務程序是通過 startService() 方法啟動的程序,但不屬於前臺程序和可見程序。例如,在後臺播放音樂或者在後臺下載就是服務程序
4、後臺程序:後臺程序指的是目前對使用者不可見的程序。例如我正在使用qq和別人聊天,這個時候qq是前臺程序,但是當我點選Home鍵讓qq介面消失的時候,這個時候它就轉換成了後臺程序。當記憶體不夠的時候,可能會將後臺程序回收。
5、空程序:該程序內部沒有任何東西在執行,作用是用來快取,來縮短,該應用下次在其中進行元件所需的啟動時間

Q:如何保證Service不被殺死?

保證服務不被殺死
1、onStartCommand方法中,返回START_STICKY
2、提升service的優先順序:在AndroidManifest.xml檔案中對於intent-filter可以通過android:priority = "1000"這個屬性設定最高優先順序,1000是最高值,如果數字越小則優先順序越低,同時適用於廣播
3、提升service的程序優先順序,比如前臺程序
4、在service的onDestroy方法中傳送廣播,在廣播中重新開啟服務

Q:談一談Fragment的生命週期?

onAttach():執行該方法時,Fragment與Activity已經完成繫結

onCreate():初始化Fragment

onCreateView():初始化Fragment的佈局。載入佈局和findViewById的操作通常在此函式內完成,但是不建議執行耗時的操作,比如讀取資料庫資料列表。

onActivityCreated():執行該方法時,與Fragment繫結的Activity的onCreate方法已經執行完成並返回,在該方法內可以進行與Activity互動的UI操作,所以在該方法之前Activity的onCreate方法並未執行完成,如果提前進行互動操作,會引發空指標異常。

onStart():執行該方法時,Fragment由不可見變為可見狀態。

onResume():執行該方法時,Fragment處於活動狀態,使用者可與之互動。

onPause():執行該方法時,Fragment處於暫停狀態,但依然可見,使用者不能與之互動。

onStop():執行該方法時,Fragment完全不可見。

onDestroyView():銷燬與Fragment有關的檢視,但未與Activity解除繫結,依然可以通過onCreateView方法重新建立檢視。通常在ViewPager+Fragment的方式下會呼叫此方法。

onDestroy():銷燬Fragment。通常按Back鍵退出或者Fragment被回收時呼叫此方法。

onDetach():解除與Activity的繫結。在onDestroy方法之後呼叫。

Q:Activity和Fragment的異同?

1、fragment顯得更加靈活。可以直接在XML檔案中新增<fragment/>,Activity則不能。
2、可以在一個介面上靈活的替換一部分頁面,Activity不可以,做不到。
3、fragment是通過inflater載入View然後在main.xml中註冊得到的。當然如果你可以在fragment中得到View那就可以通過View.findViewId()來操控fragment上的具體控制元件。
4、可以動態載入Fragment
5、生命週期不同

Q:何時會考慮使用Fragment?

當你有一個activity,想讓這個activity根據事件響應可以對應不同的介面時,就可以建立幾個fragment,將fragment繫結到該activity

Q:說下Activity的生命週期?

onCreate()、onStart()、onResume()、onPause()、onStop()、onDestory()、onRestart()

Q:onStart()和onResume()/onPause()和onStop()的區別?

onStart()介面可見
onResume()介面獲取焦點,可以進行互動
onPause()介面失去焦點,不可以進行互動
onStop()介面不可見

Q:Activity A啟動另一個Activity B會回撥哪些方法?如果Activity B是完全透明呢?如果啟動的是一個Dialog呢?

A的onPause()—>B的onCreate()—>B的onStart()—>B的onResume()

Q:談談onSaveInstanceState()方法?何時會呼叫?

當按Home或者記憶體滿了意外退出的時候會呼叫,通常用來儲存臨時資料

Q:onSaveInstanceState()與onPause()的區別?

前者用來儲存臨時資料,在按Home或者意外退出會被呼叫,後者是失去焦點,不可互動的時候被呼叫,通常用來儲存持久化的資料

Q:如何避免配置改變時Activity重建?

在AndroidManifest.xml 中相應的Activity的activity標籤下設定android:configChanges="orientation|screenSize”這樣不會重新建立Activity只會執行onConfigurationChanged方法。

Q:優先順序低的Activity在記憶體不足被回收後怎樣做可以恢復到銷燬前狀態?