500+ 精選 Java 面試題大放送

凡事預則立不預則廢,無論你是近期打算跳槽,還是過完年準備跳槽,我想此刻開始準備面試,無疑是最明智的選擇。信息過載的今天,想要找一份靠譜的高頻面試題和權威的答案非常不容易,本文為你彙總了大量的乾貨面試資料,下面一起來看吧。

500+ 精选 Java 面试题大放送

Java程序是怎麼執行的?

我們日常的工作中都使用開發工具(IntelliJ IDEA 或 Eclipse 等)可以很方便的調試程序,或者是通過打包工具把項目打包成 jar 包或者 war 包,放入 Tomcat 等 Web 容器中就可以正常運行了,但你有沒有想過 Java 程序內部是如何執行的?其實不論是在開發工具中運行還是在 Tomcat 中運行,Java 程序的執行流程基本都是相同的,它的執行流程如下:

  • 先把 Java 代碼編譯成字節碼,也就是把 .java 類型的文件編譯成 .class 類型的文件。這個過程的大致執行流程:Java 源代碼 -> 詞法分析器 -> 語法分析器 -> 語義分析器 -> 字符碼生成器 -> 最終生成字節碼,其中任何一個節點執行失敗就會造成編譯失敗;

  • 把 class 文件放置到 Java 虛擬機,這個虛擬機通常指的是 Oracle 官方自帶的 Hotspot JVM;

  • Java 虛擬機使用類加載器(Class Loader)裝載 class 文件;

  • 類加載完成之後,會進行字節碼效驗,字節碼效驗通過之後 JVM 解釋器會把字節碼翻譯成機器碼交由操作系統執行。但不是所有代碼都是解釋執行的,JVM 對此做了優化,比如,以 Hotspot 虛擬機來說,它本身提供了 JIT(Just In Time)也就是我們通常所說的動態編譯器,它能夠在運行時將熱點代碼編譯為機器碼,這個時候字節碼就變成了編譯執行。

Java 程序執行流程圖如下:

500+ 精选 Java 面试题大放送
500+ 精选 Java 面试题大放送

Java 虛擬機是如何判定熱點代碼的?

Java 虛擬機判定熱點代碼的方式有兩種:

  • 基於採樣的熱點判定:主要是虛擬機會週期性的檢查各個線程的棧頂,若某個或某些方法經常出現在棧頂,那這個方法就是“熱點方法”。這種判定方式的優點是實現簡單;缺點是很難精確一個方法的熱度,容易受到線程阻塞或外界因素的影響。

  • 基於計數器的熱點判定:主要就是虛擬機給每一個方法甚至代碼塊建立了一個計數器,統計方法的執行次數,超過一定的閥值則標記為此方法為熱點方法。

Hotspot 虛擬機使用的基於計數器的熱點探測方法。它使用了兩類計數器:方法調用計數器和回邊計數器,當到達一定的閥值是就會觸發 JIT 編譯。

方法調用計數器:在 client 模式下的閥值是 1500 次,Server 是 10000 次,可以通過虛擬機參數:-XX:CompileThreshold=N 對其進行設置。但是JVM還存在熱度衰減,時間段內調用方法的次數較少,計數器就減小。回邊計數器:主要統計的是方法中循環體代碼執行的次數。

500+ 精选 Java 面试题大放送

以下 Integer 代碼輸出的結果是?

  1. <code>Integer age = 10;/<code>

  2. <code>Integer age2 = 10;/<code>

  3. <code>Integer age3 = 133;/<code>

  4. <code>Integer age4 = 133;/<code>

  5. <code>System.out.println((age == age2) + ","+ (age3 == age4));/<code>

答:true,false題目解析:此道題目考察的是,面試者對於基礎類型高頻區緩存的掌握,因為 Integer 的高頻區的取值是 -128-127,所以在這個區間的值會複用已有的緩存,對比的結果自然是 true,false 。

500+ 精选 Java 面试题大放送

以下 StringBuffer 傳值修改後的執行結果是什麼?

  1. <code>publicstaticvoid main(String[] args) {/<code>

  2. <code>StringBuffer sf = newStringBuffer("hi");/<code>

  3. <code>changeStr(sf);/<code>

  4. <code>System.out.println(sf);/<code>

  5. <code>}/<code>

  6. <code>publicstaticvoid changeStr(StringBuffer sf){/<code>

  7. <code>sf.append("laowang");/<code>

  8. <code>}/<code>

答:<code>hilaowang/<code>題目解析:String 為不可變類型,在方法內對 String 修改的時候,相當修改傳遞過來的是一個 String 副本,所以 String 本身的值是不會被修改的,而 StringBuffer 為可變類型,傳遞過來的參數相當於對象本身,所以打印的結果就為<code>hilaowang/<code>。

500+ 精选 Java 面试题大放送

以下數組比較的結果分別是什麼?

  1. <code>String strArr = {"dog","cat","pig","bird"};/<code>

  2. <code>String strArr2 = {"dog","cat"

    ,"pig","bird"};/<code>

  3. <code>System.out.println(Arrays.equals(strArr, strArr2));/<code>

  4. <code>System.out.println(strArr.equals(strArr2));/<code>

  5. <code>System.out.println(strArr == strArr2);/<code>

答:<code>true/<code>、<code>false/<code>、<code>false/<code>。題目解析:strArr == strArr2 為引用比較,因此結果一定是 false,而數組本身的比較也就是 strArr.equals(strArr2) 為 false 的原因是因為數組沒有重寫 equals 方法,因此也是引用比較。數組 equals 源碼實現如下:

  1. <code>publicboolean equals(Object obj) {/<code>

  2. <code>return(this== obj);/<code>

  3. <code>}/<code>

而 Arrays.equals 的結果之所以是 true 是因為 Arrays.equals 重寫了 equals 方法。源代碼實現如下:

  1. <code>publicstaticboolean equals(Object[] a, Object[] a2) {/<code>

  2. <code>if(a==a2)/<code>

  3. <code>returntrue;/<code>

  4. <code>if(a==|| a2==)/<code>

  5. <code>returnfalse;/<code>

  6. <code>int length = a.length;/<code>

  7. <code>if(a2.length != length)/<code>

  8. <code>returnfalse;/<code>

  9. <code>for(int i=0; i<length>/<code>

  10. <code>Object o1 = a[i];/<code>

  11. <code>Object o2 = a2[i];/<code>

  12. <code>if(!(o1==? o2==: o1.equals(o2)))/<code>

  13. <code>returnfalse;/<code>

  14. <code>}/<code>

  15. <code>returntrue;/<code>

  16. <code>}/<code>

500+ 精选 Java 面试题大放送

常用的序列化方式都有哪些?

答:常用的序列化方式有以下三種:1) Java 原生序列化方式請參考以下代碼:

  1. <code>// 序列化和反序列化/<code>

  2. <code>classSerializableTest{/<code>

  3. <code>publicstaticvoid main(String[] args) throwsIOException, ClassNotFoundException{/<code>

  4. <code>// 對象賦值/<code>

  5. <code>User user = newUser;/<code>

  6. <code>user.setName("老王");/<code>

  7. <code>user.setAge(30);/<code>

  8. <code>System.out.println(user);/<code>

  9. <code>// 創建輸出流(序列化內容到磁盤)/<code>

  10. <code>ObjectOutputStream oos = newObjectOutputStream(newFileOutputStream("test.out"));/<code>

  11. <code>// 序列化對象/<code>

  12. <code>oos.writeObject(user);/<code>

  13. <code>oos.flush;/<code>

  14. <code>oos.close;/<code>

  15. <code>// 創建輸入流(從磁盤反序列化)/<code>

  16. <code>ObjectInputStream ois = newObjectInputStream(newFileInputStream("test.out"));/<code>

  17. <code>// 反序列化/<code>

  18. <code>User user2 = (User) ois.readObject;/<code>

  19. <code>ois.close;/<code>

  20. <code>System.out.println(user2);/<code>

  21. <code>}/<code>

  22. <code>}/<code>

  23. <code>classUserimplementsSerializable{/<code>

  24. <code>privatestaticfinallong serialVersionUID = 5132320539584511249L;/<code>

  25. <code>privateString name;/<code>

  26. <code>privateint age;/<code>

  27. <code>@Override/<code>

  28. <code>publicString toString {/<code>

  29. <code>return"{name:"+ name +",age:"+ age +"}";/<code>

  30. <code>}/<code>

  31. <code>publicString getName {/<code>

  32. <code>return name;/<code>

  33. <code>}/<code>

  34. <code>publicvoid setName(String name) {/<code>

  35. <code>this.name = name;/<code>

  36. <code>}/<code>

  37. <code>publicint getAge {/<code>

  38. <code>return age;/<code>

  39. <code>}/<code>

  40. <code>publicvoid setAge(int age) {/<code>

  41. <code>this.age = age;/<code>

  42. <code>}/<code>

  43. <code>}/<code>

2) JSON 格式,可使用 fastjson 或 GSONJSON 是一種輕量級的數據格式,JSON 序列化的優點是可讀性比較高,方便調試。我們本篇以 fastjson 的序列化為例,請參考以下代碼:

  1. <code>// 序列化和反序列化/<code>

  2. <code>classSerializableTest{/<code>

  3. <code>publicstaticvoid main(String[] args) throwsIOException, ClassNotFoundException{/<code>

  4. <code>// 對象賦值/<code>

  5. <code>User user = newUser;/<code>

  6. <code>user.setName("老王");/<code>

  7. <code>user.setAge(30

    );/<code>

  8. <code>System.out.println(user);/<code>


  9. <code>String jsonSerialize = JSON.toJSONString(user);/<code>

  10. <code>User user3 = (User) JSON.parseObject(jsonSerialize, User.class);/<code>

  11. <code>System.out.println(user3);/<code>

  12. <code>}/<code>

  13. <code>}/<code>

  14. <code>classUserimplementsSerializable{/<code>

  15. <code>privatestaticfinallong serialVersionUID = 5132320539584511249L;/<code>

  16. <code>privateString name;/<code>

  17. <code>privateint age;/<code>

  18. <code>@Override/<code>

  19. <code>publicString toString {/<code>

  20. <code>return"{name:"+ name +",age:"+ age +"}";/<code>

  21. <code>}/<code>

  22. <code>publicString getName {/<code>

  23. <code>return name;/<code>

  24. <code>}/<code>

  25. <code>publicvoid setName(String name) {/<code>

  26. <code>this.name = name;/<code>

  27. <code>}/<code>

  28. <code>publicint getAge {/<code>

  29. <code>return age;/<code>

  30. <code>}/<code>

  31. <code>publicvoid setAge(int age) {/<code>

  32. <code>this.age = age;/<code>

  33. <code>}/<code>

  34. <code>}/<code>

3) Hessian 方式序列化:Hessian 序列化的優點是可以跨編程語言,比 Java 原生的序列化和反序列化效率高。請參考以下示例代碼:

  1. <code>// 序列化和反序列化/<code>

  2. <code>classSerializableTest{/<code>

  3. <code>publicstaticvoid main(String[] args) throwsIOException, ClassNotFoundException{/<code>

  4. <code>// 序列化/<code>

  5. <code>ByteArrayOutputStream bo = newByteArrayOutputStream;/<code>

  6. <code>HessianOutput hessianOutput = newHessianOutput(bo);/<code>

  7. <code>hessianOutput.writeObject(user);/<code>

  8. <code>byte hessianBytes = bo.toByteArray;/<code>

  9. <code>// 反序列化/<code>

  10. <code>ByteArrayInputStream bi = newByteArrayInputStream(hessianBytes);/<code>

  11. <code>HessianInput hessianInput = newHessianInput(bi);/<code>

  12. <code>User user4 = (User) hessianInput.readObject;/<code>

  13. <code>System.out.println(user4);/<code>

  14. <code>}/<code>

  15. <code>}/<code>

  16. <code>classUserimplementsSerializable{/<code>

  17. <code>privatestaticfinallong serialVersionUID = 5132320539584511249L;/<code>

  18. <code>privateString name;/<code>

  19. <code>privateint age;/<code>

  20. <code>@Override/<code>

  21. <code>publicString toString {/<code>

  22. <code>return"{name:"+ name +",age:"+ age +"}";/<code>

  23. <code>}/<code>

  24. <code>publicString getName {/<code>

  25. <code>return name;/<code>

  26. <code>}/<code>

  27. <code>publicvoid setName(String name) {/<code>

  28. <code>this.name = name;/<code>

  29. <code>}/<code>

  30. <code>publicint getAge {/<code>

  31. <code>return age;/<code>

  32. <code>}/<code>

  33. <code>publicvoid setAge(int age) {/<code>

  34. <code>this.age = age;/<code>

  35. <code>}/<code>

  36. <code>}/<code>

500+ 精选 Java 面试题大放送

有哪些方法可以解決哈希衝突?

答:哈希衝突的常用解決方案有以下 4 種:

  • 開放定址法:當關鍵字的哈希地址 p=H(key)出現衝突時,以 p 為基礎,產生另一個哈希地址 p1,如果 p1 仍然衝突,再以 p 為基礎,產生另一個哈希地址 p2,循環此過程直到找出一個不衝突的哈希地址,將相應元素存入其中;

  • 再哈希法:這種方法是同時構造多個不同的哈希函數,當哈希地址 Hi=RH1(key)發生衝突時,再計算 Hi=RH2(key),循環此過程直到找到一個不衝突的哈希地址,這種方法唯一的缺點就是增加了計算時間;

  • 鏈地址法:這種方法的基本思想是將所有哈希地址為 i 的元素構成一個稱為同義詞鏈的單鏈表,並將單鏈表的頭指針存在哈希表的第 i 個單元中,因而查找、插入和刪除主要在同義詞鏈中進行。鏈地址法適用於經常進行插入和刪除的情況;

  • 建立公共溢出區:將哈希表分為基本表和溢出表兩部分,凡是和基本表發生衝突的元素,一律填入溢出表。

500+ 精选 Java 面试题大放送

JVM 內存佈局是怎樣的?

答:不同虛擬機實現可能略微有所不同,但都會遵從 Java 虛擬機規範,Java 8 虛擬機規範規定,Java 虛擬機所管理的內存將會包括以下幾個區域:

  • 程序計數器(Program Counter Register)

  • Java 虛擬機棧(Java Virtual Machine Stacks)

  • 本地方法棧(Native Method Stack)

  • Java 堆(Java Heap)

  • 方法區(Methed Area)

以上面試這幾個知識點均來自《Java面試全解析:核心知識點與典型面試題》這門專欄

掃碼查看專欄詳情

500+ 精选 Java 面试题大放送

這門專欄幫助了很多人完成面試準備,來看看讀者評價:

500+ 精选 Java 面试题大放送500+ 精选 Java 面试题大放送

通過這門課拿到 Offer 的同學:

500+ 精选 Java 面试题大放送500+ 精选 Java 面试题大放送

這門課所包含的知識點:

500+ 精选 Java 面试题大放送

上市公司技術研發經理,資深面試官,阿里雲社區認證專家。前奇虎 360 員工,有著 10 餘年的編程工作經驗,目前主要負責新員工技術面試和平臺架構制訂的相關事宜。在接下來兩個多月的時間裡,讓我們一起學習 Java 技術核心和麵試要點,一起構建一個完整的 Java 認知體系。

你將獲得

1. 收穫 Java 技術棧的核心知識點

這個課程幾乎涵蓋了 Java 技術棧的大部分內容,不止對於面試,在日常的工作中也可以發揮很大的作用。

2. 500 多道實用、權威、高頻 Java 面試題詳解

這 500 多道面試題,都是目前主流企業使用最高頻的面試題庫,也都是 Java 版本升級之後,重新整理歸納的最新答案,會讓面試者少走很多不必要的彎路。同時每道題都做到了詳盡的描述,以確保每個階段的讀者都能看得懂,面試題中的專業短語也都確保提供了必要的介紹,部分難懂的題目也提供了題目解析和示例代碼。

3. 理解技術背後的實現原理

死記硬背的內容通常會隨著時間的推移很快就忘記,所以在學習一門技術的時候,一定要了解其背後的實現原理,從而構建邏輯上的因果關係,這樣才能夠記的更久。這門課程會深入淺出地對技術背後的原理進行深入的分析,讓讀者“知其然,並知其所以然”。


分享到:


相關文章: