大數據開發(Java面試真題)
- 1、簡要介紹以下JVM有幾種垃圾收集器?
- 2、Java中Synchronized的底層原理是什么?
- 3、Java String為什么是不可變的?為什么要設計成不可變?
- 4、泛型?
- 5、常用的反射方法?
- 6、Java集合類型?
- 7、HashMap原理?轉換成紅黑樹條件?為什么這么設計?
- 8、Java線程安全的HashMap?ConcurrentHashMap和HashTable的區別?ConcurrentHashMap原理?
1、簡要介紹以下JVM有幾種垃圾收集器?
- Serial收集器:Serial收集器是JVM中最古老的一種垃圾回收器,它以單線程方式進行垃圾收集工作,適用于小型或者單核處理器的應用場景。
- Parallel收集器:Parallel收集器是Serial收集器的改進版本,它使用多線程進行垃圾收集,提高了垃圾收集的效率,適用于多核處理器的應用常見。
- CMS收集器:CMS收集器是一種以獲取最短回收停頓時間為目標的收集器,它通過并發的方式進行垃圾收集,能夠在主程序運行的同時進行垃圾收集,適用于對響應時間有較高要求的應用場景。
- G1收集器:G1收集器是一種面向服務端應用的垃圾收集器,它將堆內存劃分為多個區域,并根據垃圾產生情況優先回收垃圾角度的區域,可以達到較低的停頓時間和更好的吞吐量。
2、Java中Synchronized的底層原理是什么?
Synchronized是Java中用于實現線程同步的關鍵字,它的底層原理是通過對象監視器(也稱為內部鎖或監視鎖)來實現的。
當一個線程進入synchronized代碼塊時,它會嘗試獲取對應對象的監視器。如果該監視器沒有被其它線程占用,則該線程獲取到監視器并執行代碼塊中的邏輯。如果監視器已經被其它線程占用,該線程就會進入阻塞狀態,等待監視器的釋放。
在Java虛擬機中,每個對象都有一個與之關聯的監視器鎖。當一個線程獲取到該對象的監視器鎖時,其它線程就無法同時獲取該對象的監視器鎖,它們會被阻塞直到鎖被釋放。
在方法上適用synchronized關鍵字時,它會對該方法的整個代碼塊進行加鎖,以保證同一時間只有一個線程可以執行該方法。而在代碼塊上使用synchronized關鍵字時,它只會對該代碼塊進行加鎖,其它線程仍然可以同時執行其它非同步代碼塊。
需要注意的是,synchronized關鍵字會引入一定的性能開銷,因為每次進入synchronized代碼塊或方法時,都會進行加鎖或解鎖的操作。因此,在使用synchronized時需要權衡線程安全和性能之間的平衡。
3、Java String為什么是不可變的?為什么要設計成不可變?
Java中的String是不可變的,這是因為String類被設計成了不可變的對象。這意味著一旦一個String對象被創建,它的值就不能被修改。
不可變有以下幾個原因:
- 線程安全:不可變的特性使得String對象在多線程環境中是安全的。因為它的值不可變,不會被其它線程修改,所以不需要同步控制。
- 緩存哈希值:String類將哈希值緩存在對象中,因為它是不可變的,所以哈希值只需要計算一次,而不需要每次使用時重新計算。這樣可以提高性能。
- 字符串池:Java中的字符串池是為了節省內存而設計的。不可變的String對象可以被共享并重復使用,避免了創建多個相同值得字符串對象。這樣可以減少內存占用,提高性能。
- 安全性:字符串作為參數傳遞給一些敏感得API時,不可變的特性可以確保參數的值不會被修改,從而保證數據的安全性。
綜述所述,Java中的String被設計成不可變的主要是為了提高性能、確保線程安全以及節省內存。
4、泛型?
泛型是Java中一種參數化類型的概念,它允許我們在編寫類、接口和方法時使用未知的數據類型。通過使用泛型,我們可以創建通用的代碼,可以在不同的數據類型上進行操作,提高代碼的復用性和類型安全性。泛型使用尖括號<>來定義,可以用于類、接口和方法的聲明和實例化中。在使用泛型時,可以指定具體的數據類型,也可以使用通配符來表示未知的數據類型。
5、常用的反射方法?
常用的反射方法有:
- 獲取Class對象:通過Class.forName(),對象.getClass()、類名.class等方式獲取一個類的Class對象。
- 創建實例:通過Class對象的newInstance()方法創建類的實例。
- 獲取類的成員變量:通過Class對象的getField()、getDeclaredField()等方法獲取類的公共或私有成員變量。
- 獲取類的方法:通過Class對象的getMethod()、getDeclaredMethod()等方法獲取類的公共或私有方法。
- 調用方法:通過Method對象的invoke()方法調用方法。
- 修改成員變量的值:通過Field對象的set()方法修改成員變量的值。
- 調用構造函數:通過Class對象的getConstructor()、getDeclaredConstructor()等方法獲取類的公共或私有構造函數,并通過Constructor對象的newInstance()方法創建類的實例。
這些方法可以在運行時動態地獲取和操作類的信息,使得我們可以在不知道具體類名地情況下,通過反射機制來調用類地方法、訪問成員變量等。
6、Java集合類型?
Java集合類型有以下幾種:
- List(列表):List是一個有序的集合,可以包含重復元素。常見的實現類有ArrayList和LinkedList。
- Set(集合):Set是一個不允許重復元素的集合。常見的實現類有HashSet和TreeSet。
- Map(映射):Map是一種鍵值對的集合,每個鍵只能對應一個值。常見的實現類有HashMap和TreeMap。
- Queue(隊列):Queue是一種先進先出(FIFO)的集合。常見的實現類有LinkedList和PriorityQueue。
- Stack(棧):Stack是一種后進先出的集合。常見的實現類有Stack。
- Vector(向量):Vector是一個動態數組,與ArrayList類似,但是它是線程安全的。
7、HashMap原理?轉換成紅黑樹條件?為什么這么設計?
HashMap是Java中常見的一種數據結構,它基于哈希表實現。具體原理如下:
- HashMap內部由一個數組和鏈表(或紅黑樹)組成。數組是HashMap的主體,用于存儲鍵值對。鏈表和紅黑樹用于解決哈希沖突,提高查找效率。
- 當添加一個鍵值對到HashMap中時,首先根據鍵的hashCode()方法計算出一個哈希值,然后通過哈希值與數組長度取模,得到在數組中的位置。如果該位置上已經存在其它鍵值對,就發生了哈希沖突。
- 如果發生哈希沖突,會在該位置上的鏈表或紅黑樹上順序查找鍵值對。如果鍵已經存在,則更新對應的值。如果鍵不存在,則在鏈表或紅黑樹的末尾添加新的鍵值對。
- 當鏈表長度超過閾值(默認為8)時,鏈表會轉換為紅黑樹。
轉換為紅黑樹的條件如下:
-當鏈表長度達到8時,且當前數組長度大于等于64,HashMap會將鏈表轉換為紅黑樹。
-當紅黑樹節點數量小于等于6時,HashMap會將紅黑樹轉換為鏈表。
這樣設計的原因是,當鏈表長度過長時,查找效率會降低,因為需要遍歷鏈表進行查找。而紅黑樹相比鏈表,具有更高效的查找、插入和刪除操作,能夠更好地提高HashMap的性能。同時,對于較小的鏈表,轉換為紅黑樹的開銷反而比鏈表更大,所以在節點數量小于等于6時,會將紅黑樹轉換回鏈表,以節省內存空間。
8、Java線程安全的HashMap?ConcurrentHashMap和HashTable的區別?ConcurrentHashMap原理?
Java線程安全的HashMap可以使用ConcurrentHashMap來實現。ConcurrentHashMap是Java并發包中的一個線程安全的哈希表實現,它比傳統的HashTable和同步的HashMap具有更好的并發性能。
ConcurrentHashMap和HashTable的區別如下:
- 鎖機制:ConcurrentHashMap使用了分段鎖(Segment),每個Segment相當于一個小的HashTable,只鎖住當前操作的Segement而不是整個HashTable;而HashTable在每次操作時都鎖住整個HashTable。
- 并發性:ConcurrentHashMap允許多個線程同時讀取,而寫操作會鎖住相關的Segment,使得在并發寫入時性能更好;HashTable在寫操作時需要鎖住整個HashTable,導致并發寫入性能較差。
- 擴容機制:ConcurrentHashMap在擴容時只需要鎖住相關的Segment,不影響其它Segment的讀寫操作,提高了并發性能;HashTable在擴容時需要鎖住整個HashTable,導致其它線程無法讀寫,性能較差。
ConcurrentHashMap的原理是基于分段鎖(Segment)實現的。它將整個哈希表分成多個小的HashTable(Segment),每個Segment獨立地進行鎖定和擴容操作。這樣可以提高并發性能,允許多個線程同時讀取,而寫操作只需要鎖定相關地Segment,不影響其它Segment的讀寫操作。同時ConcurrentHashMap使用CAS算法來保證并發操作的一致性和線程安全性。