Java基礎之面試常見問題

1.equals與==的區別?
==是判斷兩個變量或實例是不是指向同一內存空間,equals是判斷兩個變量或實例所指的內存空間的值是不是相同。

2.Object有哪些公用的方法?
equals( ),測試兩個對象是否相等。
clone( ),進行對象拷貝
getClass( )返回和當前對象相關的Class對象

3.HashCode的作用,與equals有什麼區別?
同樣用於鑑定2個對象是都相等的,Java幾個中有list和set兩類,其中set不予許元素重複實現,那麼這個不允許重複實現的方法,如果用equal去比較的話,如果存在1000個元素,你new一個新元素出來,需要去調用1000次equal去逐個和他們比較是否是同一個對象,這樣會大大降低效率。hashcode實際上是返回對象的存儲地址,如果這個位置上沒有元素,就把元素直接存儲在上面,如果這個位置上已經存在元素,這個時候纔去調用equal方法與新元素進行比較,相同的話就不存在了,散列到其他地址上。

4.Override和Overload的含義區別?
Overload是重新加載,它可以表現類的多態性,可以是函數裏面可以有相同的函數名,但是參數名、返回值、類型不能相同;或者說可以改變參數、類型、返回值但是函數名字必須不變。
Override是重寫的意思,在子類繼承分類的時候子類中可以定義某方法與其父類有相同的名稱和參數,當子類在調用這個函數時自動調用子類的方法,而弗雷相當於被覆蓋(重寫)了。

5.抽象類和接口的區別?
一個類只能繼承單個類,但是可以實現多個接口,接口強調特定功能的實現,而抽象類強調所屬關係,抽象類中的所有方法並不一定要是抽象的,你可以選擇在抽象類中實現一些基本的方法,而接口要求所有的方法都必須是抽象的。

6.解析XML的幾種方式的原理與特點:DOM、SAX、PULL
DOM:消耗內存,先把xml文檔都讀取到內存中,然後再用DOM API來訪問樹形結構,並獲取數據。這個寫起來很簡單,但是很消耗內存。要是數據過大,手機不夠牛逼,可能手機直接死機。
SAX:解析效率高,佔用內存少,基於事件驅動的,更簡單地說就是對文檔進行順序掃描,當掃描到文檔(doucment)開始與結束、元素開始與結束、文檔結束等地方時通知事件處理函數,由事件處理函數做相應動作,然後繼續同樣的掃描,直至文檔結束。
PULL:與SAX類似,也是基於事件驅動,我們可以調用它的next()方法,來獲取下一個解析事件(就是開始文檔、結束文檔、開始標籤、結束標籤),當處於某個元素時,可以調用XmlPullParser的getAttribute()方法來獲取屬性的值,也可調用它的nextText()獲取本節點的值。

7.Java垃圾回收與內存分配策略

7.1垃圾回收是什麼?
就是釋放那些不再持有引用的對象的內存
7.2怎麼判斷一個對象是否需要收集?
引用計數:給對象添加引用計數器,每當有一個地方引用它,計數器就加1,引用失敗減1,當引用次數變爲0時就將其釋放。
可達性分析:以根集對象爲起始點進行搜索,如果有對象不可達的話,即是垃圾對象。
*根集:一般包括Java棧中引用的對象,方法區常量池中引用的對象,本地方法中引用的對象。
7.3垃圾回收機制
1.標記-清除:
算法和名字一樣,分爲兩個階段:標記和清除,標記所有需要回收的對象,然後統一清除回收。
2.標記-壓縮
此算法結合了“標記-清除”和“複製”兩個算法的優點,也是分兩階段,第一階段,從根節點開始標記所有被引用對象。第二階段遍歷整個堆,清除未標記對象並且把存活對象“壓縮”到堆的其中一塊,按順序排放。
3.複製
內存空間劃分爲兩個相等的區域A,B,將A中正在使用對象複製到B中,每次只處理正在使用中的對象。
4.分代收集算法
把內存空間分爲兩個或者多個域,如年輕代和老年代,年輕代的特點是對象會很快被回收,因此在年輕代使用效率比較高的算法。當一個對象經過幾次回收後依然存活,對象就會被放入成爲老年的內存空間,老年代則採用標記-壓縮算法。

8 . ArrayList、 LinkedList、 Vector 的區別
1.ArrayList 和 Vector 底層是採用數組方式存儲數據, Vector 由於使用了 synchronized方法(線程安全)所以性能上比 ArrayList 要差。
2.LinkedList 使用雙向鏈表實現存儲,隨機存取比較慢
3.HashMap 的底層源碼實現:當我們往 HashMap 中 put 元素的時候,先根據 key 的hashCode 重新計算 hash 值,根據 hash 值得到這個元素在數組中的位置(即下標),如果數組該位置上已經存放有其他元素了,那麼在這個位置上的元素將以鏈表的形式存放,新加入的放在鏈頭,最先加入的放在鏈尾。如果數組該位置上沒有元素,就直接將該元素放到此數組中的該位置上。
4.Fail-Fast 機制 :在使用迭代器的過程中有其他線程修改了 map,那麼將拋出ConcurrentModificationException,這就是所謂 fail-fast 機制。這一機制在源碼中的實現是通過 modCount 域, modCount 顧名思義就是修改次數,對 HashMap 內容的修改都將增加這個值,那麼在迭代器初始化過程中會將這個值賦給迭代器的expectedModCount。在迭代過程中,判斷 modCount 跟 expectedModCount 是否相等,如果不相等就表示已經有其他線程修改了 Map

9 . HashMap 和 HashTable 的區別
HashTable 比較老,是基於 Dictionary 類實現的, HashTable 則是基於 Map 接口實現的HashTable 是線程安全的, HashMap 則是線程不安全的 HashMap 可以讓你將空值作爲一個表的條目的 key 或 value.

爲什麼哈希表的容量一定要是2的整數次冪?
首先,length爲2的整數次冪的話,h&(length-1)就相當於對length取模,這樣便保證了散列的均勻,同時也提升了效率;其次,length爲2的整數次冪的話,爲偶數,這樣length-1爲奇數,奇數的最後一位是1,這樣便保證了h&(length-1)的最後一位可能爲0,也可能爲1(這取決於h的值),即與後的結果可能爲偶數,也可能爲奇數,這樣便可以保證散列的均勻性,而如果length爲奇數的話,很明顯length-1爲偶數,它的最後一位是0,這樣h&(length-1)的最後一位肯定爲0,即只能爲偶數,這樣任何hash值都只會被散列到數組的偶數下標位置上,這便浪費了近一半的空間,因此,length取2的整數次冪,是爲了使不同hash值發生碰撞的概率較小,這樣就能使元素在哈希表中均勻地散列。

10 . 什麼是線程池,線程池的作用是什麼
答:線程池的基本思想還是一種對象池的思想,開闢一塊內存空間,裏面存放了衆多(未死亡)的線程,池中線程執行調度由池管理器來處理。當有線程任務時,從池中取一個,執行完成後線程對象歸池,這樣可以避免反覆創建線程對象所帶來的性能開銷,節省了系統的資源。就好比原來去食堂打飯是每個人看誰搶的贏,誰先搶到誰先吃,有了線程吃之後,就是排好隊形,今天我跟你關係好,你先來吃飯。比如:一個應用要和網絡打交道,有很多步驟需要訪問網絡,爲了不阻塞主線程,每個步驟都創建個線程,在線程中和網絡交互,用線程池就變的簡單,線程池是對線程的一種封裝,讓線程用起來更加簡便,只需要創一個線程池,把這些步驟像任務一樣放進線程池,在程序銷燬時只要調用線程池的銷燬函數即可。
單個線程的弊端:
a. 每次new Thread新建對象性能差
b. 線程缺乏統一管理,可能無限制新建線程,相互之間競爭,及可能佔用過多系統資源導致死機或者OOM
c. 缺乏更多功能,如定時執行、定期執行、線程中斷。
java提供的四種線程池的好處在於:
a. 重用存在的線程,減少對象創建、消亡的開銷,性能佳。
b. 可有效控制最大併發線程數,提高系統資源的使用率,同時避免過多資源競爭,避免堵塞。
c. 提供定時執行、定期執行、單線程、併發數控制等功能。

11. Java 線程池
Java通過Executors提供四種線程池,分別爲:
newCachedThreadPool創建一個可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閒線程,若無可回收,則新建線程。
newFixedThreadPool 創建一個定長線程池,可控制線程最大併發數,超出的線程會在隊列中等待。
newScheduledThreadPool 創建一個定長線程池,支持定時及週期性任務執行。
newSingleThreadExecutor 創建一個單線程化的線程池,它只會用唯一的工作線程來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先級)執行。
(1). newCachedThreadPool
創建一個可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閒線程,若無可回收,則新建線程。線程池爲無限大,當執行第二個任務時第一個任務已經完成,會複用執行第一個任務的線程,而不用每次新建線程。
(2). newFixedThreadPool
創建一個定長線程池,可控制線程最大併發數,超出的線程會在隊列中等待。
(3) newScheduledThreadPool
創建一個定長線程池,支持定時及週期性任務執行。ScheduledExecutorService比Timer更安全,功能更強大
(4)、newSingleThreadExecutor
創建一個單線程化的線程池,它只會用唯一的工作線程來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先級)執行

12.靜態塊、靜態變量、構造函數、構造方法的加載順序?
靜態代碼塊:是在當類被載入(內存)時,(最先被調用的),靜態代碼塊被執行,且之被執行一次,靜態塊常用來執行類屬性的初始化。執行類的載入之前就會調用。
非靜態代碼塊:是在當類的對象被創建載入(內存)時,(最先被調用的),每創建一個對象,即每載入一個對象,非靜態代碼塊都執行一次。執行類對象的載入之前就會調用。
靜態變量:與靜態代碼塊一樣,也是在類加載時就會被自動執行,屬於整個類的屬性。
靜態方法、非靜態方法:都是進行方法調用時才執行,調用靜態方法,前提是:類已經加載到內存中;調用非靜態方法,前提是類的對象已經加載到內存中。

那麼,類什麼時候被加載/類加載時機:【即類被使用時,就會加載】
第一:生成該類對象的時候,會加載該類及該類的所有父類;
第二:訪問該類的靜態成員的時候;
第三:class.forName(“類名”);

代碼塊是自動的,方法是被動的。

作用:靜態代碼塊可以用來初始化一些項目最常用的變量和對象;靜態方法可以用作不創建對象也可以能需要執行的代碼。

執行順序可以簡單記爲:靜態區–>非靜態區–>構造方法–>使用對象…(唯一需重點注意的是靜態區代碼僅在該類被加載時執行一次,之後無論該類被實例化多少次,靜態區代碼都不會再執行!)

13.Lock和synchronized的區別?
1)Lock不是Java語言內置的,synchronized是Java語言的關鍵字,因此是內置特性。Lock是一個類,通過這個類可以實現同步訪問;
2)Lock和synchronized有一點非常大的不同,採用synchronized不需要用戶去手動釋放鎖,當synchronized方法或者synchronized代碼塊執行完之後,系統會自動讓線程釋放對鎖的佔用;而Lock則必須要用戶去手動釋放鎖,如果沒有主動釋放鎖,就有可能導致出現死鎖現象。

Lock和synchronized有以下幾點不同:
  1)Lock是一個接口,而synchronized是Java中的關鍵字,synchronized是內置的語言實現;
  2)synchronized在發生異常時,會自動釋放線程佔有的鎖,因此不會導致死鎖現象發生;而Lock在發生異常時,如果沒有主動通過unLock()去釋放鎖,則很可能造成死鎖現象,因此使用Lock時需要在finally塊中釋放鎖;
  3)Lock可以讓等待鎖的線程響應中斷,而synchronized卻不行,使用synchronized時,等待的線程會一直等待下去,不能夠響應中斷;
  4)通過Lock可以知道有沒有成功獲取鎖,而synchronized卻無法辦到。
  5)Lock可以提高多個線程進行讀操作的效率。
  在性能上來說,如果競爭資源不激烈,兩者的性能是差不多的,而當競爭資源非常激烈時(即有大量線程同時競爭),此時Lock的性能要遠遠優於synchronized。所以說,在具體使用時要根據適當情況選擇。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章