java基礎階段幾個必會面試題

1.說出你對面向對象的理解

       在我理解,面向對象是向現實世界模型的自然延伸,這是一種“萬物皆對象”的編程思想。在現實生活中的任何物體都可以歸為一類事物,而每一個個體都是一類事物的實例。面向對象的編程是以對象為中心,以消息為驅動,所以程序=對象+消息。
       面向對象有三大特性,封裝、繼承和多態。
       封裝就是將一類事物的屬性和行為抽象成一個類,使其屬性私有化,行為公開化,提高了數據的隱秘性的同時,使代碼模塊化。這樣做使得代碼的復用性更高。
       繼承則是進一步將一類事物共有的屬性和行為抽象成一個父類,而每一個子類是一個特殊的父類–有父類的行為和屬性,也有自己特有的行為和屬性。這樣做擴展了已存在的代碼塊,進一步提高了代碼的復用性。
       如果說封裝和繼承是為了使代碼重用,那麼多態則是為了實現接口重用。多態的一大作用就是為了解耦–為了解除父子類繼承的耦合度。如果說繼承中父子類的關係式IS-A的關係,那麼接口和實現類之之間的關係式HAS-A。簡單來說,多態就是允許父類引用(或接口)指向子類(或實現類)對象。很多的設計模式都是基於面向對象的多態性設計的。

2.JVM的內存區及其GC算法

參考:

元空間:jdk1.8取消了持久代新增了元空間,並將方法區放在元空間中

3.集合框架下的各種接口和實現類有哪些,分別有啥特點

參考:

4.string類有啥特點,有哪些常用的API

1.String類對象的相等判斷使用equals()方法完成,“==”實現的是地址數值的比較
2.字符串內容一旦聲明則不可改變,String類對象內容的改變是依靠引用關係的變更實現的。
3.String類有兩種實例化方式,使用直接賦值可以不產生垃圾空間,並且可以自動入池,不要使用構造方法賦值。

一些常見API:

 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:適用多線程下在字符緩衝區進行大量操作的情況

為什麼StringBuilder是不安全的?

下面是StringBuilder 的append方法源碼

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.抽象類和接口的區別?有了抽象類為啥還要接口?

1.一類可以實現多個接口但只能繼承自一個抽象類,從抽象類派生出的子類同樣可以實現接口,從而,我們能得出一個結論:接口是為Java實現多繼承而存在的
2.抽象類中可以存在非抽象的方法,可接口不能存在非抽象的方法,並且接口裡面的方法只是一個聲明,必須用 public abstract來修飾,沒有具體的實現
3.抽象方法中的成員變量可以被不同的修飾符修飾,而接口中的成員變量默認都是靜態常量
4.抽象類是對對象進行的抽象,而接口是一種行為規範,這一點是比較重要的.
(所以為什麼有了接口還要有抽象類)

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) 進程推進順序非法
進程在運行過程中,請求和釋放資源的順序不當,也同樣會導致死鎖。例如,併發進程 P1、P2分別保持了資源R1、R2,而進程P1申請資源R2,進程P2申請資源R1時,兩者都 會因為所需資源被佔用而阻塞。

四個產生死鎖的條件:
互斥條件:進程要求對所分配的資源(如打印機)進行排他性控制,即在一段時間內某 資源僅為一個進程所佔有。此時若有其他進程請求該資源,則請求進程只能等待。
不剝奪條件:進程所獲得的資源在未使用完畢之前,不能被其他進程強行奪走,即只能 由獲得該資源的進程自己來釋放(只能是主動釋放)。
請求和保持條件:進程已經保持了至少一個資源,但又提出了新的資源請求,而該資源 已被其他進程佔有,此時請求進程被阻塞,但對自己已獲得的資源保持不放。
循環等待條件:存在一種進程資源的循環等待鏈,鏈中每一個進程已獲得的資源同時被 鏈中下一個進程所請求。即存在一個處於等待狀態的進程集合{Pl, P2, …, pn},其中Pi等 待的資源被P(i+1)佔有(i=0, 1, …, n-1),Pn等待的資源被P0佔有。

避免死鎖:
1.加鎖順序(線程按照一定的順序加鎖)
2.加鎖時限(線程嘗試獲取鎖的時候加上一定的時限,超過時限則放棄對該鎖的請求,並釋放自己佔有的鎖)
3.死鎖檢測
參考:

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

您可能也會喜歡…