1.說出你對面向對象的理解android
在我理解,面向對象是向現實世界模型的天然延伸,這是一種「萬物皆對象」的編程思想。在現實生活中的任何物體均可以歸爲一類事物,而每個個體都是一類事物的實例。面向對象的編程是以對象爲中心,以消息爲驅動,因此程序=對象+消息。
面向對象有三大特性,封裝、繼承和多態。
封裝就是將一類事物的屬性和行爲抽象成一個類,使其屬性私有化,行爲公開化,提升了數據的隱祕性的同時,使代碼模塊化。這樣作使得代碼的複用性更高。
繼承則是進一步將一類事物共有的屬性和行爲抽象成一個父類,而每個子類是一個特殊的父類--有父類的行爲和屬性,也有本身特有的行爲和屬性。這樣作擴展了已存在的代碼塊,進一步提升了代碼的複用性。
若是說封裝和繼承是爲了使代碼重用,那麼多態則是爲了實現接口重用。多態的一大做用就是爲了解耦--爲了解除父子類繼承的耦合度。若是說繼承中父子類的關係式IS-A的關係,那麼接口和實現類之之間的關係式HAS-A。簡單來講,多態就是容許父類引用(或接口)指向子類(或實現類)對象。不少的設計模式都是基於面向對象的多態性設計的。算法
2.JVM的內存區及其GC算法
https://blog.csdn.net/anjoyandroid/article/details/78609971
元空間:jdk1.8取消了持久代新增了元空間,並將方法區放在元空間中編程
3.集合框架下的各類接口和實現類有哪些,分別有啥特色設計模式
https://blog.csdn.net/sdgihshdv/article/details/72566485
https://blog.csdn.net/suifeng629/article/details/82179996
https://blog.csdn.net/C18298182575/article/details/87167323api
4.string類有啥特色,有哪些經常使用的API數組
1.String類對象的相等判斷使用equals()方法完成,「==」實現的是地址數值的比較
2.字符串內容一旦聲明則不可改變,String類對象內容的改變是依靠引用關係的變動實現的。
3.String類有兩種實例化方式,使用直接賦值能夠不產生垃圾空間,而且能夠自動入池,不要使用構造方法賦值。安全
indexOf()檢索字符串中某個字符或某段字符的下標。
lastIndexOf()
和indexOf相似,不過是查找最後一個出現的位置。
str.lastIndexOf(str,index),從下標index往前查找最後一個出現的位置
substring()返回一個字符串的子字符串
charAt(index)返回下標對應的字符
trim()去掉字符串先後的空格
startsWith()/endsWith()檢測字符串是否已制定字符串開頭或結尾,返回值是boolean
split()/根據括號內的字符串分離字符串,返回值是一個字符串數組
....多線程
5.stringBuilder和stringBuffer的區別?併發
運行速度:StringBuilder >StringBuffer >String
線程安全:StringBuilder是線程不安全的,而StringBuffer是線程安全的
String:適用於少許的字符串操做的狀況
StringBuilder:適用於單線程下在字符緩衝區進行大量操做的狀況
StringBuffer:適用多線程下在字符緩衝區進行大量操做的狀況app
爲何StringBuilder是不安全的?
char[] value;
int count;
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
對於count + =len;不是一個原子操做 兩個線程同時執行假設都是 計數器爲 10 執行完後 就會變成11 而不是12
什麼是原子操做:
簡單的例子:
轉帳,A轉給B100,由於停電,致使A轉出了100,B卻沒收到100,因此要把100回滾給A。
原子操做就是多線程下各線程同時執行失敗且同時成功,在兩個線程下,因爲count繼承於父類AbstractStringBuilder,當
其中一個線程對coun執行+len後,另外一線程取到的count值仍爲原來的count值,故+len後和上一個線程獲得的結果同樣,
故線程不安全
而stringBuffer中源碼:
@Override
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
當一個線程訪問append後會當即上鎖,從而另外一個線程沒法訪問append方法,故是線程安全的
在多線程下,stringBuffer下各線程須要頻繁的加鎖解鎖操做,從而須要運行更長的時間,雖然stringBuilder不須要加鎖解鎖,
當因爲線程不安全性,更適用於單線程
6.線程建立的3種方式,線程阻塞的API有哪些及其之間的區別?
Runnable,Thread,經過 Callable 和 Future 建立線程。
1. 繼承Thread類來建立一個線程, 並實現run方法(線程須要完成的功能); 構建子類對象,start()啓動線程
2. 實現Runnable接口來建立一個線程, 實現Runnable,實現run()方法; 將Runnable接口類的對象做爲參數傳遞給Thread類對象, 並調用start()方法;
3. 實現Callable接口來建立一個線程, 先定義一個Callable的實現類, 重寫call()方法, call()有返回值; 兩種執行方式:
1). 藉助FutureTask執行, 建立Callable實現類的對象, 並做爲參數傳遞給FutureTask, FutureTask做爲參數傳遞給Thread類的對象, 並執行start()方法;
2). 藉助線程池來執行, 先建立線程池, 而後調用線程池的submit方法, 並將Callable實現列做爲參數傳入,
方法二的好處:
1. 能夠將一個Runnable實現類傳遞給多個線程對象, 適合用多個相同程序代碼的編程處理同一個資源
2. Thread類建立線程是採用繼承的方式, 而Java中只能單繼承, 若是某個子類的須要建立線程只能採用實現Runnable接口或者實現Callable接口的方式.
方法三的好處:
1. 有返回值
2. call()能夠拋出異常
3. 運行Callable任務能夠獲得一個Future兌現,表示異步計算的結果. 它提供了檢測計算是否完成的方法(isDone())以等待計算的完成,並檢索計算的結果.
線程阻塞api:
sleep()方法; 該方法容許指定以ms爲單位的一段時間做爲參數, 它使得線程在指定的時間內進入阻塞狀態,不能獲得CPU時間, 指定時間已過,線程從新進入可執行狀態.
suspend()和resume()方法;配套使用, suspend()使得線程進入阻塞狀態,且不會自動恢復, 必須將其對應的resume()調用, 纔可使線程進入可執行狀態.
yield(); 使得線程放棄當前分得的CPU時間, 可是不使線程阻塞, 即線程仍然處於可執行狀態;
wait()和notify()方法;配套使用,若wait()有參數,至關於sleep(但能夠經過notify強行喚醒), wait()沒有參數,至關於suspend(), 須要經過notify喚醒
sleep(0)和sleep(1)和不要sleep的區別:
sleep(0),若是線程調度器的可運行隊列中有大於或等於當前線程優先級的就緒線程存在,操做系統會將當前線程從處理器上移除,調度其餘優先級高的就緒線程運行;若是可運行隊列中的沒有就緒線程或全部就緒線程的優先級均低於當前線程優先級,那麼當前線程會繼續執行,就像沒有調用 Sleep(0)同樣。
Sleep(1),會引起線程上下文切換:調用線程會從線程調度器的可運行隊列中被移除一段時間,這個時間段約等於 timeout 所指定的時間長度。爲何說約等於呢?是由於睡眠時間單位爲毫秒,這與系統的時間精度有關。一般狀況下,系統的時間精度爲 10 ms,那麼指定任意少於 10 ms但大於 0 ms 的睡眠時間,均會向上求值爲 10 ms。
7.抽象類和接口的區別?有了抽象類爲啥還要接口?
①.一類能夠實現多個接口但只能繼承自一個抽象類,從抽象類派生出的子類一樣能夠實現接口,從而,咱們能得出一個結論:接口是爲Java實現多繼承而存在的
②.抽象類中能夠存在非抽象的方法,可接口不能存在非抽象的方法,而且接口裏面的方法只是一個聲明,必須用 public abstract來修飾,沒有具體的實現
③.抽象方法中的成員變量能夠被不一樣的修飾符修飾,而接口中的成員變量默認都是靜態常量
④.抽象類是對對象進行的抽象,而接口是一種行爲規範,這一點是比較重要的.
(因此爲何有了接口還要有抽象類)
8.冒泡排序,選擇排序,快速排序(瞭解)
冒泡排序:什麼是冒泡?好比說水底隨機產生一些氣泡,一塊兒往上冒泡,越輕的氣泡往上冒的越快
具體:12 34 10 78 67
若是從小到大排序:先將67和78比較,67比78小,依次往前比較,小的放前面,打的放後面,以此爲一輪排序,而後再將新的數組重複上述過程,共須要n輪排序(n爲元素個數);
選擇排序:從一個數組裏選出最小的元素放在數組第一位並交換位置,而後再將去掉第一位的數組找出最小元素並放在這個新數組第一位,
重複此操做。
12 34 10 78 67
第一輪:10| 34 12 78 67
第二輪:10 12| 34 78 67
第三輪:10 12 34| 78 67
第四輪:10 12 34 67| 78
排序結束
快速排序:基於基數排序。先取任意一基數,通常爲數組第一個元素(因爲當第一個元素爲最小值(最大值)時會使排序出現錯誤,故有時候也取中間的元素),而後將比基數小的數做爲一個數組,比基數大的數做爲一個數組,再將新的兩個數組分別遞歸排序。
經過基數分紅兩個數組的過程:12 34 10 78 67 8 假設數組爲arr
取一基數temp=12 取low=0(數組第一位),high=5(數組最後一位)
第一輪:第一步:先從後往前比較:arr[high]=8<12=temp,結束這一步操做,high與low不變。若是這裏arr[high]>12,則令high-1獲得新的high將arr[high]與temp比較,依此下去直到arr[high]<temp,這種狀況high發生改變,low不變。
第二步:再從前日後將arr[low]與temp比較,原理與第一步相同,由於arr[1]>temp,此時low=1,結束這一步操做。
第三步:交換arr[low]與arr[high]的值
第一輪結果:12 8 10 78 67 34(low=1,high=5)
第二輪:與第一輪同樣,第一步,從arr[high]往前,直到arr[2]=10<12,此時high=2,結束這一步
第二步,從arr[low]日後,12,8,10都不大於12,到這裏的時候,由於low=2=high,故比較,獲得索引index=low=high=2
第二輪結果:12 8 10 78 67 34
由於index獲得了值3,將arr[index]做爲分界點將最後一輪結果數組[12 8 10 78 67 34]分爲兩個數組[12 8 10]和[78 67 34]
將新的到的兩個數組重複進行上述操做
[12 8 10]->由於12爲最大值,故取中間值8->[8]和[10 12]->[8]、[10]、[12]
[78 67 34]->取67,->[34]、[67 78]->[34]、[67]、[78]->[8]、[10]、[12]、[34]、[67]、[78]
(拓展:希爾排序、插入排序)
9.什麼是死鎖?如何避免死鎖
死鎖的定義:所謂死鎖是指多個線程因競爭資源而形成的一種僵局(互相等待),若無外力做用,這些進程都將沒法向前推動。
產生緣由:
1) 系統資源的競爭
一般系統中擁有的不可剝奪資源,其數量不足以知足多個進程運行的須要,使得進程在 運行過程當中,會因爭奪資源而陷入僵局,如磁帶機、打印機等。只有對不可剝奪資源的競爭 纔可能產生死鎖,對可剝奪資源的競爭是不會引發死鎖的。
2) 進程推動順序非法
進程在運行過程當中,請求和釋放資源的順序不當,也一樣會致使死鎖。例如,併發進程 P一、P2分別保持了資源R一、R2,而進程P1申請資源R2,進程P2申請資源R1時,二者都 會由於所需資源被佔用而阻塞。
四個產生死鎖的條件:
互斥條件:進程要求對所分配的資源(如打印機)進行排他性控制,即在一段時間內某 資源僅爲一個進程所佔有。此時如有其餘進程請求該資源,則請求進程只能等待。
不剝奪條件:進程所得到的資源在未使用完畢以前,不能被其餘進程強行奪走,即只能 由得到該資源的進程本身來釋放(只能是主動釋放)。
請求和保持條件:進程已經保持了至少一個資源,但又提出了新的資源請求,而該資源 已被其餘進程佔有,此時請求進程被阻塞,但對本身已得到的資源保持不放。
循環等待條件:存在一種進程資源的循環等待鏈,鏈中每個進程已得到的資源同時被 鏈中下一個進程所請求。即存在一個處於等待狀態的進程集合{Pl, P2, ..., pn},其中Pi等 待的資源被P(i+1)佔有(i=0, 1, ..., n-1),Pn等待的資源被P0佔有。
避免死鎖:1.加鎖順序(線程按照必定的順序加鎖)2.加鎖時限(線程嘗試獲取鎖的時候加上必定的時限,超過期限則放棄對該鎖的請求,並釋放本身佔有的鎖)3.死鎖檢測https://blog.csdn.net/ls5718/article/details/51896159