程序員:爲什麼“基礎不牢”成爲我薪資被砍的理由?!

前言

我發現有很多程序員面試前都是準備地好好的,什麼疑難雜症,未解之謎都是準備得妥妥的,張口就來。反而到了最容易的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> anagrams = new HashMap<>(); 對抗Google的guava.

6.集合類可以像js中的數組一樣賦值和引用了。

List list = ["item"];

String item = list[0];

Set set = {"item"};

Map 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區別

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

首先說運行速度,或者說是執行速度,在這方面運行速度快慢爲: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的存儲規則:

優先使用數組存儲, 如果出現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. 擴容方式

3. 關於null值

4. 線程安全


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

結語

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

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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章