前言
金三銀四馬上要來了,整理了Java一些經典面試題,也給出了答案,希望對大家有幫助,有哪裏你覺得不正確的話,歡迎指出,非常感謝。
HashMap,HashTable,ConcurrentHash的共同點和區別
思路:可以從它們的底層結構、是否允許存儲null,是否線性安全等幾個維度進行描述,最後可以向面試官描述一下HashMap的死循環問題,以及ConcurrentHashMap爲啥放棄分段鎖。
HashMap
- 底層由鏈表+數組實現
- 可以存儲null鍵和null值
- 線性不安全
- 初始容量爲16,擴容每次都是2的n次冪
- 加載因子爲0.75,當Map中元素總數超過Entry數組的0.75,觸發擴容操作.
- 併發情況下,HashMap進行put操作會引起死循環,導致CPU利用率接近100%
有關於HashMap死循環,有興趣可以看看這篇文章,寫得很好: 老生常談,HashMap的死循環
有關於HashMap這些常量設計目的,也可以看我這篇文章:
面試加分項-HashMap源碼中這些常量的設計目的
HashTable
- HashTable的底層也是由鏈表+數組實現。
- 無論key還是value都不能爲null
- 它是線性安全的,使用了synchronized關鍵字。
ConcurrentHashMap
- 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 是基於事件和回調機制實現的,也就是應用操作之後會直接返回,不會堵塞在那裏,當後臺處理完成,操作系統會通知相應的線程進行後續的操作。
以上內容來自這篇文章,大家可以看一下,寫得比較詳細
談談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面試題呢?你也可以告訴我,哈哈。
參考與感謝
- String和New String()的區別
- 老生常談,HashMap的死循環
- Java核心(五)深入理解BIO、NIO、AIO
- 探討一下實現冪等性的幾種方式
- 在Java的反射中,Class.forName和ClassLoader的區別
- Java Serializable:明明就一個空的接口嘛
個人公衆號
- 如果你是個愛學習的好孩子,可以關注我公衆號,一起學習討論。
- 如果你覺得本文有哪些不正確的地方,可以評論,也可以關注我公衆號,私聊我,大家一起學習進步哈。