120個java最常見的面試題,(上)

文章目錄

1. 什麼是 Java 虛擬機?爲什麼 Java 被稱作是“平臺無關的編程語言”?

Java 虛擬機是一個可以執行 Java 字節碼的虛擬機進程。Java 源文件被編譯成能被 Java 虛擬
機執行的字節碼文件。
Java 被設計成允許應用程序可以運行在任意的平臺,而不需要程序員爲每一個平臺單獨重寫
或者是重新編譯。Java 虛擬機讓這個變爲可能,因爲它知道底層硬件平臺的指令長度和其特性。

2. JDK 和 JRE 的區別是什麼?

Java 運行時環境(JRE)是將要執行 Java 程序的 Java 虛擬機。它同時也包含了執行 applet 需要 的瀏覽器插件。
Java 開發工具包(JDK)是完整的 Java 軟件開發包,包含了 JRE,編譯器和其他的工具(比如:JavaDoc,Java調試器),可以讓開發者開發、編譯、執行 Java 應用程序。

3. ”static”關鍵字是什麼意思?Java 中是否可以覆蓋(override)一個 private 或者是static 的方法?

“static”關鍵字表明一個成員變量或者是成員方法可以在沒有所屬的類的實例變量的情況下被訪問。
Java 中 static 方法不能被覆蓋,因爲方法覆蓋是基於運行時動態綁定的,而 static 方法是編 譯時靜態綁
定的。static 方法跟類的任何實例都不相關,所以概念上不適用。private 也是不支持覆蓋的,因爲私有的成員外界是看不到的所以也就不存在覆蓋的問題。

4. 是否可以在 static 環境中訪問非 static 變量?

static 變量在 Java 中是屬於類的,它在所有的實例中的值是一樣的。當類被 Java 虛擬機載入 的時候,
會對 static 變量進行初始化。如果你的代碼嘗試不用實例來訪問非 static 的變量,編譯器會報錯,因爲這些變量還沒有被創建出來,還沒有跟任何實例關聯上。

5. Java 支持的數據類型有哪些?什麼是自動拆裝箱?

Java 語言支持的 8 中基本數據類型是:
整型:byte short int long
浮點型:float double
布爾型:boolean
字符型:char
自動裝箱是 Java 編譯器在基本數據類型和對應的對象包裝類型之間做的一個轉化。比如:把 int 轉化
成 Integer,double 轉化成 double,等等。反之就是自動拆箱。

6. Java 中的方法重寫(Overriding)和方法重載(Overloading)是什麼意思?

重寫方法的規則:(方法重寫也稱爲方法覆蓋)

  1. 參數列表必須完全與被重寫的方法相同,否則不能稱其爲重寫而是重載。
  2. 返回的類型必須一直與被重寫的方法的返回類型相同,否則不能稱其爲重寫而是重載。
  3. 訪問修飾符的限制一定要大於被重寫方法的訪問修飾符(public>protected>default>private)
  4. 重寫方法一定不能拋出新的檢查異常或者比被重寫方法申明更加寬泛的檢查型異常。例如:父類的一個方法申明瞭一個檢查異常 IOException,在重寫這個方法是就不能拋出 Exception,只能拋出
    IOException 的子類異常,可以拋出非檢查異常。

而重載的規則:

  1. 必須具有不同的參數列表;
  2. 可以有不責罵的返回類型,只要參數列表不同就可以了;
  3. 可以有不同的訪問修飾符;
  4. 可以拋出不同的異常;

重寫與重載的區別在於:

重寫多態性起作用,對調用被重載過的方法可以大大減少代碼的輸入量,同一個方法名只要往裏面傳遞不同的參數就可以擁有不同的功能或返回值。
用好重寫和重載可以設計一個結構清晰而簡潔的類,可以說重寫和重載在編寫代碼過程中的作用非同一般.

7. Java 中,什麼是構造函數?什麼是構造函數重載?什麼是複製構造函數?

當新對象被創建的時候,構造函數會被調用。每一個類都有構造函數。在程序員沒有給類提供構造函數的情況下,Java 編譯器會爲這個類創建一個默認的構造函數。
Java 中構造函數重載和方法重載很相似。可以爲一個類創建多個構造函數。每一個構造函數 必須有它自
己唯一的參數列表。
Java 不支持像 C++中那樣的複製構造函數,這個不同點是因爲如果你不自己寫構造函數的情況下,Java 不
會創建默認的複製構造函數。

8. Java 支持多繼承麼?支持多實現嗎?

不支持,Java 不支持多繼承。每個類都只能繼承一個類,但是可以實現多個接口。

9. 接口可以繼承接口嗎?如果可以繼承 請列舉一個案例?

可以,List 繼承 Collection

10. 接口和抽象類的區別是什麼?

接口和抽象類有什麼區別
你選擇使用接口和抽象類的依據是什麼?
接口和抽象類的概念不一樣。接口是對動作的抽象,抽象類是對根源的抽象。
抽象類表示的是,這個對象是什麼。接口表示的是,這個對象能做什麼。比如,蘇格蘭摺耳貓,布偶貓,這兩個類(如果是類的話„„),他們的抽象類是貓。說明,他們都是貓。 貓可以吃東西,耗子也可以吃東西,你可以把“吃東西”定義成一個接口,然後讓這些類去實現它。所以,在高級語言上,一個類只能繼承一個類(抽象類)(正如貓不可能同時是生物和非生物),但是可以
實現多個接口(吃飯接口、走路接口)。
第一點.接口是抽象類的變體,接口中所有的方法都是抽象的。而抽象類是聲明方法的存在而不去實現它的類。
第二點.接口可以多繼承,抽象類不行
第三點.接口定義方法,不能實現,而抽象類可以實現部分方法。
第四點.接口中基本數據類型爲 static 而抽類象不是的。
第五點.Java 接口中聲明的變量默認都是 final 的。抽象類可以包含非 final 的變量。
第六點.Java 接口中的成員函數默認是 public 的。抽象類的成員函數可以是 private,protected 或者
是 public。
當你關注一個事物的本質的時候,用抽象類;當你關注一個操作的時候,用接口。
抽象類的功能要遠超過接口,但是,定義抽象類的代價高。因爲高級語言來說(從實際設計上來說也是)
每個類只能繼承一個類。在這個類中,你必須繼承或編寫出其所有子類的
所有共性。雖然接口在功能上會弱化許多,但是它只是針對一個動作的描述。而且你可以在一個類中同時實現多個接口。在設計階段會降低難度的。
接口是絕對抽象的,不可以被實例化。抽象類也不可以被實例化

11. 什麼是值傳遞和引用傳遞?

對象被值傳遞,意味着傳遞了對象的一個副本。因此,就算是改變了對象副本,也不會影響 源對象的值。

對象被引用傳遞,意味着傳遞的並不是實際的對象,而是對象的引用。因此,外部對引用對 象所做的改變
會反映到所有的對象上。

12. 進程和線程的區別是什麼?

進程是執行着的應用程序,而線程是進程內部的一個執行序列。一個進程可以有多個線程。
線程又叫做輕量級進程。

13. 創建線程有幾種不同的方式?你喜歡哪一種?爲什麼?

有三種方式可以用來創建線程:
繼承 Thread 類
實現 Runnable 接口
應用程序可以使用 Executor 框架來創建線程池
實現 Runnable 接口這種方式更受歡迎,因爲這不需要繼承 Thread 類。在應用設計中已經繼承了別的對象的情況下,這需要多繼承(而 Java 不支持多繼承),只能實現接口。同時,線程池也是非常高效的,很容易實現和使用。

14. 概括的解釋下線程的幾種可用狀態。

線程在執行過程中,可以處於下面幾種狀態:
就緒(Runnable):線程準備運行,不一定立馬就能開始執行。
運行中(Running):進程正在執行線程的代碼。
等待中(Waiting):線程處於阻塞的狀態,等待外部的處理結束。 睡眠中
(Sleeping):線程被強制睡眠。
I/O 阻塞(Blocked on I/O):等待 I/O 操作完成。
同步阻塞(Blocked on Synchronization):等待獲取鎖。
死亡(Dead):線程完成了執行。

15. 同步方法和同步代碼塊的區別是什麼?

在 Java 語言中,每一個對象有一把鎖。線程可以使用 synchronized 關鍵字來獲取對象上的鎖。

synchronized 關鍵字可應用在方法級別(粗粒度鎖)或者是代碼塊級別(細粒度鎖)。

16. 在監視器(Monitor)內部,是如何做線程同步的?程序應該做哪種級別的同步?

監視器和鎖在 Java 虛擬機中是一塊使用的。監視器監視一塊同步代碼塊,確保一次只有一個線程執行同步代碼塊。每一個監視器都和一個對象引用相關聯。線程在獲取鎖之前不允許執行同步代碼。

17. 什麼是死鎖(deadlock)?

兩個進程都在等待對方執行完畢才能繼續往下執行的時候就發生了死鎖。結果就是兩個進程都陷入了無限的等待中。

18. 如何確保 N 個線程可以訪問 N 個資源同時又不導致死鎖?

使用多線程的時候,一種非常簡單的避免死鎖的方式就是:指定獲取鎖的順序,並強制線程

按照指定的順序獲取鎖。因此,如果所有的線程都是以同樣的順序加鎖和釋放鎖,就不會出 現死鎖了。

19. Java 集合類框架的基本接口有哪些?

Java 集合類提供了一套設計良好的支持對一組對象進行操作的接口和類。Java 集合類裏面最
基本的接口有:
Collection:代表一組對象,每一個對象都是它的子元素。
Set:不包含重複元素的 Collection。
List:有順序的 collection,並且可以包含重複元素。
Map:可以把鍵(key)映射到值(value)的對象,鍵不能重複。

20. 爲什麼集合類沒有實現 Cloneable 和 Serializable 接口?

集合類接口指定了一組叫做元素的對象。集合類接口的每一種具體的實現類都可以選擇以它自己的方式對元素進行保存和排序。有的集合類允許重複的鍵,有些不允許。

21. 什麼是迭代器(Iterator)?

Iterator 接口提供了很多對集合元素進行迭代的方法。每一個集合類都包含了可以返回迭代器實例的 迭代方法。迭代器可以在迭代的過程中刪除底層集合的元素。
克隆(cloning)或者是序列化(serialization)的語義和含義是跟具體的實現相關的。因此,應該由集合類的具體實現來決定如何被克隆或者是序列化。

22. Iterator 和 ListIterator 的區別是什麼?

下面列出了他們的區別:
Iterator 可用來遍歷 Set 和 List 集合,但是 ListIterator 只能用來遍歷 List。
Iterator 對集合只能是前向遍歷,ListIterator 既可以前向也可以後向。
ListIterator 實現了 Iterator 接口,幷包含其他的功能,比如:增加元素,替換元素,獲取前
一個和後一個元素的索引,等等。

23. 快速失敗(fail-fast)和安全失敗(fail-safe)的區別是什麼?

Iterator 的安全失敗是基於對底層集合做拷貝,因此,它不受源集合上修改的影響。java.util 包下面的所有的集合類都是快速失敗的,而 java.util.concurrent 包下面的所有的類都是安全 失敗的。快速失敗的迭代器會拋出 ConcurrentModificationException 異常,而安全失敗的迭 代器永遠不會拋出這樣的異常。

24. Java 中的 HashMap 的工作原理是什麼?

Java 中的 HashMap 是以鍵值對(key-value)的形式存儲元素的。HashMap 需要一個 hash 函數,它使用 hashCode()和 equals()方法來向集合/從集合添加和檢索元素。當調用 put()方法的時候,HashMap 會計算 key 的 hash 值,然後把鍵值對存儲在集合中合適的索引上。如果 key 已經存在了,value 會被更新成新值。HashMap 的一些重要的特性是它的容量(capacity),負 載因子(load factor)和擴容極限(threshold resizing)。

25. hashCode()和 equals()方法的重要性體現在什麼地方?

Java中的 HashMap 使用 hashCode()和 equals()方法來確定鍵值對的索引,當根據鍵獲取值的時候也會用到這兩個方法。如果沒有正確的實現這兩個方法,兩個不同的鍵可能會有相同的hash 值,因此,可能會被集合認爲是相等的。而且,這兩個方法也用來發現重複元素。所以這兩個方法的實現對 HashMap 的精確性和正確性是至關重要的。

26. HashMap 和 Hashtable 有什麼區別?

Java 中的 HashMap 使用 hashCode()和 equals()方法來確定鍵值對的索引,當根據鍵獲取值的時候也會用到這兩個方法。如果沒有正確的實現這兩個方法,兩個不同的鍵可能會有相同的hash 值,因此,可能會被集合認爲是相等的。而且,這兩個方法也用來發現重複元素。所以這兩個方法的實現對 HashMap 的精確性和正確性是至關重要的。

26. HashMap 和 Hashtable 有什麼區別?

HashMap 和 Hashtable 都實現了 Map 接口,因此很多特性非常相似。但是,他們有以下不同點:
HashMap 允許鍵和值是 null,而 Hashtable 不允許鍵或者值是 null。
Hashtable 是同步的,而 HashMap 不是。因此,HashMap 更適合於單線程環境,而 Hashtable 適合於多線程環境。
HashMap 提供了可供應用迭代的鍵的集合,因此,HashMap 是快速失敗的。另一方面,
Hashtable 提供了對鍵的列舉(Enumeration)。
一般認爲 Hashtable 是一個遺留的類。

27. 數組(Array)和列表(ArrayList)有什麼區別?什麼時候應該使用 Array 而不是ArrayList ?

下面列出了 Array 和 ArrayList 的不同點:
Array 可以包含基本類型和對象類型,ArrayList 只能包含對象類型。
Array 大小是固定的,ArrayList 的大小是動態變化的。
ArrayList 提供了更多的方法和特性,比如:addAll(),removeAll(),iterator()等等。
對於基本類型數據,集合使用自動裝箱來減少編碼工作量。但是,當處理固定大小的基本數據類型的時候,這種方式相對比較慢。

28. ArrayList 和 LinkedList 有什麼區別?

ArrayList 和 LinkedList 都實現了 List 接口,他們有以下的不同點:
ArrayList 是基於索引的數據接口,它的底層是數組。它可以以 O(1)時間複雜度對元素進行隨機訪問。與此對應,LinkedList 是以元素列表的形式存儲它的數據,每一個元素都和它的前 一個和後一個元素鏈接在一起,在這種情況下,查找某個元素的時間複雜度是 O(n)。
相對於 ArrayList ,LinkedList 的插入,添加,刪除操作速度更快,因爲當元素被添加到集合任意位置的時候,不需要像數組那樣重新計算大小或者是更新索引。
LinkedList 比 ArrayList 更佔內存,因爲 LinkedList 爲每一個節點存儲了兩個引用,一個指向前一個元素,一個指向下一個元素。
也可以參考 ArrayList vs. LinkedList 。

29. Comparable 和 Comparator 接口是幹什麼的?列出它們的區別。

Java 提供了只包含一個 compareTo()方法的 Comparable 接口。這個方法可以個給兩個對象排序。具體來說,它返回負數,0,正數來表明輸入對象小於,等於,大於已經存在的對象。
Java 提供了包含 compare()和 equals()兩個方法的 Comparator 接口。compare()方法用來給兩個輸入參數排序,返回負數,0,正數表明第一個參數是小於,等於,大於第二個參數。equals()方法需要一個對象作爲參數,它用來決定輸入參數是否和comparator 相等。只有當輸入參數也是一個 comparator 並且輸入參數和當前 comparator 的排序結果是相同的時候,這個方
法才返回 true 。

30. 什麼是 Java 優先級隊列(Priority Queue)?

PriorityQueue 是一個基於優先級堆的無界隊列,它的元素是按照自然順序(natural order)排序的。在創建的時候,我們可以給它提供一個負責給元素排序的比較器。PriorityQueue 不允許

null 值,因爲他們沒有自然順序,或者說他們沒有任何的相關聯的比較器。最後,PriorityQueue 不是線程安全的,入隊和出隊的時間複雜度是 O(log(n))。

31. 你瞭解大 O 符號(big-O notation)麼?你能給出不同數據結構的例子麼?

大 O 符號描述了當數據結構裏面的元素增加的時候,算法的規模或者是性能在最壞的場景下有多麼好。 大 O 符號也可用來描述其他的行爲,比如:內存消耗。因爲集合類實際上是數據結構,我 們一般使用大 O 符號基於時間,內存和性能來選擇最好的實現。大 O 符號可以對大量數據的性能給出一個很好的說明。

32. 如何權衡是使用無序的數組還是有序的數組?

有序數組最大的好處在於查找的時間複雜度是 O(log n),而無序數組是 O(n)。有序數組的缺點是插入操作的時間複雜度是 O(n),因爲值大的元素需要往後移動來給新元素騰位置。相反,無序數組的插入時間複雜度是常量 O(1)。

33. Java 集合類框架的最佳實踐有哪些?

根據應用的需要正確選擇要使用的集合的類型對性能非常重要,比如:假如元素的大小是固定的,而且能事先知道,我們就應該用 Array 而不是 ArrayList 。有些集合類允許指定初始容量。因此,如果我們能估計出存儲的元素的數目,我們可以設置初始容量來避免重新計算 hash 值或者是擴容。爲了類型安全,可讀性和健壯性的原因總是要使用泛型。同時,使用泛型還可以避免運行時的 ClassCastException。
使用 JDK 提供的不變類(immutable class)作爲 Map 的鍵可以避免爲我們自己的類實現hashCode()和 equals()方法。編程的時候接口優於實現。
底層的集合實際上是空的情況下,返回長度是 0 的集合或者是數組,不要返回 null

34. Enumeration 接口和 Iterator 接口的區別有哪些?

Enumeration 速度是 Iterator 的 2 倍,同時佔用更少的內存。但是,Iterator 遠遠比 Enumeration 安全,因爲其他線程不能夠修改正在被 iterator 遍歷的集合裏面的對象。同時,Iterator 允許調用者刪除底層集合裏面的元素,這對 Enumeration 來說是不可能的。

35. HashSet 和 TreeSet 有什麼區別?

HashSet 是由一個 hash 表來實現的,因此,它的元素是無序的。add() ,remove(),contains() 方法的時間複雜度是 O(1)。
另一方面,TreeSet 是由一個樹形的結構來實現的,它裏面的元素是有序的。因此,add(),remove(),contains()方法的時間複雜度是 O(logn)。

36. Java 中垃圾回收有什麼目的?什麼時候進行垃圾回收?

垃圾回收的目的是識別並且丟棄應用不再使用的對象來釋放和重用資源。

37. System.gc()和 Runtime.gc()會做什麼事情?

這兩個方法用來提示 JVM 要進行垃圾回收。但是,立即開始還是延遲進行垃圾回收是取決於 JVM 的。

38. finalize()方法什麼時候被調用?析構函數(finalization)的目的是什麼?

在釋放對象佔用的內存之前,垃圾收集器會調用對象的 finalize()方法。一般建議在該方法中釋放對象持有的資源。

39. 如果對象的引用被置爲 null,垃圾收集器是否會立即釋放對象佔用的內存?

不會,在下一個垃圾回收週期中,這個對象將是可被回收的。

40. Java 堆的結構是什麼樣子的?什麼是堆中的永久代(Perm Gen space)?

JVM 的堆是運行時數據區,所有類的實例和數組都是在堆上分配內存。它在 JVM 啓動的時候被創建。對象所佔的堆內存是由自動內存管理系統也就是垃圾收集器回收。堆內存是由存活和死亡的對象組成的。存活的對象是應用可以訪問的,不會被垃圾回收。死亡的對象是應用不可訪問尚且還沒有被垃圾收集器回收掉的對象。一直到垃圾收集器把這些對象回收掉之前,他們會一直佔據堆內存空間。

41. 串行(serial)收集器和吞吐量(throughput)收集器的區別是什麼?

吞吐量收集器使用並行版本的新生代垃圾收集器,它用於中等規模和大規模數據的應用程序。而串行收集器對大多數的小應用(在現代處理器上需要大概 100M 左右的內存)就足夠了。

42. 在 Java 中,對象什麼時候可以被垃圾回收?

當對象對當前使用這個對象的應用程序變得不可觸及的時候,這個對象就可以被回收了。

43. JVM 的永久代中會發生垃圾回收麼?

垃圾回收不會發生在永久代,如果永久代滿了或者是超過了臨界值,會觸發完全垃圾回收 (Full GC)。如果你仔細查看垃圾收集器的輸出信息,就會發現永久代也是被回收的。這就是爲什麼正確的永久代大小對避免 Full GC 是非常重要的原因。請參考下 Java8 :從永久代到元數據區
(譯者注:Java8 中已經移除了永久代,新加了一個叫做元數據區的 native 內存區)

擴展連接:https://blog.csdn.net/weixin_44395707/category_9792353.html

未完待續。。。

PDF文檔已經發布到公衆號,如果需要可以掃描下方的二維碼回覆資源010領取
在這裏插入圖片描述

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