Java 面試基礎知識解析

(一)Java 基礎知識點

1)面向對象的特性有哪些?

答:封裝、繼承和多態_(應要多算一個那就是抽象)_java

  • 封裝是指將對象的實現細節隱藏起來,而後經過公共的方法來向外暴露出該對象的功能。
    但封裝不只僅是 private + getter/setter ,使用封裝能夠對 setter 進行更深層次的定製,例如你能夠對執行方法的對象作規定,也能夠對數據作必定的要求,還能夠作類型轉換等等。使用封裝不只僅安全,更能夠簡化操做。(封裝擴展閱讀:oc面向對象三大特性之一 <封裝>
  • 繼承是面向對象實現軟件複用的重要手段,當子類繼承父類後,子類是一種特殊的父類,能直接或間接得到父類裏的成員。
    繼承的缺點:1)繼承是一種強耦合關係,父類變子類也必須變;2)繼承破壞了封裝,對於父類而言,它的實現細節對子類來講都是透明的。
  • 多態簡而言之就是同一個行爲具備多個不一樣表現形式或形態的能力。
    好比說,有一杯水,我不知道它是溫的、冰的仍是燙的,可是我一摸我就知道了,我摸水杯的這個動做,對於不一樣溫度的水,就會獲得不一樣的結果,這就是多態。
    多態的條件1)繼承;2)重寫;3)向上轉型。
    多態的好處:當把不一樣的子類對象都看成父類類型來看,能夠屏蔽不一樣子類對象之間的實現差別,從而寫出通用的代碼達到通用編程,以適應需求的不斷變化。(多態擴展閱讀:從新認識java(五) ---- 面向對象之多態(向上轉型與向下轉型)
  • 抽象是指從特定的角度出發,從已經存在的一些事物中抽取咱們所關注的特性、行爲,從而造成一個新的事物的思惟過程,是一種從複雜到簡潔的思惟方式。

2)面向對象和麪向過程的區別?

答:面向過程是一種站在過程的角度思考問題的思想,強調的是功能行爲,功能的執行過程,即先幹啥,後幹啥。程序員

面向過程的設計:最小的程序單元是函數,每一個函數負責完成某一個功能,用以接受輸入數據,函數對輸入數據進行處理,而後輸出結果數據。整個軟件系統由一個個的函數組成,其中做爲程序入口的函數稱之爲主函數,主函數依次調用其餘函數,普通函數之間能夠相互調用,從而實現整個系統功能。web

  • 面向過程的缺陷:
    向過程的設計,是採用置頂而下的設計方式,在設計階段就須要考慮每個模塊應該分解成哪些子模塊,每個子模塊有細分爲更小的子模塊,如此類推,直到將模塊細化爲一個個函數。
  • 問題:1)設計不夠直觀,與人類的習慣思惟不一致;2)系統軟件適應性差,可擴展性差,維護性低。

面向過程最大的問題在於隨着系統的膨脹,面向過程將沒法應付,最終致使系統的崩潰。爲了解決這一種軟件危機,咱們提出面向對象思想。面試

面向對象是一種基於面向過程的新的編程思想,是一種站在對象的角度思考問題的思想,咱們把多個功能合理的放到不一樣對象裏,強調的是具有某些功能的對象。算法

  • 面向對象更加符合咱們常規的思惟方式,穩定性好,可重用性強,易於開發大型軟件產品,有良好的可維護性。在軟件工程上,面向對象可使工程更加模塊化,實現更低的耦合和更高的內聚。
  • 注意: 不要粗淺的認爲面向對象必定就優於面向過程的設計
看到知乎上有一句有意思的話:

你的程序要完成一個任務,至關於講一個故事。sql

面向過程:編年體;
面向對象:紀傳體。編程

而對於複雜的程序/宏大的故事,事實都證實了,面向對象/紀傳是更合理的表述方法。數組

擴展閱讀:面向過程 VS 面向對象緩存

3)JDK 和 JRE 的區別是什麼?

解析:這是考察一些基本的概念安全

答:Java 運行時環境(JRE-Java Runtime Environment),它包括 Java 虛擬機、Java 核心類庫和支持文件,但並不包含開發工具(JDK-Java Development Kit)——編譯器、調試器和其餘工具。

Java 開發工具包(JDK)是完整的 Java 軟件開發包,包含了 JRE,編譯器和其餘的工具(好比 JavaDoc, Java 調試器),可讓開發者開發、編譯、執行 Java 應用程序。

  • 還有其餘的一些名詞也能夠再看一下:

4)Java 中覆蓋和重載是什麼意思?

解析:覆蓋和重載是比較重要的基礎知識點,而且容易混淆,因此面試中常見。

答:覆蓋(Override)是指子類對父類方法的一種重寫,只能比父類拋出更少的異常,訪問權限不能比父類的小,被覆蓋的方法不能是 private 的,不然只是在子類中從新定義了一個新方法。

重載(Overload)表示同一個類中能夠有多個名稱相同的方法,但這些方法的參數列表各不相同。

面試官: 那麼構成重載的條件有哪些?

答:參數類型不一樣、參數個數不一樣、參數順序不一樣。

面試官: 函數的返回值不一樣能夠構成重載嗎?爲何?

答:不能夠,由於 Java 中調用函數並不須要強制賦值。舉例以下:

以下兩個方法:

void f(){}
int f(){ return 1; }

只要編譯器能夠根據語境明確判斷出語義,好比在 int x = f(); 中,那麼的確能夠據此區分重載方法。不過, 有時你並不關心方法的返回值,你想要的是方法調用的其餘效果 (這常被稱爲 「爲了反作用而調用」 ),這時你可能會調用方法而忽略其返回值,因此若是像下面的調用:

f();

此時 Java 如何才能判斷調用的是哪個 f() 呢?別人如何理解這種代碼呢?因此,根據方法返回值來區分重載方法是行不通的。

5)抽象類和接口的區別有哪些?

答:

  1. 抽象類中能夠沒有抽象方法;接口中的方法必須是抽象方法;
  2. 抽象類中能夠有普通的成員變量;接口中的變量必須是 static final 類型的,必須被初始化,接口中只有常量,沒有變量。
  3. 抽象類只能單繼承,接口能夠繼承多個父接口;
  4. Java 8 中接口中會有 default 方法,即方法能夠被實現。

面試官:抽象類和接口如何選擇?

答:

  1. 若是要建立不帶任何方法定義和成員變量的基類,那麼就應該選擇接口而不是抽象類。
  2. 若是知道某個類應該是基類,那麼第一個選擇的應該是讓它成爲一個接口,只有在必需要有方法定義和成員變量的時候,才應該選擇抽象類。由於抽象類中容許存在一個或多個被具體實現的方法,只要方法沒有被所有實現該類就還是抽象類。

6)Java 和 C++ 的區別:

解析:雖然咱們不太懂C++,可是就是會這麼問,尤爲是三面(總監級別)面試中。

答:

  1. 都是面向對象的語言,都支持封裝、繼承和多態
  2. 指針:Java不提供指針來直接訪問內存,程序更加安全
  3. 繼承: Java的類是單繼承的,C++支持多重繼承;Java經過一個類實現多個接口來實現C++中的多重繼承; Java中類不能夠多繼承,可是!!!接口能夠多繼承
  4. 內存: Java有自動內存管理機制,不須要程序員手動釋放無用內存

7)「static」 關鍵字是什麼意思?

答:「static」 關鍵字代表一個成員變量或者是成員方法能夠在沒有所屬的類的實例變量的狀況下被訪問。

面試官:Java中是否能夠覆蓋(override)一個 private 或者是 static 的方法?

答:Java 中 static 方法不能被覆蓋,由於方法覆蓋是基於運行時動態綁定的,而 static 方法是編譯時靜態綁定的。static 方法跟類的任何實例都不相關,因此概念上不適用。

Java 中也不能夠覆蓋 private 的方法,由於 private 修飾的變量和方法只能在當前類中使用,若是是其餘的類繼承當前類是不能訪問到 private 變量或方法的,固然也不能覆蓋。

擴展閱讀:從新認識java(六) ---- java中的另類:static關鍵字(附代碼塊知識)

8)Java 是值傳遞仍是引用傳遞?

解析:這類題目,面試官會手寫一個例子,讓你說出函數執行結果。

答:值傳遞是對基本型變量而言的,傳遞的是該變量的一個副本,改變副本不影響原變量。引用傳遞通常是對於對象型變量而言的,傳遞的是該對象地址的一個副本, 並非原對象自己 。

通常認爲,Java 內的傳遞都是值傳遞.,Java 中實例對象的傳遞是引用傳遞,Java 是值傳遞的!

  • 咱們先來看一個例子:

這是一個很經典的例子,咱們但願在調用了 swap() 方法以後交換 arg1 和 arg2 的值,但事實上並無,爲何會這樣?

這就是由於 Java 是值傳遞的,也就是說,咱們在調用一個須要傳遞參數的函數時,傳遞給函數的參數並非咱們傳遞進去的參數自己,而是它的一個副本,咱們改變了數據其實只是改變了副本的數據而已,並不會對原來的參數有任何的改變。

  • 再來看一個例子:

咱們本身定義了一個內部類 Person ,該類只有一個 int 類型的 age 屬性,而後有 getter/setter ,咱們但願經過 changeAge() 函數來改變 Person 對象的 age 屬性,爲何此次成功了呢?

你依然能夠理解爲,主函數將 person 複製了一份到 changeAge 函數中去,最終仍是隻改變了 changeAge 中複製的那一份參數的值,而本來的參數並無改變,但 changeAge 中的那一份和本來的參數指向了同一個內存區域!

9)JDK 中經常使用的包有哪些?

答:java.lang、java.util、java.io、java.net、java.sql。

10)JDK,JRE 和 JVM 的聯繫和區別?

答:JDK 是 Java 開發工具包,是 Java 開發環境的核心組件,並提供編譯、調試和運行一個 Java 程序所須要的全部工具,可執行文件和二進制文件,是一個平臺特定的軟件。

JRE 是 Java 運行時環境,是 JVM 的實施實現,提供了運行 Java 程序的平臺。JRE 包含了 JVM,可是不包含 Java 編譯器 / 調試器之類的開發工具。

JVM 是 Java 虛擬機,當咱們運行一個程序時,JVM 負責將字節碼轉換爲特定機器代碼,JVM 提供了內存管理 / 垃圾回收和安全機制等。

這種獨立於硬件和操做系統,正是 Java 程序能夠一次編寫多處執行的緣由。

區別:

  1. JDK 用於開發,JRE 用於運行 Java 程序;
  2. JDK 和 JRE 中都包含 JVM;
  3. JVM 是 Java 編程語言的核心而且具備平臺獨立性。

11)Integer 的緩存機制

解析:考察的是對源碼的熟悉程度

  • 看一個例子:

第一個返回true很好理解,就像上面講的,a和b指向相同的地址。

第二個返回false是爲何呢?這是由於 Integer 有緩存機制,在 JVM 啓動初期就緩存了 -128 到 127 這個區間內的全部數字。

第三個返回false是由於用了new關鍵字來開闢了新的空間,i和j兩個對象分別指向堆區中的兩塊內存空間。

咱們能夠跟蹤一下Integer的源碼,看看到底怎麼回事。在IDEA中,你只須要按住Ctrl而後點擊Integer,就會自動進入jar包中對應的類文件。

跟蹤到文件的700多行,你會看到這麼一段,感興趣能夠仔細讀一下,不用去讀也沒有關係,由於你只須要知道這是 Java 的一個緩存機制。Integer 類的內部類緩存了 -128 到 127 的全部數字。(事實上,Integer類的緩存上限是能夠經過修改系統來更改的,瞭解就好了,沒必要去深究。)

12)下述兩種方法分別建立了幾個 Sring 對象?

// 第一種:直接賦一個字面量
String str1 = "ABCD";
// 第二種:經過構造器建立
String str2 = new String("ABCD");

解析:考察的是對 String 對象和 JVM 內存劃分的知識。

答:String str1 = "ABCD";最多建立一個String對象,最少不建立String對象.若是常量池中,存在」ABCD」,那麼str1直接引用,此時不建立String對象.不然,先在常量池先建立」ABCD」內存空間,再引用.

String str2 = new String("ABCD");最多建立兩個String對象,至少建立一個String對象。new關鍵字絕對會在堆空間建立一塊新的內存區域,因此至少建立一個String對象。

咱們來看圖理解一下:

  • 當執行第一句話的時候,會在常量池中添加一個新的ABCD字符,str1指向常量池的ABCD
  • 當執行第二句話的時候,由於有new操做符,因此會在堆空間新開闢一塊空間用來存儲新的String對象,由於此時常量池中已經有了ABCD字符,因此堆中的String對象指向常量池中的ABCD,而str2則指向堆空間中的String對象。

String 對象是一個特殊的存在,須要注意的知識點也比較多,這裏給一個以前寫的 String 詳解的文章連接:傳送門 其中包含的問題大概有:1)「+」 怎麼鏈接字符串;2)字符串的比較;3)StringBuilder/StringBuffer/String 的區別;

13)i++ 與 ++i 到底有什麼不一樣?

解析:對於這兩個的區別,熟悉的表述是:前置++是先將變量的值加 1,而後使用加 1 後的值參與運算,然後置++則是先使用該值參與運算,而後再將該值加 1 .但事實上,前置++和後置++同樣,在參與運算以前都會將變量的值加 1

答:實際上,無論是前置 ++,仍是後置 ++,都是先將變量的值加 1,而後才繼續計算的。兩者之間真正的區別是:前置 ++ 是將變量的值加 1 後,使用增值後的變量進行運算的,然後置 ++ 是首先將變量賦值給一個臨時變量,接下來對變量的值加 1,而後使用那個臨時變量進行運算。

14)交換變量的三種方式

答:

  • 第一種:經過第三個變量
public class Test{
    public static void main(String[] args) {
        int x = 5;
        int y = 10;
        swap(x,y);
        System.out.println(x);
        System.out.println(y);

        Value v = new Value(5,10);
        swap(v);
        System.out.println(v.x);
        System.out.println(v.y);
    }

    // 無效的交換:形參的改變沒法副作用於實參
    public static void swap(int x,int y) {
        int temp = x;
        x = y;
        y = temp;
    }

    // 有效的交換:經過引用(變量指向一個對象)來修改爲員變量
    public static void swap(Value value) {
        int temp = value.x;
        value.x = value.y;
        value.y = temp;
    }
}

class Value{
    int x;
    int y;

    public Value(int x,int y) {
        this.x = x;
        this.y = y;
    }
}
輸出的結果:
5
10
10
5

這有點相似於C/C++語言中的指針,不過相對來講更加安全。

事實上,其實若是把基礎類型int改爲對應的包裝類的話其實能夠更加簡單的完成這個操做,不過須要付出更多的內存代價。

第二種:經過經過相加的方式(相同的 Value 類再也不重複展現)
public class Test{
    public static void main(String[] args) {
        Value v1 = new Value(5,10);
        swap(v1);
        System.out.println("v1交換以後的結果爲:");
        System.out.println(v1.x);
        System.out.println(v1.y);
    }

    public static void swap(Value v) {
        v.x = v.x + v.y;
        v.y = v.x - v.y;
        v.x = v.x - v.y;
    }
}
輸出的結果:
v1的交換結果:
10
5

核心的算法就是swap方法:

v.x = v.x + v.y;    // 把v.x與v.y的和存儲在v.x中
v.y = v.x - v.y;    // v.x減掉v.y原本的值即爲v.x
v.x = v.x - v.y;    // v.x減掉v.y的值也就是之前x.y的值

這樣就能夠不經過臨時變量,來達到交換兩個變量的目的,若是以爲上面的方法不太容易理解,咱們也能夠用另外一個參數z來表示上述過程:

int z = v.x + v.y;    // 把v.x與v.y的和存儲在z中
v.y = z - v.y;        // z減掉之前的v.y就等於v.x
v.x = z - v.y;        // z減掉如今的v.y即之前的v.x,即爲v.y

但並不推薦這種作法,緣由在於當數值很大的時候,16進制的求和運算可能形成數據的溢出,雖然最後的結果依然會是咱們所指望的那樣,但仍然不是十分可取。

  • 第三種:經過異或的方式:

位異或運算符(^)有這樣的一個性質,就是兩個整型的數據x與y,有:
(x ^ y ^ y) == x這說明,若是一個變量x異或另一個變量y兩次,結果爲x。經過這一點,能夠實現交換兩個變量的值:

public class Test{
    public static void main(String[] args) {
        Value v1 = new Value(5,10);
        swap(v1);
        System.out.println("v1交換以後的結果爲:");
        System.out.println(v1.x);
        System.out.println(v1.y);
    }

    public static void swap(Value v) {
        v.x = v.x ^ v.y;
        v.y = v.x ^ v.y;
        v.x = v.x ^ v.y;
    }
}
輸出的結果:
v1交換以後的結果爲:
10
5

跟上面相加的方式過程幾乎相似,只不過運算的方式不一樣而已。異或的方法比相加更加可取的地方在於,異或不存在數據溢出。

15)Java 對象初始化順序?

答:不考慮靜態成員的初始化,調用一個對象的構造函數時,程序先調用父類的構造函數(能夠經過super關鍵字指定父類的構造函數,不然默認調用無參的構造函數,而且須要在子類的構造函數的第一行調用),以後靜態成員變量的初始化函數和靜態初始化塊則按照在代碼當中的順序執行,成員變量若是沒有指定值的話則賦予默認值,即基本數據類型爲0或false等,對象則爲null;最後調用自身構造函數。

  • 咱們能夠寫一段程序來對初始化順序進行一個簡單的驗證:
public class Derive extends Base
{
    private Member m1 = new Member("Member 1");
    {
        System.out.println("Initial Block()");
    }

    public Derive() {
        System.out.println("Derive()");
    }

    private Member m2 = new Member("Member 2");
    private int i = getInt();

    private int getInt()
    {
        System.out.println("getInt()");
        return 2;
    }

    public static void main(String[] args)
    {
        new Derive();
    }
}

class Base
{
    public Base()
    {
        System.out.println("Base()");
    }
}

class Member
{
    public Member(String m)
    {
        System.out.println("Member() "+m);
    }
}
程序的輸出結果是:
Base()
Member() Member 1
Initial Block()
Member() Member 2
getInt()
Derive()

16)true、false 與 null 是關鍵字嗎?

答:不是。true、false 是布爾類型的字面常量,null 是引用類型的字面常量。

面試官:那 goto 與 const 呢?

答:是。goto 與 const 均是 Java 語言保留的關鍵字,即沒有任何語法應用。

17)exception 和 error 有什麼區別?

答:exception 和 error都是 Throwable 的子類。exception 用於用戶程序能夠捕獲的異常狀況;error 定義了不指望被用戶程序捕獲的異常。

exception 表示一種設計或設計的問題,也就是說只要程序正常運行,從不會發生的狀況;而 error 表示回覆不是不可能可是很困難的狀況下的一種嚴重問題,好比內存溢出,不可能期望程序處理這樣的狀況。

18)throw 和 throws 有什麼區別?

答:throw 關鍵字用來在程序中明確的拋出異常,相反,throws 語句用來代表方法不能處理的異常。每個方法都必需要指定哪些異常不能處理,因此方法的調用者纔可以確保處理可能發生的異常,多個異常是用逗號分隔的。

小結:本節主要闡述了 Java 基礎知識,並無涉及到一些高級的特性,這些問題通常難度不大,適當複習下,應該沒問題。

(二)Java 中常見集合

集合這方面的考察至關多,這部分是面試中必考的知識點。

1)說說常見的集合有哪些吧?

答:Map接口和Collection接口是全部集合框架的父接口:

  1. Collection接口的子接口包括:Set接口和List接口
  2. Map接口的實現類主要有:HashMap、TreeMap、Hashtable、ConcurrentHashMap以及Properties等
  3. Set接口的實現類主要有:HashSet、TreeSet、LinkedHashSet等
  4. List接口的實現類主要有:ArrayList、LinkedList、Stack以及Vector等

2)HashMap和Hashtable的區別有哪些?(必問)

答:

  1. HashMap沒有考慮同步,是線程不安全的;Hashtable使用了synchronized關鍵字,是線程安全的;
  2. 前者容許null做爲Key;後者不容許null做爲Key

3)HashMap的底層實現你知道嗎?

答:在Java8以前,其底層實現是數組+鏈表實現,Java8使用了數組+鏈表+紅黑樹實現。此時你能夠簡單的在紙上畫圖分析:

4)ConcurrentHashMap 和 Hashtable 的區別?(必問)

答:ConcurrentHashMap 結合了 HashMap 和 HashTable 兩者的優點。HashMap 沒有考慮同步,HashTable 考慮了同步的問題。可是 HashTable 在每次同步執行時都要鎖住整個結構。 ConcurrentHashMap 鎖的方式是稍微細粒度的。 ConcurrentHashMap 將 hash 表分爲 16 個桶(默認值),諸如get,put,remove 等經常使用操做只鎖當前須要用到的桶。

面試官:ConcurrentHashMap的具體實現知道嗎?

答:

1. 該類包含兩個靜態內部類 HashEntry 和 Segment ;前者用來封裝映射表的鍵值對,後者用來充當鎖的角色;

2. Segment 是一種可重入的鎖 ReentrantLock,每一個 Segment 守護一個HashEntry 數組裏得元素,當對 HashEntry 數組的數據進行修改時,必須首先得到對應的 Segment 鎖。

5)HashMap 的長度爲何是2的冪次方?

答:

1. 經過將 Key 的 hash 值與 length - 1 進行 & 運算,實現了當前 Key 的定位,2 的冪次方能夠減小衝突(碰撞)的次數,提升 HashMap 查詢效率

2. 若是 length 爲 2 的次冪 則 length-1 轉化爲二進制一定是 11111……的形式,在於 h 的二進制與操做效率會很是的快,並且空間不浪費;若是 length 不是 2 的次冪,好比 length 爲 15,則 length - 1 爲 14,對應的二進制爲 1110,在於 h 與操做,最後一位都爲 0 ,而 0001,0011,0101,1001,1011,0111,1101 這幾個位置永遠都不能存放元素了,空間浪費至關大,更糟的是這種狀況中,數組可使用的位置比數組長度小了不少,這意味着進一步增長了碰撞的概率,減慢了查詢的效率!這樣就會形成空間的浪費。

6)List和Set的區別是啥?

答:List元素是有序的,能夠重複;Set元素是無序的,不能夠重複。

7)List、Set和Map的初始容量和加載因子:

答:

1. List

  • ArrayList的初始容量是10;加載因子爲0.5; 擴容增量:原容量的 0.5倍+1;一次擴容後長度爲15。
  • Vector初始容量爲10,加載因子是1。擴容增量:原容量的 1倍,如 Vector的容量爲10,一次擴容後是容量爲20。

2. Set

HashSet,初始容量爲16,加載因子爲0.75; 擴容增量:原容量的 1 倍; 如 HashSet的容量爲16,一次擴容後容量爲32

3. Map

HashMap,初始容量16,加載因子爲0.75; 擴容增量:原容量的 1 倍; 如 HashMap的容量爲16,一次擴容後容量爲32

8)Comparable接口和Comparator接口有什麼區別?

答:

1. 前者簡單,可是若是須要從新定義比較類型時,須要修改源代碼。

2. 後者不須要修改源代碼,自定義一個比較器,實現自定義的比較方法。 具體解析參考博客:Java集合框架—Set

9)Java集合的快速失敗機制 「fail-fast」

答:

是java集合的一種錯誤檢測機制,當多個線程對集合進行結構上的改變的操做時,有可能會產生 fail-fast 機制。

例如:假設存在兩個線程(線程一、線程2),線程1經過Iterator在遍歷集合A中的元素,在某個時候線程2修改了集合A的結構(是結構上面的修改,而不是簡單的修改集合元素的內容),那麼這個時候程序就會拋出 ConcurrentModificationException 異常,從而產生fail-fast機制。

緣由:迭代器在遍歷時直接訪問集合中的內容,而且在遍歷過程當中使用一個 modCount 變量。集合在被遍歷期間若是內容發生變化,就會改變modCount的值。每當迭代器使用hashNext()/next()遍歷下一個元素以前,都會檢測modCount變量是否爲expectedmodCount值,是的話就返回遍歷;不然拋出異常,終止遍歷。

解決辦法:

1. 在遍歷過程當中,全部涉及到改變modCount值得地方所有加上synchronized。

2. 使用CopyOnWriteArrayList來替換ArrayList

10)ArrayList 和 Vector 的區別

答:

這兩個類都實現了 List 接口(List 接口繼承了 Collection 接口),他們都是有序集合,即存儲在這兩個集合中的元素位置都是有順序的,至關於一種動態的數組,咱們之後能夠按位置索引來取出某個元素,而且其中的數據是容許重複的,這是與 HashSet 之類的集合的最大不一樣處,HashSet 之類的集合不能夠按索引號去檢索其中的元素,也不容許有重複的元素。

ArrayList 與 Vector 的區別主要包括兩個方面:

  1. 同步性:
    Vector 是線程安全的,也就是說它的方法之間是線程同步(加了synchronized 關鍵字)的,而 ArrayList 是線程不安全的,它的方法之間是線程不一樣步的。若是隻有一個線程會訪問到集合,那最好是使用 ArrayList,由於它不考慮線程安全的問題,因此效率會高一些;若是有多個線程會訪問到集合,那最好是使用 Vector,由於不須要咱們本身再去考慮和編寫線程安全的代碼。
  2. 數據增加:
    ArrayList 與 Vector 都有一個初始的容量大小,當存儲進它們裏面的元素的我的超過了容量時,就須要增長 ArrayList 和 Vector 的存儲空間,每次要增長存儲空間時,不是隻增長一個存儲單元,而是增長多個存儲單元,每次增長的存儲單元的個數在內存空間利用與程序效率之間要去的必定的平衡。Vector 在數據滿時(加載因子1)增加爲原來的兩倍(擴容增量:原容量的 1 倍),而 ArrayList 在數據量達到容量的一半時(加載因子 0.5)增加爲原容量的 0.5 倍 + 1 個空間。

面試官:那 ArrayList 和 LinkedList 的區別呢?

答:

  1. LinkedList 實現了 List 和 Deque 接口,通常稱爲雙向鏈表;
  2. LinkedList 在插入和刪除數據時效率更高,ArrayList 在查找某個 index 的數據時效率更高;
  3. LinkedList 比 ArrayList 須要更多的內存;

面試官:Array 和 ArrayList 有什麼區別?何時該應 Array 而不是 ArrayList 呢?

答:它們的區別是:

  1. Array 能夠包含基本類型和對象類型,ArrayList 只能包含對象類型。
  2. Array 大小是固定的,ArrayList 的大小是動態變化的。
  3. ArrayList 提供了更多的方法和特性,好比:addAll(),removeAll(),iterator() 等等。

對於基本類型數據,集合使用自動裝箱來減小編碼工做量。可是,當處理固定大小的基本數據類型的時候,這種方式相對比較慢。

11)如何去掉一個 Vector 集合中重複的元素?

答:

Vector newVector = new Vector();
for (int i = 0; i < vector.size(); i++) {
    Object obj = vector.get(i);
    if (!newVector.contains(obj)) {
        newVector.add(obj);
    }
}

還有一種簡單的方式,利用了 Set 不容許重複元素的特性:

HashSet set = new HashSet(vector);
小結:本小節是 Java 中關於集合的考察,是 Java 崗位面試中必考的知識點,除了應該掌握以上的問題,包括各個集合的底層實現也建議各位同窗閱讀,加深理解。

12)如何權衡是使用無序的數組仍是有序的數組?

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

相關文章
相關標籤/搜索