常見Java面試題解析(基礎篇,附答案)

前言

金三銀四立刻要來了,整理了Java一些經典面試題,也給出了答案,但願對你們有幫助,有哪裏你以爲不正確的話,歡迎指出,很是感謝。html

HashMap,HashTable,ConcurrentHash的共同點和區別

思路:能夠從它們的底層結構、是否容許存儲null,是否線性安全等幾個維度進行描述,最後能夠向面試官描述一下HashMap的死循環問題,以及ConcurrentHashMap爲啥放棄分段鎖。java

HashMap程序員

  • 底層由鏈表+數組實現
  • 能夠存儲null鍵和null值
  • 線性不安全
  • 初始容量爲16,擴容每次都是2的n次冪
  • 加載因子爲0.75,當Map中元素總數超過Entry數組的0.75,觸發擴容操做.
  • 併發狀況下,HashMap進行put操做會引發死循環,致使CPU利用率接近100%

有關於HashMap死循環,有興趣能夠看看這篇文章,寫得很好: 老生常談,HashMap的死循環面試

有關於HashMap這些常量設計目的,也能夠看我這篇文章: 面試加分項-HashMap源碼中這些常量的設計目的redis

HashTable算法

  • HashTable的底層也是由鏈表+數組實現。
  • 不管key仍是value都不能爲null
  • 它是線性安全的,使用了synchronized關鍵字。

ConcurrentHashMapspring

  • ConcurrentHashMap的底層是數組+鏈表/紅黑樹
  • 不能存儲null鍵和值
  • ConcurrentHashMap是線程安全的
  • ConcurrentHashMap使用鎖分段技術確保線性安全
  • JDK8爲什麼又放棄分段鎖,是由於多個分段鎖浪費內存空間,競爭同一個鎖的機率很是小,分段鎖反而會形成效率低。

ArrayList和LinkedList有什麼區別。

思路:從它們的底層數據結構、效率、開銷進行闡述數據庫

  • ArrayList是數組的數據結構,LinkedList是鏈表的數據結構。
  • 隨機訪問的時候,ArrayList的效率比較高,由於LinkedList要移動指針,而ArrayList是基於索引(index)的數據結構,能夠直接映射到。
  • 插入、刪除數據時,LinkedList的效率比較高,由於ArrayList要移動數據。
  • LinkedList比ArrayList開銷更大,由於LinkedList的節點除了存儲數據,還須要存儲引用。

String,Stringbuffer,StringBuilder的區別。

String:設計模式

  • String類是一個不可變的類,一旦建立就不能夠修改。
  • String是final類,不能被繼承
  • String實現了equals()方法和hashCode()方法

StringBuffer:數組

  • 繼承自AbstractStringBuilder,是可變類。
  • StringBuffer是線程安全的
  • 能夠經過append方法動態構造數據。

StringBuilder:

  • 繼承自AbstractStringBuilder,是可變類。
  • StringBuilder是非線性安全的。
  • 執行效率比StringBuffer高。

JAVA中的幾種基本數據類型是什麼,各自佔用多少字節。

基本類型 位數 字節
int 32 4
short 16 2
long 64 8
byte 8 1
char 16 2
float 32 4
double 64 8
boolean 1 1/8

看例子:

public class Test {
    public static void main(String[] args) {
        System.out.println("Byte bit num: " + Byte.SIZE);
        System.out.println("Short bit num : " + Short.SIZE);
        System.out.println("Character bit num: " + Character.SIZE);
        System.out.println("Integer bit num: " + Integer.SIZE);
        System.out.println("Float bit num: " + Float.SIZE);
        System.out.println("Long bit num: " + Long.SIZE);
        System.out.println("Double bit num: " + Double.SIZE);
    }
}
複製代碼

運行結果:

Byte bit num: 8
Short bit num : 16
Character bit num: 16
Integer bit num: 32
Float bit num: 32
Long bit num: 64
Double bit num: 64
複製代碼

String s 與new String的區別

String str ="whx";
String newStr =new String ("whx");
複製代碼

String str ="whx"

先在常量池中查找有沒有"whx" 這個對象,若是有,就讓str指向那個"whx".若是沒有,在常量池中新建一個「whx」對象,並讓str指向在常量池中新建的對象"whx"。

String newStr =new String ("whx");

是在堆中創建的對象"whx" ,在棧中建立堆中"whx" 對象的內存地址。

如圖所示:

這篇文章講的挺好的: String和New String()的區別

Bio、Nio、Aio區別

BIO

就是傳統的 java.io 包,它是基於流模型實現的,交互的方式是同步、阻塞方式,也就是說在讀入輸入流或者輸出流時,在讀寫動做完成以前,線程會一直阻塞在那裏,它們之間的調用時可靠的線性順序。它的有點就是代碼比較簡單、直觀;缺點就是 IO 的效率和擴展性很低,容易成爲應用性能瓶頸。

NIO

是 Java 1.4 引入的 java.nio 包,提供了 Channel、Selector、Buffer 等新的抽象,能夠構建多路複用的、同步非阻塞 IO 程序,同時提供了更接近操做系統底層高性能的數據操做方式。

AIO

是 Java 1.7 以後引入的包,是 NIO 的升級版本,提供了異步非堵塞的 IO 操做方式,因此人們叫它 AIO(Asynchronous IO),異步 IO 是基於事件和回調機制實現的,也就是應用操做以後會直接返回,不會堵塞在那裏,當後臺處理完成,操做系統會通知相應的線程進行後續的操做。

以上內容來自這篇文章,你們能夠看一下,寫得比較詳細

Java核心(五)深刻理解BIO、NIO、AIO

談談spring的生命週期

  • 首先容器啓動後,對bean進行初始化
  • 按照bean的定義,注入屬性
  • 檢測該對象是否實現了xxxAware接口,並將相關的xxxAware實例注入給bean,如BeanNameAware等
  • 以上步驟,bean對象已正確構造,經過實現BeanPostProcessor接口,能夠再進行一些自定義方法處理。 如:postProcessBeforeInitialzation。
  • BeanPostProcessor的前置處理完成後,能夠實現postConstruct,afterPropertiesSet,init-method等方法, 增長咱們自定義的邏輯,
  • 經過實現BeanPostProcessor接口,進行postProcessAfterInitialzation後置處理
  • 接着Bean準備好被使用啦。
  • 容器關閉後,若是Bean實現了DisposableBean接口,則會回調該接口的destroy()方法
  • 經過給destroy-method指定函數,就能夠在bean銷燬前執行指定的邏

反射的原理,反射建立類實例的三種方式是什麼。

Java反射機制:

Java 的反射機制是指在運行狀態中,對於任意一個類都可以知道這個類全部的屬性和方法; 而且對於任意一個對象,都可以調用它的任意一個方法;這種動態獲取信息以及動態調用對象方法的功能成爲Java語言的反射機制

獲取 Class 類對象三種方式:

  • 使用 Class.forName 靜態方法
  • 使用類的.class 方法
  • 使用實例對象的 getClass() 方法

能夠看一下我寫的這篇文章: 談談Java反射:從入門到實踐,再到原理

說幾種實現冪等的方式

什麼是冪等性?一次和屢次請求某一個資源對於資源自己應該具備一樣的結果。就是說,其任意屢次執行對資源自己所產生的影響均與一次執行的影響相同。

實現冪等通常有如下幾種方式:

  • 悲觀鎖方式(如數據庫的悲觀鎖,select...for update)
  • 樂觀鎖方式 (如CAS算法)
  • 惟一性約束(如惟一索引)
  • 分佈式鎖 (redis分佈式鎖等)

能夠看一下這篇文章,寫得不錯: 探討一下實現冪等性的幾種方式

講講類的實例化順序,如父類靜態數據,構造函數,字段,子類靜態數據,構造函數,字段等。

直接看個例子吧:

public class Parent {
    {
        System.out.println("父類非靜態代碼塊");
    }
    static {
        System.out.println("父類靜態塊");
    }
    public Parent() {
        System.out.println("父類構造器");
    }
}
public class Son extends Parent {
    public Son() {
        System.out.println("子類構造器");
    }
    static {
        System.out.println("子類靜態代碼塊");
    }
    {
        System.out.println("子類非靜態代碼塊");
    }
}
public class Test {
    public static void main(String[] args) {
        Son son = new Son();
    }
}
複製代碼

運行結果:

父類靜態塊
子類靜態代碼塊
父類非靜態代碼塊
父類構造器
子類非靜態代碼塊
子類構造器
複製代碼

因此,類實例化順序爲: 父類靜態代碼塊/靜態域->子類靜態代碼塊/靜態域 -> 父類非靜態代碼塊 -> 父類構造器 -> 子類非靜態代碼塊 -> 子類構造器

反射中,Class.forName和ClassLoader區別

Class.forName和ClassLoader均可以對類進行加載。它們區別在哪裏呢? ClassLoader負責加載 Java 類的字節代碼到 Java 虛擬機中。Class.forName實際上是調用了ClassLoader,以下:

這裏面,forName0的第二個參數爲true,表示對加載的類進行初始化化。其實還能夠調用 Class<?> forName(String name, boolean initialize, ClassLoader loader)方法實現同樣的功能,它的源碼以下:

因此,Class.forName和ClassLoader的區別,就是在類加載的時候,class.forName有參數控制是否對類進行初始化。

JDK動態代理與cglib實現的區別

  • java動態代理是利用反射機制生成一個實現代理接口的匿名類,在調用具體方法前調用InvokeHandler來處理。
  • cglib動態代理是利用asm開源包,對代理對象類的class文件加載進來,經過修改其字節碼生成子類來處理。
  • JDK動態代理只能對實現了接口的類生成代理,而不能針對類
  • cglib是針對類實現代理,主要是對指定的類生成一個子類,覆蓋其中的方法。由於是繼承,因此該類或方法最好不要聲明成final

這篇文章寫得不錯,描述Java動態代理的幾種實現方式,分別說出相應的優缺點

error和exception的區別,CheckedException,RuntimeException的區別。

Error: 表示編譯時或者系統錯誤,如虛擬機相關的錯誤,OutOfMemoryError等,error是沒法處理的。

Exception: 代碼異常,Java程序員關心的基類型一般是Exception。它能被程序自己能夠處理,這也是它跟Error的區別。

它能夠分爲RuntimeException(運行時異常)和CheckedException(可檢查的異常)。 常見的RuntimeException異常:

- NullPointerException 空指針異常
- ArithmeticException 出現異常的運算條件時,拋出此異常
- IndexOutOfBoundsException 數組索引越界異常
- ClassNotFoundException 找不到類異常
- IllegalArgumentException(非法參數異常)
複製代碼

常見的 Checked Exception 異常:

- IOException (操做輸入流和輸出流時可能出現的異常)
- ClassCastException(類型轉換異常類)
複製代碼

有興趣能夠看我以前寫得這篇文章: Java程序員必備:異常的十個關鍵知識點

CAS機制是什麼,如何解決ABA問題?

CAS涉及三個操做數

  • 1.須要讀寫的內存地址V
  • 2.進行比較的預期原值A
  • 3.擬寫入的新值B 若是內存位置的值V與預期原A值相匹配,那麼處理器會自動將該位置值更新爲新值B。

CAS思想:要進行更新時,認爲位置V上的值仍是跟A值相等,若是是是相等,就認爲它沒有被別的線程更改過,便可更新爲B值。不然,認爲它已經被別的線程修改過,不更新爲B的值,返回當前位置V最新的值。

有興趣的朋友能夠看一下我這篇文章,一次CAS思想解決實際問題: CAS樂觀鎖解決併發問題的一次實踐

深拷貝和淺拷貝區別

淺拷貝

複製了對象的引用地址,兩個對象指向同一個內存地址,因此修改其中任意的值,另外一個值都會隨之變化。

深拷貝

將對象及值複製過來,兩個對象修改其中任意的值另外一個值不會改變

談談序列化與反序列化

  • 序列化是指將對象轉換爲字節序列的過程,而反序列化則是將字節序列轉換爲對象的過程。
  • Java對象序列化是將實現了Serializable接口的對象轉換成一個字節序列,可以經過網絡傳輸、文件存儲等方式傳輸 ,傳輸過程當中卻沒必要擔憂數據在不一樣機器、不一樣環境下發生改變,也沒必要關心字節的順序或其餘任何細節,並可以在之後將這個字節序列徹底恢復爲原來的對象。

這篇文章寫得很好: Java Serializable:明明就一個空的接口嘛

==與equlas有什麼區別?

==

  • 若是是基本類型,==表示判斷它們值是否相等;
  • 若是是引用對象,==表示判斷兩個對象指向的內存地址是否相同。

equals

  • 若是是字符串,表示判斷字符串內容是否相同;
  • 若是是object對象的方法,比較的也是引用的內存地址值;
  • 若是本身的類重寫equals方法,能夠自定義兩個對象是否相等。

談談AQS 原理以及AQS同步組件

AQS原理面試題的核心回答要點

  • state 狀態的維護。
  • CLH隊列
  • ConditionObject通知
  • 模板方法設計模式
  • 獨佔與共享模式。
  • 自定義同步器。
  • AQS全家桶的一些延伸,如:ReentrantLock等。

能夠看我這篇文章:AQS解析與實戰

final、finalize()、finally的區別

  • final是關鍵字,用於修飾類、成員變量和成員方法。
  • Finalize是object類中的一個方法,子類能夠重寫finalize()方法實現對資源的回收。
  • finally通常跟try一塊兒,出如今異常處理代碼塊中

synchronized 底層如何實現?

能夠看一下我這篇文章: Synchronized解析——若是你願意一層一層剝開個人心

Java線程池的原理?線程池有哪些?線程池工廠有哪些線程池類型,及其線程池參數是什麼?

對於Java線程池,這個流程圖比較重要:

能夠看我這篇文章: 面試必備:Java線程池解析

待更新

還有哪些經典Java面試題呢?你也能夠告訴我,哈哈。

參考與感謝

我的公衆號

  • 若是你是個愛學習的好孩子,能夠關注我公衆號,一塊兒學習討論。
  • 若是你以爲本文有哪些不正確的地方,能夠評論,也能夠關注我公衆號,私聊我,你們一塊兒學習進步哈。
相關文章
相關標籤/搜索