1. 程式人生 > >程式設計師:為什麼“基礎不牢”成為我薪資被砍的理由?!

程式設計師:為什麼“基礎不牢”成為我薪資被砍的理由?!

前言

我發現有很多程式設計師面試前都是準備地好好的,什麼疑難雜症,未解之謎都是準備得妥妥的,張口就來。反而到了最容易的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的使用等等

詳細解析:https://blog.csdn.net/sinat_29581293/article/details/70214436

10. string、stringbuilder、stringbuffer區別

這三個類之間的區別主要是在兩個方面,即執行速度和執行緒安全這兩方面。

  1. 首先說執行速度,或者說是執行速度,在這方面執行速度快慢為: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 .異常的結構,執行時異常和非執行時異常

大神的解釋:https://blog.csdn.net/qq_27093465/article/details/52268531

14. String a= “abc” String b = “abc” String c = new String(“abc”) String d = “ab” + “c” .他們之間用 == 比較的結果

傳送門:https://blog.csdn.net/qq_36381855/article/details/79686812

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 底層實現原理與執行緒安全問題

大神又來了:https://blog.csdn.net/qq_27093465/article/details/52279473

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() 生成演算法、方法怎麼重寫

參考地址:https://blog.csdn.net/neosmith/article/details/17068365

結語

好了,今天的分享就到這裡了,希望能夠幫助到需要面試的道友順利渡劫。高深的問題固然要好好回答,但基礎也不能落下,顧

此失彼導致薪資被砍相信也不是大家希望看到的,反正我是經歷過,很難受,哈哈。