Java經典面試題詳解,突圍金九銀十面試季(附詳細答案)

2020-09-22 12:00:51

前言:

想在面試、工作中脫穎而出?想在最短的時間內快速掌握 Java 的核心基礎知識點?想要成為一位優秀的 Java 工程師?本篇文章能助你一臂之力!

金九銀十,目前正值招聘求職旺季,很多同學對一些新技術名詞都能侃侃而談,但對一些核心原理理解的不夠透徹,特別是對 Java的一些核心基礎知識點掌握的不夠,例如JVM、常用的演演算法和資料結構等。正所謂萬丈高樓平地起,只有把基礎掌握的牢固,才能走的更遠,面對不斷更新的技術才能快速掌握,同時在面試、工作中也更能脫穎而出!

在這裡插入圖片描述

1.Java 自動裝箱與拆箱

裝箱就是自動將基本資料型別轉換為包裝器型別(int–>Integer);呼叫方法:Integer 的 valueOf(int) 方法

拆箱就是自動將包裝器型別轉換為基本資料型別(Integer–>int)。呼叫方法:Integer 的 intValue 方法

在 Java SE5 之前,如果要生成一個數值為 10 的 Integer 物件,必須這樣進行:

Integer i = new Integer(10);

而在從 Java SE5 開始就提供了自動裝箱的特性,如果要生成一個數值為 10 的 Integer 物件,只需要這

樣就可以了:

Integer i = 10;

2.過載和重寫的區別

重寫(Override)

從字面上看,重寫就是 重新寫一遍的意思。其實就是在子類中把父類別本身有的方法重新寫一遍。子類繼承了父類別原有的方法,但有時子類並不想原封不動的繼承父類別中的某個方法,所以在方法名,參數列,返回型別(除過子類中方法的返回值是父類別中方法返回值的子類時)都相同的情況下, 對方法體進行修改或重寫,這就是重寫。但要注意子類函數的存取修飾許可權不能少於父類別的。

public class Father {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Son s = new Son();
        s.sayHello();
    }
    public void sayHello() {
        System.out.println("Hello");
    }
}
class Son extends Father{
    @Override
    public void sayHello() {
        // TODO Auto-generated method stub
        System.out.println("hello by ");
    }
}

重寫 總結:

(1)發生在父類別與子類之間

(2)方法名,參數列,返回型別(除過子類中方法的返回型別是父類別中返回型別的子類)必須相同

(3)存取修飾符的限制一定要大於被重寫方法的存取修飾符(public>protected>default>private)

(4)重寫方法一定不能丟擲新的檢查異常或者比被重寫方法申明更加寬泛的檢查型異常

過載(Overload)

在一個類中,同名的方法如果有不同的參數列(引數型別不同、引數個數不同甚至是引數順序不同)

則視為過載。同時,過載對返回型別沒有要求,可以相同也可以不同,但不能通過返回型別是否相同來

判斷過載。

public static void main(String[] args) {
    // TODO Auto-generated method stub
    Father s = new Father();
    s.sayHello();
    s.sayHello("wintershii");
}
public void sayHello() {
    System.out.println("Hello");
}
public void sayHello(String name) {
    System.out.println("Hello" + " " + name);
}
}

過載 總結:

(1)過載 Overload 是一個類中多型性的一種表現

(2)過載要求同名方法的參數列不同(引數型別,引數個數甚至是引數順序)

(3)過載的時候,返回值型別可以相同也可以不相同。無法以返回型別作為過載函數的區分標準

下方面試題我只挑選了幾個比較好的,剩餘的實在是太多了寫不完,都整理成筆記了,想要筆記的可以點選下方群號領取。另外本人整理收藏了20年多家公司面試知識點整理 ,以及各種Java核心知識點免費分享給大家,下方只是部分截圖 想要資料的話也可以點選795983544領取 暗號CSDN。

在這裡插入圖片描述

3.equals 與==的區別

== :

== 比較的是變數(棧)記憶體中存放的物件的(堆)記憶體地址,用來判斷兩個物件的地址是否相同,即是否是指相同一個物件。比較的是真正意義上的指標操作。

(1)比較的是操作符兩端的運算元是否是同一個物件。

(2)兩邊的運算元必須是同一型別的(可以是父子類之間)才能編譯通過。

(3)比較的是地址,如果是具體的阿拉伯數位的比較,值相等則為 true,如:

int a=10 與 long b=10L 與 double c=10.0 都是相同的(為 true),因為他們都指向地址為 10 的堆。

equals:

equals 用來比較的是兩個物件的內容是否相等,由於所有的類都是繼承自 java.lang.Object 類的,所以適用於所有物件,如果沒有對該方法進行覆蓋的話,呼叫的仍然是 Object 類中的方法,而 Object 中的 equals 方法返回的卻是==的判斷。

總結:

所有比較是否相等時,都是用 equals 並且在對常數相比較時,把常數寫在前面,因為使用 object 的 equals object 可能為 null 則空指標在阿里的程式碼規範中只使用 equals ,阿里外掛預設會識別,並可以快速修改,推薦安裝阿里外掛來排查老程式碼使用「==」,替換成 equals

4. Hashcode 的作用

java 的集合有兩類,一類是 List,還有一類是 Set。前者有序可重複,後者無序不重複。當我們在 set 中插入的時候怎麼判斷是否已經存在該元素呢,可以通過 equals 方法。但是如果元素太多,用這樣的方法就會比較滿。

於是有人發明了雜湊演演算法來提高集合中查詢元素的效率。 這種方式將集合分成若干個儲存區域,每個物件可以計算出一個雜湊碼,可以將雜湊碼分組,每組分別對應某個儲存區域,根據一個物件的雜湊碼就可以確定該物件應該儲存的那個區域。

hashCode 方法可以這樣理解:它返回的就是根據物件的記憶體地址換算出的一個值。這樣一來,當集合要新增新的元素時,先呼叫這個元素的 hashCode 方法,就一下子能定位到它應該放置的物理位置上。如果這個位置上沒有元素,它就可以直接儲存在這個位置上,不用再進行任何比較了;如果這個位置上已經有元素了,就呼叫它的 equals 方法與新元素進行比較,相同的話就不存了,不相同就雜湊其它的地址。這樣一來實際呼叫 equals 方法的次數就大大降低了,幾乎只需要一兩次。

5.String、String StringBuffer 和 StringBuilder 的區別是什麼?

String 是唯讀字串,它並不是基本資料型別,而是一個物件。從底層原始碼來看是一個final 型別的字元陣列,所參照的字串不能被改變,一經定義,無法再增刪改。每次對 String 的操作都會生成新的 String 物件。

private final char value[];

每次+操作 : 隱式在堆上 new 了一個跟原字串相同的 StringBuilder 物件,再呼叫 append 方法 拼接+後面的字元。

StringBuffer 和 StringBuilder 他們兩都繼承了 AbstractStringBuilder 抽象類,從 AbstractStringBuilder 抽象類中我們可以看到。

/**
* The value is used for character storage.
*/
char[] value;

他們的底層都是可變的字元陣列,所以在進行頻繁的字串操作時,建議使用 StringBuffer 和 StringBuilder 來進行操作。 另外 StringBuffer 對方法加了同步鎖或者對呼叫的方法加了同步鎖,所以是執行緒安全的。StringBuilder 並沒有對方法進行加同步鎖,所以是非執行緒安全的。

6.ArrayList 和 linkedList 的區別

Array(陣列)是基於索引(index)的資料結構,它使用索引在陣列中搜尋和讀取資料是很快的。

Array 獲取資料的時間複雜度是 O(1),但是要刪除資料卻是開銷很大,因為這需要重排陣列中的所有資料,(因為刪除資料以後, 需要把後面所有的資料前移)

缺點: 陣列初始化必須指定初始化的長度, 否則報錯

例如:

int[] a = new int[4];
//推介使用 int[] 這種方式初始化
int c[] = {23,43,56,78};
//長度:4,索引範圍:[0,3]

List—是一個有序的集合,可以包含重複的元素,提供了按索引存取的方式,它繼承 Collection。

List 有兩個重要的實現類:ArrayList 和 LinkedList

ArrayList: 可以看作是能夠自動增長容量的陣列

ArrayList 的 toArray 方法返回一個陣列

ArrayList 的 asList 方法返回一個列表

ArrayList 底層的實現是 Array, 陣列擴容實現

LinkList 是一個雙連結串列,在新增和刪除元素時具有比 ArrayList 更好的效能.但在 get 與 set 方面弱於

ArrayList.當然,這些對比都是指資料量很大或者操作很頻繁。

7.HashMap 和 HashTable 的區別

(1)兩者父類別不同

HashMap 是繼承自 AbstractMap 類,而 Hashtable 是繼承自 Dictionary 類。不過它們都實現了同時實現了 map、Cloneable(可複製)、Serializable(可序列化)這三個介面。

(2)對外提供的介面不同

Hashtable 比 HashMap 多提供了 elments() 和 contains() 兩個方法。

elments() 方法繼承自 Hashtable 的父類別 Dictionnary。elements() 方法用於返回此 Hashtable 中的 value 的列舉。

contains()方法判斷該 Hashtable 是否包含傳入的 value。它的作用與 containsValue()一致。事實上,contansValue() 就只是呼叫了一下 contains() 方法。

(3)對 null 的支援不同

Hashtable:key 和 value 都不能為 null。

HashMap:key 可以為 null,但是這樣的 key 只能有一個,因為必須保證 key 的唯一性;可以有多個 key 值對應的 value 為 null。

(4)安全性不同

HashMap 是執行緒不安全的,在多執行緒並行的環境下,可能會產生死鎖等問題,因此需要開發人員自己處理多執行緒的安全問題。

Hashtable 是執行緒安全的,它的每個方法上都有 synchronized 關鍵字,因此可直接用於多執行緒中。雖然 HashMap 是執行緒不安全的,但是它的效率遠遠高於 Hashtable,這樣設計是合理的,因為大部分的使用場景都是單執行緒。當需要多執行緒操作的時候可以使用執行緒安全的 ConcurrentHashMap。

ConcurrentHashMap 雖然也是執行緒安全的,但是它的效率比 Hashtable 要高好多倍。因為

ConcurrentHashMap 使用了分段鎖,並不對整個資料進行鎖定。

(5)計算 hash 值的方法不同

8.Collection 包結構,與 Collections 的區別

Collection 是集合類的上級介面,子介面有 Set、List、LinkedList、ArrayList、Vector、Stack、Set;Collections 是集合類的一個幫助類, 它包含有各種有關集合操作的靜態多型方法,用於實現對各種集合的搜尋、排序、執行緒安全化等操作。此類不能範例化,就像一個工具類,服務於 Java 的 Collection 框架。

9.Java 的四種參照,強弱軟虛

強參照

強參照是平常中使用最多的參照,強參照在程式記憶體不足(OOM)的時候也不會被回收,使用方式:

String str = new String("str");

軟參照

軟參照在程式記憶體不足時,會被回收,使用方式:

// 注意:wrf 這個參照也是強參照,它是指向 SoftReference 這個物件的,
// 這裡的軟參照指的是指向 new String("str")的參照,也就是 SoftReference 類中 T
SoftReference<String> wrf = new SoftReference<String>(new String("str"));

可用場景: 建立快取的時候,建立的物件放進快取中,當記憶體不足時,JVM 就會回收早先建立的物件。

弱參照

弱參照就是隻要 JVM 垃圾回收器發現了它,就會將之回收,使用方式:

WeakReference<String> wrf = new WeakReference<String>(str);

可用場景: Java 原始碼中的 java.util.WeakHashMap 中的 key 就是使用弱參照,我的理解就是,一旦我不需要某個參照,JVM 會自動幫我處理它,這樣我就不需要做其它操作。

虛參照

虛參照的回收機制跟弱參照差不多,但是它被回收之前,會被放入 ReferenceQueue 中。注意哦,其它參照是被 JVM 回收後才被傳入 ReferenceQueue 中的。由於這個機制,所以虛參照大多被用於參照銷燬前的處理工作。還有就是,虛參照建立的時候,必須帶有 ReferenceQueue,使用

例子:

PhantomReference<String> prf = new PhantomReference<String>(new
String("str"), new ReferenceQueue<>());

可用場景: 物件銷燬前的一些操作,比如說資源釋放等。** Object.finalize()雖然也可以做這類動作,但是這個方式即不安全又低效。

10.a=a+b 與 a+=b 有什麼區別嗎?

操作符會進行隱式自動型別轉換,此處 a+=b 隱式的將加操作的結果型別強制轉換為持有結果的型別,

byte a = 127;
byte b = 127;
b = a + b;
// 報編譯錯誤:cannot convert from int to byte
b += a;

以下程式碼是否有錯,有的話怎麼改?

short s1= 1;
s1 = s1 + 1;

有錯誤.short 型別在進行運算時會自動提升為 int 型別,也就是說 s1+1 的運算結果是 int 型別,而 s1 是 short 型別,此時編譯器會報錯.

正確寫法:

short s1= 1;
s1 += 1;

+=操作符會對右邊的表示式結果強轉匹配左邊的資料型別,所以沒錯.

11.try catch finally,try 裡有 return,finally 還執行麼?

執行,並且finally 的執行早於 try 裡面的 return

結論:

(1)不管有木有出現異常,finally 塊中程式碼都會執行;

(2)當 try 和 catch 中有 return 時,finally 仍然會執行;

(3)finally 是在 return 後面的表示式運算後執行的(此時並沒有返回運算後的值,而是先把要返回的值儲存起來,管finally 中的程式碼怎麼樣,返回的值都不會改變,任然是之前儲存的值),所以函數返回值是在finally 執行前確定的;

(4)finally 中最好不要包含 return,否則程式會提前退出,返回值不是 try 或 catch 中儲存的返回值。、

12.Java 執行緒實現/建立方式

繼承 Thread 類

Thread 類本質上是實現了 Runnable 介面的一個範例,代表一個執行緒的範例。啟動執行緒的唯一方法就是通過 Thread 類的 start()實體方法。start()方法是一個 native 方法,它將啟動一個新執行緒,並執行 run()方法。

 public class MyThread extends Thread { 
         public void run() { 
             System.out.println("MyThread.run()"); 
         } 
    } 
        MyThread myThread1 = new MyThread(); 
        myThread1.start();

實現 Runnable 介面

如果自己的類已經 extends 另一個類,就無法直接 extends Thread,此時,可以實現一個Runnable 介面。

public class MyThread extends OtherClass implements Runnable { 
        public void run() { 
             System.out.println("MyThread.run()"); 
         } 
    } 
    //啟動 MyThread
    MyThread myThread = new MyThread(); 
    Thread thread = new Thread(myThread); 
    thread.start(); 
    target.run()
    public void run() { 
     if (target != null) { 
     target.run(); 
     } 
    }

13.執行緒池原理

執行緒池做的工作主要是控制執行的執行緒的數量,處理過程中將任務放入佇列,然後線上程建立後啟動這些任務,如果執行緒數量超過了最大數量超出數量的執行緒排隊等候,等其它執行緒執行完畢,再從佇列中取出任務來執行。主要特點為:執行緒複用;控制最大並行數;管理執行緒。

執行緒複用 一個 Thread 的類都有一個 start 方法。 當呼叫 start 啟動執行緒時 Java 虛擬機器器會呼叫該類的 run 方法。 那麼該類的 run() 方法中就是呼叫了 Runnable 物件的 run() 方法。 我們可以繼承重寫 Thread 類,在其 start 方法中新增不斷迴圈呼叫傳遞過來的 Runnable 物件。 這就是執行緒池的實現原理。迴圈方法中不斷獲取 Runnable 是用 Queue 實現的,在獲取下一個 Runnable 之前可以是阻塞的。

執行緒池的組成 一般的執行緒池主要分為以下 4 個組成部分:

(1)執行緒池管理器:用於建立並管理執行緒池。 (2)工作執行緒:執行緒池中的執行緒。 (3)任務介面:每個任務必須實現的介面,用於工作執行緒排程其執行。 (4)任務佇列:用於存放待處理的任務,提供一種緩衝機制。

Java 中的執行緒池是通過 Executor 框架實現的,該框架中用到了 Executor,Executors,ExecutorService,ThreadPoolExecutor ,Callable 和 Future、FutureTask 這幾個類。

ThreadPoolExecutor 的構造方法如下:

public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize, long keepAliveTime,
            TimeUnit unit, BlockingQueue<Runnable> workQueue) {
            this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,Executors.defaultThreadFactory(), defaultHandler);

corePoolSize:指定了執行緒池中的執行緒數量。

maximumPoolSize:指定了執行緒池中的最大執行緒數量。

keepAliveTime:當前執行緒池數量超過 corePoolSize 時,多餘的空閒執行緒的存活時間,即多次時間內會被銷燬。

unit:keepAliveTime 的單位。

workQueue:任務佇列,被提交但尚未被執行的任務。

threadFactory:執行緒工廠,用於建立執行緒,一般用預設的即可。

handler:拒絕策略,當任務太多來不及處理,如何拒絕任務。

拒絕策略 執行緒池中的執行緒已經用完了,無法繼續為新任務服務,同時,等待佇列也已經排滿了,再也塞不下新任務了。這時候我們就需要拒絕策略機制合理的處理這個問題。

JDK 內建的拒絕策略如下:

AbortPolicy : 直接丟擲異常,阻止系統正常執行。

CallerRunsPolicy : 只要執行緒池未關閉,該策略直接在呼叫者執行緒中,執行當前被丟棄的任務。顯然這樣做不會真的丟棄任務,但是,任務提交執行緒的效能極有可能會急劇下降。

DiscardOldestPolicy : 丟棄最老的一個請求,也就是即將被執行的一個任務,並嘗試再次提交當前任務。

DiscardPolicy : 該策略默默地丟棄無法處理的任務,不予任何處理。如果允許任務丟失,這是最好的一種方案。

以上內建拒絕策略均實現了 RejectedExecutionHandler 介面,若以上策略仍無法滿足實際需要,完全可以自己擴充套件 RejectedExecutionHandler 介面。

Java 執行緒池工作過程 (1)執行緒池剛建立時,裡面沒有一個執行緒。任務佇列是作為引數傳進來的。不過,就算佇列裡面有任務,執行緒池也不會馬上執行它們。

(2)當呼叫 execute() 方法新增一個任務時,執行緒池會做如下判斷:

a) 如果正在執行的執行緒數量小於 corePoolSize,那麼馬上建立執行緒執行這個任務; b) 如果正在執行的執行緒數量大於或等於 corePoolSize,那麼將這個任務放入佇列; c) 如果這時候佇列滿了,而且正在執行的執行緒數量小maximumPoolSize,那麼還是要建立非核心執行緒立刻執行這個任務; d) 如果佇列滿了,而且正在執行的執行緒數量大於或等maximumPoolSize,那麼執行緒池會丟擲異常 RejectExecutionException。

(3)當一個執行緒完成任務時,它會從佇列中取下一個任務來執行。

(4)當一個執行緒無事可做,超過一定的時間(keepAliveTime)時,執行緒池會判斷,如果當前執行的執行緒數大於 corePoolSize,那麼這個執行緒就被停掉。所以執行緒池的所有任務完成後,它最終會收縮到 corePoolSize 的大小。

14.Java 常用演演算法

1. 快速排序演演算法
快速排序的原理:選擇一個關鍵值作為基準值。比基準值小的都在左邊序列(一般是無序的),比基準值大的都在右邊(一般是無序的)。一般選擇序列的第一個元素。一次迴圈:從後往前比較,用基準值和最後一個值比較,如果比基準值小的交換位置,如果沒有繼續比較下一個,直到找到第一個比基準值小的值才交換。找到這個值之後,又從前往後開始比較,如果有比基準值大的,交換位置,如果沒有繼續比較下一個,直到找到第一個比基準值大的值才交換。直到從前往後的比較索引>從後往前比較的索引,結束第一次迴圈,此時,對於基準值來說,左右兩邊就是有序的了。

 public void sort(int[] a,int low,int high){
         int start = low;
         int end = high;
         int key = a[low]; 
         while(end>start){
         //從後往前比較
         while(end>start&&a[end]>=key) 
        //如果沒有比關鍵值小的,比較下一個,直到有比關鍵值小的交換位置,然後又從前往後比較
         end--;
         if(a[end]<=key){
             int temp = a[end];
             a[end] = a[start];
             a[start] = temp;
         }
         //從前往後比較
         while(end>start&&a[start]<=key)
        //如果沒有比關鍵值大的,比較下一個,直到有比關鍵值大的交換位置
         start++;
         if(a[start]>=key){
             int temp = a[start];
             a[start] = a[end];
             a[end] = temp;
         }
         //此時第一次迴圈比較結束,關鍵值的位置已經確定了。左邊的值都比關鍵值小,右邊的值都比關鍵值大,但是兩邊的順序還有可能是不一樣的,進行下面的遞迴呼叫
     }
         //遞迴
        if(start>low) sort(a,low,start-1);//左邊序列。第一個索引位置到關鍵值索引-1
         if(end<high) sort(a,end+1,high);//右邊序列。從關鍵值索引+1 到最後一個
         }
 }

2 .氣泡排序演演算法
(1)比較前後相鄰的二個資料,如果前面資料大於後面的資料,就將這二個資料交換。

(2)這樣對陣列的第 0 個資料到 N-1 個資料進行一次遍歷後,最大的一個資料就「沉」到陣列第N-1 個位置。

(3)N=N-1,如果 N 不為 0 就重複前面二步,否則排序完成。

public static void bubbleSort1(int [] a, int n){
         int i, j;
         for(i=0; i<n; i++){//表示 n 次排序過程。
             for(j=1; j<n-i; j++){
                 if(a[j-1] > a[j]){//前面的數位大於後面的數位就交換
                //交換 a[j-1]和 a[j]
                int temp;
                temp = a[j-1];
                a[j-1] = a[j];
                a[j]=temp;
                }
            }
         }
    }

15.Spring Beans

什麼是Spring beans?

Spring beans 是那些形成Spring應用的主幹的java物件。它們被Spring IOC容器初始化,裝配,和管理。這些beans通過容器中設定的後設資料建立。比如,以XML檔案中 的形式定義。

一個 Spring Bean 定義 包含什麼?

一個Spring Bean 的定義包含容器必知的所有設定後設資料,包括如何建立一個bean,它的生命週期詳情及它的依賴。

如何給Spring 容器提供設定後設資料?Spring有幾種設定方式

這裡有三種重要的方法給Spring 容器提供設定後設資料。

XML組態檔。
基於註解的設定。
基於java的設定。

Spring組態檔包含了哪些資訊

Spring組態檔是個XML 檔案,這個檔案包含了類資訊,描述瞭如何設定它們,以及如何相互呼叫。

Spring基於xml注入bean的幾種方式

Set方法注入;
構造器注入:①通過index設定引數的位置;②通過type設定引數型別;
靜態工廠注入;
範例工廠;

總結:

網際網路大廠比較喜歡的人才特點:對技術有熱情,強硬的技術基礎實力;主動,善於團隊共同作業,善於總結思考。無論是哪家公司,都很重視高並行高可用技術,重視基礎,所以千萬別小看任何知識。面試是一個雙向選擇的過程,不要抱著畏懼的心態去面試,不利於自己的發揮。同時看中的應該不止薪資,還要看你是不是真的喜歡這家公司,是不是能真的得到鍛鍊。其實我寫了這麼多,只是我自己的總結,並不一定適用於所有人,相信經過一些面試,大家都會有這些感觸。

另外想要面試答案的小夥伴請點選795983544 暗號CSDN自行領取,本人還整理收藏了20年多家公司面試知識點以及各種技術點整理 下面有部分截圖希望能對大家有所幫助。
在這裡插入圖片描述