32道常見的Java基礎面試題

dotzhang發表於2018-08-29

內容整理自網上。

  1. 什麼是 Java 虛擬機器(JVM)?為什麼 Java 被稱作是“平臺無關的程式語言”?

    Java 虛擬機器是一個可以執行 Java 位元組碼的虛擬機器程式。Java 原始檔被編譯成能被 Java 虛擬機器執行的位元組碼檔案。

    Java 被設計成允許應用程式可以執行在任意的平臺,而不需要程式設計師為每一個平臺單獨重寫或者是重新編譯。Java 虛擬機器讓這個變為可能,因為它知道底層硬體平臺的指令長度和其他特性。

  2. JDK、JRE、JVM 分別是什麼關係?

    JDK 即為 Java 開發工具包,包含編寫 Java 程式所必須的編譯、執行等開發工具以及 JRE。開發工具如:用於編譯 Java 程式的 javac 命令、用於啟動 JVM 執行 Java 程式的 Java 命令、用於生成文件的 Javadoc 命令以及用於打包的 jar 命令等等。

    JRE 即為 Java 執行環境,提供了執行 Java 應用程式所必須的軟體環境,包含有 Java 虛擬機器(JVM)和豐富的系統類庫。系統類庫即為 Java 提前封裝好的功能類,只需拿來直接使用即可,可以大大的提高開發效率。

    JVM 即為 Java 虛擬機器,提供了位元組碼檔案(.class)的執行環境支援。 簡單說,就是 JDK 包含 JRE 包含 JVM。

  3. Java 支援的資料型別有哪些?

    Java 支援的資料型別包括基本資料型別和引用型別。基本資料型別如下。 整數值型:byte,short,int,long
    字元型:char
    浮點型別:float,double
    布林型:boolean
    整數預設 int 型,小數預設是 double 型。Float 和 long 型別的必須加字尾。比如 float f = 100f。

    首先知道 String 是引用型別不是基本型別,引用型別宣告的變數是指該變數在記憶體中實際儲存的是一個引用地址,實體在堆中。引用型別包括類、介面、陣列等。String 類還是 final 修飾的。

  4. 什麼是自動拆裝箱?

    自動裝箱和拆箱就是基本型別和引用型別之間的轉換,至於為什麼要轉換,因為基本型別轉換為引用型別後,就可以 new 物件,從而呼叫包裝類中封裝好的方法進行基本型別之間的轉換或者 toString(當然用類名直接呼叫也可以,便於一眼看出該方法是靜態的),還有就是如果集合中想存放基本型別,泛型的限定型別只能是對應的包裝型別。

  5. 什麼是物件導向?

    物件導向是一種思想,世間萬物都可以看做一個物件,這裡只討論物件導向程式設計(OOP),Java 是一個支援併發、基於類和麵向物件的計算機程式語言,物件導向軟體開發具有以下優點:程式碼開發模組化,更易維護和修改;程式碼複用性強;增強程式碼的可靠性和靈活性;增加程式碼的可讀性。

  6. 物件導向的四大基本特性?

    抽象:提取現實世界中某事物的關鍵特性,為該事物構建模型的過程。對同一事物在不同的需求下,需要提取的特性可能不一樣。得到的抽象模型中一般包含:屬性(資料)和操作(行為)。這個抽象模型我們稱之為類,對類進行例項化得到物件。

    封裝:封裝可以使類具有獨立性和隔離性,保證類的高內聚。只暴露給類外部或者子類必須的屬性和操作。類封裝的實現依賴類的修飾符(public、protected 和 private 等)。

    繼承:對現有類的一種複用機制。一個類如果繼承現有的類,則這個類將擁有被繼承類的所有非私有特性(屬性和操作)。這裡指的繼承包含:類的繼承和介面的實現。

    多型:多型是在繼承的基礎上實現的。多型的三個要素:繼承、重寫和父類引用指向子類物件。父類引用指向不同的子類物件時,呼叫相同的方法,呈現出不同的行為,就是類多型特性。多型可以分成編譯時多型和執行時多型。

    抽象、封裝、繼承和多型是物件導向的基礎。

  7. & 與 && 的區別?

    & 運算子有兩種用法:(1) 按位與,(2) 邏輯與。&& 運算子是短路與運算。

    邏輯與跟短路與的差別是非常巨大的,雖然二者都要求運算子左右兩端的布林值都是 true 整個表示式的值才是 true。&& 之所以稱為短路運算是因為,如果 && 左邊的表示式的值是 false,右邊的表示式會被直接短路掉,不會進行運算。

    很多時候我們可能都需要用 && 而不是 &,例如在驗證使用者登入時判定使用者名稱不是 null 而且不是空字串,應當寫為:

    username != null && !username.equals(“”)

    二者的順序不能交換,更不能用 & 運算子,因為第一個條件如果不成立,根本不能進行字串的 equals 比較,否則會產生 NullPointerException 異常。

    注意:邏輯或運算子(|)和短路或運算子(||)的差別也是如此。

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

    值傳遞是對基本型變數而言的,傳遞的是該變數的一個副本,改變副本不影響原變數。

    引用傳遞一般是對於物件型變數而言的,傳遞的是該物件地址的一個副本,並不是原物件本身。一般認為,Java 內的傳遞都是值傳遞,Java 中例項物件的傳遞是引用傳遞。

  9. 是否可以在 static 環境中訪問非 static 變數?

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

  10. Java 中的方法覆蓋(Overriding)和方法過載(Overloading)是什麼意思?

    Java 中的方法過載發生在同一個類裡面兩個或者是多個方法的方法名相同但是引數不同的情況。與此相對,方法覆蓋是說子類重新定義了父類的方法。方法覆蓋必須有相同的方法名,引數列表和返回型別。覆蓋者可能不會限制它所覆蓋的方法的訪問。

  11. Java 支援多繼承麼?

    Java 中類不支援多繼承,只支援單繼承(即一個類只有一個父類)。 但是 Java 中的介面支援多繼承,即一個子介面可以有多個父介面。(介面的作用是用來擴充套件物件的功能,一個子介面繼承多個父介面,說明子介面擴充套件了多個功能,當類實現介面時,類就擴充套件了相應的功能)。

  12. Java 中,什麼是構造方法?什麼是構造方法過載?什麼是複製構造方法?

    當新物件被建立的時候,構造方法會被呼叫。每一個類都有構造方法。在程式設計師沒有給類提供構造方法的情況下,Java 編譯器會為這個類建立一個預設的構造方法。

    Java 中構造方法過載和方法過載很相似。可以為一個類建立多個構造方法。每一個構造方法必須有它自己唯一的引數列表。

    Java 不支援像 C++ 中那樣的複製構造方法,這個不同點是因為如果你不自己寫構造方法的情況下,Java 不會建立預設的複製構造方法。

  13. 介面和抽象類的區別是什麼?

    從設計層面來說,抽象是對類的抽象,是一種模板設計,介面是行為的抽象,是一種行為的規範。

    Java 提供和支援建立抽象類和介面。它們的實現有共同點,不同點在於: 介面中所有的方法隱含的都是抽象的,而抽象類則可以同時包含抽象和非抽象的方法。

    類可以實現很多個介面,但是隻能繼承一個抽象類。類可以不實現抽象類和介面宣告的所有方法,當然,在這種情況下,類也必須得宣告成是抽象的。

    抽象類可以在不提供介面方法實現的情況下實現介面。 Java 介面中宣告的變數預設都是 final 的。抽象類可以包含非 final 的變數。Java 介面中的成員函式預設是 public 的。抽象類的成員函式可以是 private,protected 或者是 public。

    介面是絕對抽象的,不可以被例項化。抽象類也不可以被例項化,但是,如果它包含 main 方法的話是可以被呼叫的。也可以參考 JDK8 中抽象類和介面的區別。

  14. 用最有效率的方法計算 2 乘以 8?

    2 << 3(左移3位相當於乘以2的3次方,右移3位相當於除以2的3次方)。

  15. 手寫單例模式(餓漢和懶漢模式)和工廠模式?
    餓漢模式
    //餓漢式單例類.在類初始化時,已經自行例項化
    public class Singleton1 {
    //私有的預設構造子
    private Singleton1() {}
    //已經自行例項化
    private static final Singleton1 single = new Singleton1();
    //靜態工廠方法
    public static Singleton1 getInstance() {
    return single;
    }
    }
    懶漢模式
    //懶漢式單例類.在第一次呼叫的時候例項化
    public class Singleton2 {
    //私有的預設構造子
    private Singleton2() {}
    //注意,這裡沒有final
    private static Singleton2 single=null;
    //靜態工廠方法
    public synchronized static Singleton2 getInstance() {
    if (single == null) {
    single = new Singleton2();
    }
    return single;
    }
    }

    工廠模式,也可以參考之前的設計模式中的工廠模式,文末有連結。
    interface IFactory{
    public IProduct createProduct();
    }
    Class Factory implements IFactory{
    public IProduct createProduct()
    {
    return new Product();
    }
    }
    Public class client{
    Public Static void main (String [] args){
    IFactory factory=new Factory();
    IProduct product=factory.createProduct();
    product.ProductMethod();
    }
    }

  16. String和StringBuilder、StringBuffer的區別?

    Java 平臺提供了兩種型別的字串:String 和 StringBuffer/StringBuilder,它們可以儲存和操作字串。

    其中 String 是隻讀字串,也就意味著 String 引用的字串內容是不能被改變的。

    而 StringBuffer/StringBuilder 類表示的字串物件可以直接進行修改。StringBuilder 是 Java 5 中引入的,它和 StringBuffer 的方法完全相同,區別在於它是在單執行緒環境下使用的,因為它的所有方面都沒有被 synchronized 修飾,因此它的效率也比 StringBuffer 要高。

  17. Java 集合框架有哪些?說出一些集合框架的優點?

    每種程式語言中都有集合,最初的 Java 版本包含幾種集合類:Vector、Stack、HashTable 和 Array。隨著集合的廣泛使用,Java1.2 提出了囊括所有集合介面、實現和演算法的集合框架。在保證執行緒安全的情況下使用泛型和併發集合類,Java 已經經歷了很久。它還包括在 Java 併發包中,阻塞介面以及它們的實現。集合框架的部分優點如下: (1)使用核心集合類降低開發成本,而非實現我們自己的集合類。 (2)隨著使用經過嚴格測試的集合框架類,程式碼質量會得到提高。 (3)通過使用 JDK 附帶的集合類,可以降低程式碼維護成本。 (4)複用性和可操作性。

  18. 集合框架中的泛型有什麼優點?

    Java1.5 引入了泛型,所有的集合介面和實現都大量地使用它。泛型允許我們為集合提供一個可以容納的物件型別。因此,如果你新增其它型別的任何元素,它會在編譯時報錯。這避免了在執行時出現 ClassCastException,因為你將會在編譯時得到報錯資訊。泛型也使得程式碼整潔,我們不需要使用顯式轉換和 instanceOf 操作符。它也給執行時帶來好處,因為不會產生型別檢查的位元組碼指令。

  19. Java 集合框架的基礎介面有哪些?

    Collection 為集合層級的根介面。一個集合代表一組物件,這些物件即為它的元素。Java 平臺不提供這個介面任何直接的實現。

    Set 是一個不能包含重複元素的集合。這個介面對數學集合抽象進行建模,被用來代表集合,就如一副牌。

    List 是一個有序集合,可以包含重複元素。你可以通過它的索引來訪問任何元素。List 更像長度動態變換的陣列。

    Map 是一個將 key 對映到 value 的物件。一個 Map 不能包含重複的 key,每個 key 最多隻能對映一個 value。

    一些其它的介面有 Queue、Dequeue、SortedSet、SortedMap 和 ListIterator。

  20. 為何 Collection 不從 Cloneable 和 Serializable 介面繼承?

    Collection 介面指定一組物件,物件即為它的元素。如何維護這些元素由 Collection 的具體實現決定。例如,一些如 List 的 Collection 實現允許重複的元素,而其它的如 Set 就不允許。很多 Collection 實現有一個公有的 clone 方法。然而,把它放到集合的所有實現中也是沒有意義的。這是因為 Collection 是一個抽象表現,重要的是實現。

    當與具體實現打交道的時候,克隆或序列化的語義和含義才發揮作用。所以,具體實現應該決定如何對它進行克隆或序列化,或它是否可以被克隆或序列化。在所有的實現中授權克隆和序列化,最終導致更少的靈活性和更多的限制,特定的實現應該決定它是否可以被克隆和序列化。

  21. 為何 Map 介面不繼承 Collection 介面?

    儘管 Map 介面和它的實現也是集合框架的一部分,但 Map 不是集合,集合也不是 Map。因此,Map 繼承 Collection 毫無意義,反之亦然。

    如果 Map 繼承 Collection 介面,那麼元素去哪兒?Map 包含key-value 對,它提供抽取 key 或 value 列表集合的方法,但是它不適合“一組物件”規範。

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

    Iterator 介面提供了很多對集合元素進行迭代的方法。每一個集合類都包含了可以返回迭代器例項的迭代方法。迭代器可以在迭代的過程中刪除底層集合的元素,但是不可以直接呼叫集合的 remove(Object Obj) 刪除,可以通過迭代器的 remove() 方法刪除。

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

    Iterator 可用來遍歷 Set 和 List 集合,但是 ListIterator 只能用來遍歷 List。

    Iterator 對集合只能是前向遍歷,ListIterator 既可以前向也可以後向。

    ListIterator 實現了 Iterator 介面,幷包含其他的功能。比如:增加元素,替換元素,獲取前一個和後一個元素的索引等等。

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

    我們知道在 Java 中最常用的兩種結構是陣列和模擬指標(引用),幾乎所有的資料結構都可以利用這兩種來組合實現,HashMap 也是如此。實際上 HashMap 是一個“連結串列雜湊”。

    HashMap 是基於 hashing 的原理,我們使用 put(key, value) 儲存物件到 HashMap 中,使用 get(key) 從 HashMap 中獲取物件。當我們給 put() 方法傳遞鍵和值時,我們先對鍵呼叫 hashCode() 方法,返回的 hashCode 用於找到 bucket 位置來儲存 Entry 物件。

  25. 當兩個物件的 hashcode 相同會發生什麼?

    因為 hashcode 相同,所以它們的 bucket 位置相同,“碰撞”會發生。因為 HashMap 使用連結串列儲存物件,這個 Entry(包含有鍵值對的 Map.Entry 物件)會儲存在連結串列中。

  26. 如果兩個鍵的 hashcode 相同,你如何獲取值物件?

    當我們呼叫 get() 方法,HashMap 會使用鍵物件的 hashcode 找到 bucket 位置,然後會呼叫 keys.equals() 方法去找到連結串列中正確的節點,最終找到要找的值物件。

  27. hashCode() 和 equals() 方法有何重要性?

    HashMap 使用 Key 物件的 hashCode() 和 equals() 方法去決定 key-value 對的索引。當我們試著從 HashMap 中獲取值的時候,這些方法也會被用到。

    如果這些方法沒有被正確地實現,在這種情況下,兩個不同 Key 也許會產生相同的 hashCode() 和 equals() 輸出,HashMap 將會認為它們是相同的,然後覆蓋它們,而非把它們儲存到不同的地方。

    同樣的,所有不允許儲存重複資料的集合類都使用 hashCode() 和 equals() 去查詢重複,所以正確實現它們非常重要。equals() 和 hashCode() 的實現應該遵循以下規則:

    如果 o1.equals(o2),那麼o1.hashCode() == o2.hashCode()總是為true的。 如果 o1.hashCode() == o2.hashCode(),並不意味著o1.equals(o2)會為true。

  28. HashMap 和 HashTable 有什麼區別?

    1. HashMap 是非執行緒安全的,HashTable 是執行緒安全的。
    2. HashMap 的鍵和值都允許有 null 值存在,而 HashTable 則不行。
    3. 因為執行緒安全的問題,HashMap 效率比 HashTable 的要高。
    4. HashTable 是同步的,而 HashMap 不是。因此,HashMap 更適合於單執行緒環境,而 HashTable 適合於多執行緒環境。

    一般現在不建議用 HashTable,一是 HashTable 是遺留類,內部實現很多沒優化和冗餘。二是即使在多執行緒環境下,現在也有同步的 ConcurrentHashMap 替代,沒有必要因為是多執行緒而用 HashTable。

  29. 如何決定選用 HashMap 還是 TreeMap?

    對於在 Map 中插入、刪除和定位元素這類操作,HashMap 是最好的選擇。然而,假如你需要對一個有序的 key 集合進行遍歷, TreeMap 是更好的選擇。基於你的 collection 的大小,也許向 HashMap 中新增元素會更快,將 map 換為 TreeMap 進行有序 key 的遍歷。

  30. ArrayList 和 Vector 有何異同點?

    ArrayList 和 Vector 在很多時候都很類似。
    (1)兩者都是基於索引的,內部由一個陣列支援。
    (2)兩者維護插入的順序,我們可以根據插入順序來獲取元素。
    (3)ArrayList 和 Vector 的迭代器實現都是 fail-fast 的。
    (4)ArrayList 和 Vector 兩者允許 null 值,也可以使用索引值對元素進行隨機訪問。
    以下是ArrayList和Vector的不同點。
    (1)Vector 是同步的,而 ArrayList 不是。然而,如果你尋求在迭代的時候對列表進行改變,你應該使用 CopyOnWriteArrayList。
    (2)ArrayList 比 Vector 快,它因為有同步,不會過載。
    (3)ArrayList 更加通用,因為我們可以使用 Collections 工具類輕易地獲取同步列表和只讀列表。

  31. Array 和 ArrayList 有何區別?什麼時候更適合用 Array?

    Array 可以容納基本型別和物件,而 ArrayList 只能容納物件。 Array 是指定大小的,而 ArrayList 大小是固定的。Array 沒有提供 ArrayList 那麼多功能,比如 addAll、removeAll 和 iterator 等。儘管 ArrayList 明顯是更好的選擇,但也有些時候 Array 比較好用,比如下面的三種情況。
    (1)如果列表的大小已經指定,大部分情況下是儲存和遍歷它們。
    (2)對於遍歷基本資料型別,儘管 Collections 使用自動裝箱來減輕編碼任務,在指定大小的基本型別的列表上工作也會變得很慢。
    (3)如果你要使用多維陣列,使用 [][] 比 List。

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

    快速失敗:當你在迭代一個集合的時候,如果有另一個執行緒正在修改你正在訪問的那個集合時,就會丟擲一個 ConcurrentModification 異常。 在 java.util 包下的都是快速失敗。

    安全失敗:你在迭代的時候會去底層集合做一個拷貝,所以你在修改上層集合的時候是不會受影響的,不會丟擲 ConcurrentModification 異常。在java.util.concurrent 包下的全是安全失敗的。

相關文章