前言
我發現有很多程式設計師面試前都是準備地好好的,什麼疑難雜症,未解之謎都是準備得妥妥的,張口就來。反而到了最容易的Java
基礎的時候,各種翻車(可能是覺得基礎的內容太簡單沒有花精力),本來是能夠拿到更高的薪資,就因為基礎沒有回答好,被
抓住當成藉口又砍了好幾K,實在是得不償失。所以今天給大家分享一份Java基礎的面試題彙總以及解析,以便大家更好地應對
面試,衝擊更高薪資!
1. String類為什麼是final的
主要是為了”安全性“和”效率“的緣故,因為:
1、由於String類不能被繼承,所以就不會沒修改,這就避免了因為繼承引起的安全隱患;
2、String類在程式中出現的頻率比較高,如果為了避免安全隱患,在它每次出現時都用final來修飾,這無疑會降低程式的執行效
率,所以乾脆直接將其設為final一提高效率;
2. HashMap的原始碼,實現原理,底層結構。
HashMap就是陣列+連結串列的組合實現,每個陣列元素儲存一個連結串列的頭結點,本質上來說是雜湊表“拉鍊法”的實現。
HashMap的連結串列元素對應的是一個靜態內部類Entry,Entry主要包含key,value,next三個元素
主要有put和get方法,put的原理是,通過hash%Entry.length計算index,此時記作Entry[index]=該元素。如果index相同
就是新入的元素放置到Entry[index],原先的元素記作Entry[index].next
get就比較簡單了,先遍歷陣列,再遍歷連結串列元素。
null key總是放在Entry陣列的第一個元素
解決hash衝突的方法:鏈地址法
再雜湊rehash的過程:確定容量超過目前雜湊表的容量,重新調整table 的容量大小,當超過容量的最大值時,取
Integer.Maxvalue
3. 什麼是Java集合類?說說你知道的幾個Java集合類。
集合類存放於java.util包中。
集合類存放的都是物件的引用,而非物件本身,出於表達上的便利,我們稱集合中的物件就是指集合中物件的引用(reference)。
集合型別主要有3種:set(集)、list(列表)和map(對映)。
集合介面分為:Collection和Map,list、set實現了Collection介面
4. 描述一下ArrayList和LinkedList各自實現和區別
ArrayList,LinkedList都實現了java.util.List介面,
- ArrayList是實現了基於動態陣列的資料結構,LinkedList基於連結串列的資料結構。
- 對於隨機訪問get和set,ArrayList覺得優於LinkedList,因為LinkedList要移動指標。
- 對於新增和刪除操作add和remove,LinedList比較佔優勢,因為ArrayList要移動資料。
5. Java中的佇列都有哪些,有什麼區別
Java中的佇列都有哪些,實際上是問queue的實現有哪些,如:ConcurrentLinkedQueue、LinkedBlockingQueue 、
ArrayBlockingQueue、LinkedList。
關於ConcurrentLinkedQueue和LinkedBlockingQueue:
- LinkedBlockingQueue是使用鎖機制,ConcurrentLinkedQueue是使用CAS演算法,雖然LinkedBlockingQueue的底層獲取鎖也是使用的CAS演算法
- 關於取元素,ConcurrentLinkedQueue不支援阻塞去取元素,LinkedBlockingQueue支援阻塞的take()方法,如若大家需要ConcurrentLinkedQueue的消費者產生阻塞效果,需要自行實現
- 關於插入元素的效能,從字面上和程式碼簡單的分析來看ConcurrentLinkedQueue肯定是最快的,但是這個也要看具體的測試場景,我做了兩個簡單的demo做測試,測試的結果如下,兩個的效能差不多,但在實際的使用過程中,尤其在多cpu的伺服器上,有鎖和無鎖的差距便體現出來了,ConcurrentLinkedQueue會比LinkedBlockingQueue快很多:ConcurrentLinkedQueuePerform:在使用ConcurrentLinkedQueue的情況下100個執行緒迴圈增加的元素數為:33828193
LinkedBlockingQueuePerform:在使用LinkedBlockingQueue的情況下100個執行緒迴圈增加的元素數為:33827382
6. 反射中,Class.forName和classloader的區別
Java中Class.forName和classloader都可以用來對類進行載入。
Class.forName除了將類的.class檔案載入到jvm中之外,還會對類進行解釋,執行類中的static塊。
而classloader只幹一件事情,就是將.class檔案載入到jvm中,不會執行static中的內容,只有在newInstance才會去執行static塊。
Class.forName(name,initialize,loader)帶引數也可控制是否載入static塊。並且只有呼叫了newInstance()方法採用呼叫建構函式,建立類的物件。
7. Java7、Java8的新特性(baidu問的,好BT)
以下特性為個人比較關注的特性,並不齊全;想了解更多,請自行搜尋官方文件。
Java7特性:
1.switch case可以使用String,原來只能用int和char;
2.支援2進位制0b開頭;支援數字中間有下劃線,解析時自動剔除;
3.一次抓多個異常;用|隔開;
4.try-with-resource,在try中開啟資源,系統自動在使用完後關閉;
5. Map<String, List<String>> anagrams = new HashMap<>(); 對抗Google的guava.
6.集合類可以像js中的陣列一樣賦值和引用了。
List<String> list = ["item"];
String item = list[0];
Set<String> set = {"item"};
Map<String, Integer> map = {"key" : 1};
int value = map["key"];
7. 把字串常量池從permgen區移到了堆區;導致String.intern()方法在1.7之前和之後表現出現不一致;
Java8特性:
1.lambda表示式;
2.新增stream,Date,Time,Base64工具類;
3.使用metaspace,元空間替代permgen區;
4.類依賴分析器:jdeps,可以以包,目錄,資料夾作為輸入,輸出依賴關係,沒有的會顯示 not found
5.jjs,可以執行JavaScript程式碼;
8. Java陣列和連結串列兩種結構的操作效率,在哪些情況下,哪些操作的效率高
陣列在隨機訪問資料、隨機增加資料、隨機刪除資料的執行效率上比連結串列的效率高,資料量越小,兩者之間效率的差距越小,資料量越大差距越大。
也歡迎大家一起討論面試終於到的各種奇葩問題,特地建了一個Java技術交流群:895244712,希望有個小圈子的朋友可以加進來,不定時分享一些技術乾貨,希望能帶來幫助。
9. Java記憶體洩露的問題調查定位:jmap,jstack的使用等等
詳細解析:blog.csdn.net/sinat_29581…
10. string、stringbuilder、stringbuffer區別
這三個類之間的區別主要是在兩個方面,即執行速度和執行緒安全這兩方面。
- 首先說執行速度,或者說是執行速度,在這方面執行速度快慢為:StringBuilder > StringBuffer > String
String最慢的原因:
String為字串常量,而StringBuilder和StringBuffer均為字串變數,即String物件一旦建立之後該物件是不可更改的,但後兩者的物件是變數,是可以更改的。
2. 再來說執行緒安全
線上程安全上,StringBuilder是執行緒不安全的,而StringBuffer是執行緒安全的
如果一個StringBuffer物件在字串緩衝區被多個執行緒使用時,StringBuffer中很多方法可以帶有synchronized關鍵字,所以可以保證執行緒是安全的,但StringBuilder的方法則沒有該關鍵字,所以不能保證執行緒安全,有可能會出現一些錯誤的操作。所以如果要進行的操作是多執行緒的,那麼就要使用StringBuffer,但是在單執行緒的情況下,還是建議使用速度比較快的StringBuilder。
3. 總結一下
String:適用於少量的字串操作的情況
StringBuilder:適用於單執行緒下在字元緩衝區進行大量操作的情況
StringBuffer:適用多執行緒下在字元緩衝區進行大量操作的情況
11.hashtable和hashmap的區別
1. 儲存結構
HashMap |
HashTable |
陣列 + 連結串列/紅黑樹 |
陣列 + 連結串列 |
HashMap的儲存規則:
優先使用陣列儲存, 如果出現Hash衝突, 將在陣列的該位置拉伸出連結串列進行儲存(在連結串列的尾部進行新增), 如果連結串列的長度大於設定值後, 將連結串列轉為紅黑樹.
HashTable的儲存規則:
優先使用陣列儲存, 儲存元素時, 先取出下標上的元素(可能為null), 然後新增到陣列元素Entry物件的next屬性中(在連結串列的頭部進行新增).
出現Hash衝突時, 新元素next屬性會指向衝突的元素. 如果沒有Hash衝突, 則新元素的next屬性就是null
描述的有點模糊, 貼出原始碼會清晰一點:
Entry<K,V> e = (Entry<K,V>) tab[index];
tab[index] = new Entry<>(hash, key, value, e);
複製程式碼
2. 擴容方式
HashMap |
HashTable |
oldCap * 2 |
oldCap * 2 + 1 |
3. 關於null值
HashMap |
HashTable |
key, value 均可以為 null |
key, value 均不可以為 null |
4. 執行緒安全
HashMap | HashTable |
執行緒不安全 | 執行緒安全 |
13 .異常的結構,執行時異常和非執行時異常
大神的解釋:blog.csdn.net/qq_27093465…
14. String a= “abc” String b = “abc” String c = new String(“abc”) String d = “ab” + “c” .他們之間用 == 比較的結果
傳送門:blog.csdn.net/qq_36381855…
15. String 類的常用方法
String類中提供了大量的操作方法,這裡例舉13種關於String類常用的方法供大家參考。參考程式碼如下:
package cn.mc;
public class StringTestMc {
private String str = "helloWorld";
/**
* 將字串變成一個字元陣列
*/
public void tocharyArry() {
char c[] = str.toCharArray();
for (int i = 0; i < c.length; i++) {
System.out.println("轉為陣列輸出:" + c[i]);
}
}
/**
* 從字串中取出指定位置的字元
*/
public void tocharAt() {
char c = str.charAt(3);
System.out.println("指定字元為:" + c);
}
/**
* 將字串變成一個byte陣列
*/
public void tobyte() {
byte b[] = str.getBytes();
System.out.println("轉換成byte陣列輸出為:" + new String(b));
}
/**
* 取得一個字串的長度
*/
public void tolength() {
int l = str.length();
System.out.println("這個字串的長度為:" + l);
}
/**
* 查詢一個指定的字串是否存在,返回的是字串的位置,如果不存在,則返回-1
*/
public void toindexOf() {
int a1 = str.indexOf("e");// 查詢字元e的位置
int a2 = str.indexOf("l", 2);// 查詢l的位置,從第3個開始查詢
System.out.println("e的位置為:" + a1);
System.out.println("l的位置為:" + a2);
}
/**
* 去掉字串左右空格
*/
public void totrim() {
String str1 = " hello ";
System.out.println("去掉左右空格後輸出:" + str1.trim());
}
/**
* 字串的擷取
*/
public void tosubstring() {
System.out.println("擷取後的字元為:" + str.substring(0, 3));// 擷取0-3個位置的內容
System.out.println("從第3個位置開始擷取:" + str.substring(2));// 從第3個位置開始擷取
}
/**
* 按照指定的字串拆分字元,拆分的資料將以字串陣列的形式返回
*/
public void tosplit() {
String s[] = str.split("e");// 按hello中的e進行字串拆分
for (int i = 0; i < s.length; i++) {
System.out.println("拆分後結果為:" + s[i]);
}
}
/**
* 將字串進行大小寫轉換
*/
public void tochange() {
System.out.println("將\"hello\"轉換成大寫為:" + str.toUpperCase());// 將hello轉換成大寫
System.out.println("將\"HELLO\"轉換成大寫為:"
+ str.toUpperCase().toLowerCase());// 將HELLO轉換成小寫
}
/**
* 判斷是否以指定的字串開頭或者結尾
*/
public void tostartsWithOrendWith()
{
if(str.startsWith("he"))//判斷字串是否以he開頭
{
System.out.println("字串是以he開頭");
}
if(str.endsWith("lo"))
{
System.out.println("字串是以lo結尾");
}
}
/**
* 兩個String型別內容比較
*/
public void toequals()
{
String str3="world";
if(str.equals(str3))
{
System.out.println("這倆個String型別的值相等");
}
else
System.out.println("這倆個String型別的不值相等");
}
/**
* 兩個字串不區分大小寫進行比較
*/
public void toequalslgnoreCase()
{
String str4="HELLO";
if(str.equalsIgnoreCase(str4))
{
System.out.println("hello和HELLO忽略大小寫比較值相等");
}
}
/**
* 將一個指定得到字串替換成其他字串
*/
public void toreplaceAll()
{
String str5=str.replaceAll("l", "a");
System.out.println("替換後的結果為:"+str5);
}
public static void main(String[] args) {
StringTest obj = new StringTest();
obj.tocharyArry();
obj.tocharAt();
obj.tobyte();
obj.tolength();
obj.toindexOf();
obj.totrim();
obj.tosubstring();
obj.tosplit();
obj.tochange();
obj.tostartsWithOrendWith();
obj.toequals();
obj.toequalslgnoreCase();
obj.toreplaceAll();
}
}
複製程式碼
16. Java 的引用型別有哪幾種
有這樣一類物件:當記憶體空間還足夠,則可保留在記憶體中;如果記憶體空間在gc之後還是非常緊張,則可拋棄這些物件。很多系統的快取功能適合這樣的場景,所以jdk1.2以後
java將引用分為了強引用、軟引用、弱引用、虛引用四種,引用強度一次減弱。
- 強引用:類似Object a=new Object()這類,永遠不會被回收。
- 軟引用:SoftReference,當系統快要發生記憶體溢位異常時,將會把這些物件列入回收範圍進行二次回收,如果這次回收還是沒有足夠記憶體,則丟擲記憶體溢位異常。
- 弱引用:比軟引用更弱,活不過下一次gc。無論當前記憶體是否足夠,下一次gc都會被回收掉。
- 虛引用:又叫幻引用,最弱,一個物件時候有虛引用的存在,不會對它的生存時間構成影響,唯一目的就是能在這物件被回收以後收到一個系統通知。。
17. 抽象類和介面的區別
介面是公開的,裡面不能有私有的方法或變數,是用於讓別人使用的,而抽象類是可以有私有方法或私有變數的,
另外,實現介面的一定要實現介面裡定義的所有方法,而實現抽象類可以有選擇地重寫需要用到的方法,一般的應用裡,最頂級的是介面,然後是抽象類實現介面,最後才到具體類實現。
還有,介面可以實現多重繼承,而一個類只能繼承一個超類,但可以通過繼承多個介面實現多重繼承,介面還有標識(裡面沒有任何方法,如Remote介面)和資料共享(裡面的變數全是常量)的作用.
18. java的基礎型別和位元組大小
java資料型別 位元組 表示範圍
byte(位元組型) 1 -128~127
boolean(布林型) 1 true或false
short(短整型) 2 -32768~32767
char(字元型) 2 從字元型對應的整型數來劃分,其表示範圍是0~65535
int(整型) 4 -2147483648~2147483647
float(浮點型) 4 -3.4E38~3.4E38
double(雙精度型) 8 -1.7E308~1.7E308
long(長整型) 8 -9223372036854775808 ~ 9223372036854775807
19. Hashtable,HashMap,ConcurrentHashMap 底層實現原理與執行緒安全問題
大神又來了:blog.csdn.net/qq_27093465…
21. Hash衝突怎麼辦?哪些解決雜湊衝突的方法?
Hash演算法解決衝突的方法一般有以下幾種常用的解決方法
1, 開放定址法:
所謂的開放定址法就是一旦發生了衝突,就去尋找下一個空的雜湊地址,只要雜湊表足夠大,空的雜湊地址總能找到,並將記錄存入
公式為:fi(key) = (f(key)+di) MOD m (di=1,2,3,……,m-1)
※ 用開放定址法解決衝突的做法是:當衝突發生時,使用某種探測技術在雜湊表中形成一個探測序列。沿此序列逐個單元地查詢,直到找到給定的關鍵字,或者
碰到一個開放的地址(即該地址單元為空)為止(若要插入,在探查到開放的地址,則可將待插入的新結點存人該地址單元)。查詢時探測到開放的地址則表明表
中無待查的關鍵字,即查詢失敗。
比如說,我們的關鍵字集合為{12,67,56,16,25,37,22,29,15,47,48,34},表長為12。 我們用雜湊函式f(key) = key mod l2
當計算前S個數{12,67,56,16,25}時,都是沒有衝突的雜湊地址,直接存入:
計算key = 37時,發現f(37) = 1,此時就與25所在的位置衝突。
於是我們應用上面的公式f(37) = (f(37)+1) mod 12 = 2。於是將37存入下標為2的位置:
2, 再雜湊法:
再雜湊法又叫雙雜湊法,有多個不同的Hash函式,當發生衝突時,使用第二個,第三個,….,等雜湊函式
計算地址,直到無衝突。雖然不易發生聚集,但是增加了計算時間。
3, 鏈地址法:
鏈地址法的基本思想是:每個雜湊表節點都有一個next指標,多個雜湊表節點可以用next指標構成一個單向連結串列,被分配到同一個索引上的多個節點可以用這個單向
連結串列連線起來,如:
鍵值對k2, v2與鍵值對k1, v1通過計算後的索引值都為2,這時及產生衝突,但是可以通道next指標將k2, k1所在的節點連線起來,這樣就解決了雜湊的衝突問題
4, 建立公共溢位區:
這種方法的基本思想是:將雜湊表分為基本表和溢位表兩部分,凡是和基本表發生衝突的元素,一律填入溢位表
22. hashCode() 與 equals() 生成演算法、方法怎麼重寫
參考地址:blog.csdn.net/neosmith/ar…
結語
好了,今天的分享就到這裡了,希望能夠幫助到需要面試的道友順利渡劫。高深的問題固然要好好回答,但基礎也不能落下,顧此失彼導致薪資被砍相信也不是大家希望看到的,反正我是經歷過,很難受,哈哈。