2w字長文!手擼一套 Java 基礎面試題

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Java 基礎篇"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"Java 有哪些特點"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"併發性的"}]},{"type":"text","text":": 你可以在其中執行許多語句,而不必一次執行它"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"面向對象的"}]},{"type":"text","text":":基於類和麪向對象的編程語言。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"獨立性的"}]},{"type":"text","text":": 支持"},{"type":"text","marks":[{"type":"strong"}],"text":"一次編寫,到處運行"},{"type":"text","text":"的獨立編程語言,即編譯後的代碼可以在支持 Java 的所有平臺上運行。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/c1/c1a4b23312d70aa579c77cff4974cfc8.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"Java 的特性"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Java 的特性有如下這幾點"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"簡單"}]},{"type":"text","text":",Java 會讓你的工作變得更加輕鬆,使你把關注點放在主要業務邏輯上,而不必關心指針、運算符重載、內存回收等與主要業務無關的功能。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"便攜性"}]},{"type":"text","text":",Java 是平臺無關性的,這意味着在一個平臺上編寫的任何應用程序都可以輕鬆移植到另一個平臺上。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"安全性"}]},{"type":"text","text":", 編譯後會將所有的代碼轉換爲字節碼,人類無法讀取。它使開發無病毒,無篡改的系統/應用成爲可能。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"動態性"}]},{"type":"text","text":",它具有適應不斷變化的環境的能力,它能夠支持動態內存分配,從而減少了內存浪費,提高了應用程序的性能。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"分佈式"}]},{"type":"text","text":",Java 提供的功能有助於創建分佈式應用。使用"},{"type":"codeinline","content":[{"type":"text","text":"遠程方法調用(RMI)"}]},{"type":"text","text":",程序可以通過網絡調用另一個程序的方法並獲取輸出。您可以通過從互聯網上的任何計算機上調用方法來訪問文件。這是革命性的一個特點,對於當今的互聯網來說太重要了。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"健壯性"}]},{"type":"text","text":",Java 有強大的內存管理功能,在編譯和運行時檢查代碼,它有助於消除錯誤。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"高性能"}]},{"type":"text","text":",Java 最黑的科技就是字節碼編程,Java 代碼編譯成的字節碼可以輕鬆轉換爲本地機器代碼。通過 JIT 即時編譯器來實現高性能。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"解釋性"}]},{"type":"text","text":",Java 被編譯成字節碼,由 Java 運行時環境解釋。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"多線程性"}]},{"type":"text","text":",Java支持多個執行線程(也稱爲輕量級進程),包括一組同步原語。這使得使用線程編程更加容易,Java 通過管程模型來實現線程安全性。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"描述一下值傳遞和引用傳遞的區別"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"要想真正理解的話,可以參考這篇文章 : https://www.zhihu.com/question/31203609"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"簡單理解的話就是"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"值傳遞"}]},{"type":"text","text":"是指在調用函數時將實際參數複製一份到函數中,這樣的話如果函數對其傳遞過來的形式參數進行修改,將不會影響到實際參數"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"引用傳遞"}]},{"type":"text","text":" 是指在調用函數時將對象的地址直接傳遞到函數中,如果在對形式參數進行修改,將影響到實際參數的值。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"== 和 equals 區別是什麼"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"=="}]},{"type":"text","text":" 是 Java 中一種操作符,它有兩種比較方式"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對於"},{"type":"codeinline","content":[{"type":"text","text":"基本數據類型"}]},{"type":"text","text":"來說, == 判斷的是兩邊的"},{"type":"codeinline","content":[{"type":"text","text":"值"}]},{"type":"text","text":"是否相等"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class DoubleCompareAndEquals {\n\n Person person1 = new Person(24,\"boy\");\n Person person2 = new Person(24,\"girl\");\n int c = 10;\n\n private void doubleCompare(){\n\n int a = 10;\n int b = 10;\n\n System.out.println(a == b);\n System.out.println(a == c);\n System.out.println(person1.getId() == person2.getId());\n\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對於"},{"type":"codeinline","content":[{"type":"text","text":"引用類型"}]},{"type":"text","text":"來說, == 判斷的是兩邊的"},{"type":"codeinline","content":[{"type":"text","text":"引用"}]},{"type":"text","text":"是否相等,也就是判斷兩個對象是否指向了同一塊內存區域。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"private void equals(){\n\n System.out.println(person1.getName().equals(person2.getName()));\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"equals"}]},{"type":"text","text":" 是 Java 中所有對象的父類,即 "},{"type":"codeinline","content":[{"type":"text","text":"Object"}]},{"type":"text","text":" 類定義的一個方法。它只能比較對象,它表示的是引用雙方的值是否相等。所以記住,並不是說 == 比較的就是引用是否相等,equals 比較的就是值,這需要區分來說的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"equals 用作對象之間的比較具有如下特性"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"自反性"}]},{"type":"text","text":":對於任何非空引用 x 來說,x.equals(x) 應該返回 true。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"對稱性"}]},{"type":"text","text":":對於任何非空引用 x 和 y 來說,若x.equals(y)爲 true,則y.equals(x)也爲 true。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"傳遞性"}]},{"type":"text","text":":對於任何非空引用的值來說,有三個值,x、y 和 z,如果x.equals(y) 返回true,y.equals(z) 返回true,那麼x.equals(z) 也應該返回true。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"一致性"}]},{"type":"text","text":":對於任何非空引用 x 和 y 來說,如果 x.equals(y) 相等的話,那麼它們必須始終相等。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"非空性"}]},{"type":"text","text":":對於任何非空引用的值 x 來說,x.equals(null) 必須返回 false。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"String 中的 equals 是如何重寫的"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"String 代表的是 Java 中的"},{"type":"codeinline","content":[{"type":"text","text":"字符串"}]},{"type":"text","text":",String 類比較特殊,它整個類都是被 "},{"type":"codeinline","content":[{"type":"text","text":"final"}]},{"type":"text","text":" 修飾的,也就是說,String 不能被任何類繼承,任何 "},{"type":"codeinline","content":[{"type":"text","text":"修改"}]},{"type":"text","text":" String 字符串的方法都是創建了一個新的字符串。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"equals 方法是 Object 類定義的方法,Object 是所有類的父類,當然也包括 String,String 重寫了 "},{"type":"codeinline","content":[{"type":"text","text":"equals"}]},{"type":"text","text":" 方法,下面我們來看看是怎麼重寫的"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/7a/7a359f2714dd099f167e79b85723861c.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先會判斷要比較的兩個字符串它們的"},{"type":"codeinline","content":[{"type":"text","text":"引用"}]},{"type":"text","text":"是否相等。如果引用相等的話,直接返回 true ,不相等的話繼續下面的判斷"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然後再判斷被比較的對象是否是 String 的實例,如果不是的話直接返回 false,如果是的話,再比較兩個字符串的長度是否相等,如果長度不想等的話也就沒有比較的必要了;長度如果相同,會比較字符串中的每個 "},{"type":"codeinline","content":[{"type":"text","text":"字符"}]},{"type":"text","text":" 是否相等,一旦有一個字符不相等,就會直接返回 false。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下面是它的流程圖"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/50/5014aa8cc1b7531c2889e6a2fbef1a54.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏再提示一下,你可能有疑惑什麼時候是 "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"if (this == anObject) {\n return true;\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這個判斷語句如何才能返回 true?因爲都是字符串啊,字符串比較的不都是堆空間嗎,猛然一看發現好像永遠也不會走,但是你忘記了 "},{"type":"codeinline","content":[{"type":"text","text":"String.intern()"}]},{"type":"text","text":" 方法,它表示的概念在不同的 JDK 版本有不同的區分"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在 JDK1.7 及以後調用 intern 方法是判斷運行時常量池中是否有指定的字符串,如果沒有的話,就把字符串添加到常量池中,並返回常量池中的對象。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"驗證過程如下"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"private void StringOverrideEquals(){\n\n String s1 = \"aaa\";\n String s2 = \"aa\" + new String(\"a\");\n String s3 = new String(\"aaa\");\n\n System.out.println(s1.intern().equals(s1));\n System.out.println(s1.intern().equals(s2));\n System.out.println(s3.intern().equals(s1));\n\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先 s1.intern.equals(s1) 這個無論如何都返回 true,因爲 s1 字符串創建出來就已經在常量池中存在了。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然後第二條語句返回 false,因爲 s1 返回的是常量池中的對象,而 s2 返回的是堆中的對象"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第三條語句 s3.intern.equals(s1),返回 true ,因爲 s3 對象雖然在堆中創建了一個對象,但是 s3 中的 \"aaa\" 返回的是常量池中的對象。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/5e/5e9e7b609fb2ed56976d4719f4b137eb.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"爲什麼重寫 equals 方法必須重寫 hashcode 方法"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"equals 方法和 hashCode 都是 Object 中定義的方法,它們經常被一起重寫。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"equals 方法是用來比較對象大小是否相等的方法,hashcode 方法是用來判斷每個對象 hash 值的一種方法。如果只重寫 equals 方法而不重寫 hashcode 方法,很可能會造成兩個不同的對象,它們的 hashcode 也相等,造成衝突。比如 "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"String str1 = \"通話\";\nString str2 = \"重地\";"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"它們兩個的 hashcode 相等,但是 equals 可不相等。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們來看一下 hashCode 官方的定義"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/88/881ecf9bd308d218fd9e76cc8af8b91f.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"總結起來就是"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果在 Java 運行期間對同一個對象調用 hashCode 方法後,無論調用多少次,都應該返回相同的 hashCode,但是在不同的 Java 程序中,執行 hashCode 方法返回的值可能不一致。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果兩個對象的 equals 相等,那麼 hashCode 必須相同"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果兩個對象 equals 不相等,那麼 hashCode 也有可能相同,所以需要重寫 hashCode 方法,因爲你不知道 hashCode 的底層構造(反正我是不知道,有大牛可以傳授傳授),所以你需要重寫 hashCode 方法,來爲不同的對象生成不同的 hashCode 值,這樣能夠提高不同對象的訪問速度。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"hashCode 通常是將地址轉換爲整數來實現的。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"String s1 = new String(\"abc\") 在內存中創建了幾個對象"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一個或者兩個,String s1 是聲明瞭一個 String 類型的 s1 變量,它不是對象。使用 "},{"type":"codeinline","content":[{"type":"text","text":"new"}]},{"type":"text","text":" 關鍵字會在堆中創建一個對象,另外一個對象是 "},{"type":"codeinline","content":[{"type":"text","text":"abc"}]},{"type":"text","text":" ,它會在常量池中創建,所以一共創建了兩個對象;如果 abc 在常量池中已經存在的話,那麼就會創建一個對象。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"詳細請翻閱筆者的另外一篇文章 "},{"type":"link","attrs":{"href":"https://mp.weixin.qq.com/s?_biz=MzI0ODk2NDIyMQ==&mid=2247484794&idx=1&sn=22efd808fa5a9e68cacabd4b6e08fdc3&chksm=e999f068deee797eef9b46b160c06afa4d50e03b3626d1ae1aad05ddc37ec9001c4514264e0f&token=1065926980&lang=zhCN#rd","title":""},"content":[{"type":"text","text":"一篇與衆不同的 String、StringBuffer、StringBuilde 詳解"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"String 爲什麼是不可變的、jdk 源碼中的 String 如何定義的、爲什麼這麼設計。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先了解一下什麼是"},{"type":"codeinline","content":[{"type":"text","text":"不可變對象"}]},{"type":"text","text":",不可變對象就是一經創建後,其對象的內部狀態不能被修改,啥意思呢?也就是說不可變對象需要遵守下面幾條原則"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不可變對象的內部屬性都是 final 的"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不可變對象的內部屬性都是 private 的"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不可變對象不能提供任何可以修改內部狀態的方法、setter 方法也不行"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不可變對象不能被繼承和擴展"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"與其說問 String 爲什麼是不可變的,不如說如何把 String 設計成不可變的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"String 類是一種對象,它是獨立於 Java 基本數據類型而存在的,String 你可以把它理解爲字符串的集合,String 被設計爲 final 的,表示 String 對象一經創建後,它的值就不能再被修改,任何對 String 值進行修改的方法就是重新創建一個字符串。String 對象創建後會存在於運行時常量池中,運行時常量池是屬於方法區的一部分,JDK1.7 後把它移到了堆中。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不可變對象不是真的不可變,可以通過"},{"type":"codeinline","content":[{"type":"text","text":"反射"}]},{"type":"text","text":"來對其內部的屬性和值進行修改,不過一般我們不這樣做。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"static 關鍵字是幹什麼用的?談談你的理解"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"static 是 Java 中非常重要的關鍵字,static 表示的概念是 "},{"type":"codeinline","content":[{"type":"text","text":"靜態的"}]},{"type":"text","text":",在 Java 中,static 主要用來"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"修飾變量,static 修飾的變量稱爲"},{"type":"codeinline","content":[{"type":"text","text":"靜態變量"}]},{"type":"text","text":"、也稱爲"},{"type":"codeinline","content":[{"type":"text","text":"類變量"}]},{"type":"text","text":",類變量屬於類所有,對於不同的類來說,static 變量只有一份,static 修飾的變量位於方法區中;static 修飾的變量能夠直接通過 "},{"type":"text","marks":[{"type":"strong"}],"text":"類名.變量名"},{"type":"text","text":" 來進行訪問,不用通過實例化類再進行使用。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"修飾方法,static 修飾的方法被稱爲"},{"type":"codeinline","content":[{"type":"text","text":"靜態方法"}]},{"type":"text","text":",靜態方法能夠直接通過 "},{"type":"text","marks":[{"type":"strong"}],"text":"類名.方法名"},{"type":"text","text":" 來使用,在靜態方法內部不能使用非靜態屬性和方法"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"static 可以修飾代碼塊,主要分爲兩種,一種直接定義在類中,使用 "},{"type":"codeinline","content":[{"type":"text","text":"static{}"}]},{"type":"text","text":",這種被稱爲"},{"type":"codeinline","content":[{"type":"text","text":"靜態代碼塊"}]},{"type":"text","text":",一種是在類中定義"},{"type":"codeinline","content":[{"type":"text","text":"靜態內部類"}]},{"type":"text","text":",使用 "},{"type":"codeinline","content":[{"type":"text","text":"static class xxx"}]},{"type":"text","text":" 來進行定義。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"static 可以用於靜態導包,通過使用 "},{"type":"codeinline","content":[{"type":"text","text":"import static xxx"}]},{"type":"text","text":" 來實現,這種方式一般不推薦使用"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"static 可以和單例模式一起使用,通過雙重檢查鎖來實現線程安全的單例模式。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"詳情請參考這篇文章 "},{"type":"link","attrs":{"href":"https://mp.weixin.qq.com/s?_biz=MzI0ODk2NDIyMQ==&mid=2247484455&idx=1&sn=582d5d2722dab28a36b6c7bc3f39d3fb&chksm=e999f135deee7823226d4da1e8367168a3d0ec6e66c9a589843233b7e801c416d2e535b383be&token=1154740235&lang=zhCN#rd","title":""},"content":[{"type":"text","text":"一篇 static 還能難得住我?"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"final 關鍵字是幹什麼用的?談談你的理解"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"final 是 Java 中的關鍵字,它表示的意思是 "},{"type":"codeinline","content":[{"type":"text","text":"不可變的"}]},{"type":"text","text":",在 Java 中,final 主要用來"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"修飾類,final 修飾的類不能被繼承,不能被繼承的意思就是不能使用 "},{"type":"codeinline","content":[{"type":"text","text":"extends"}]},{"type":"text","text":" 來繼承被 final 修飾的類。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"修飾變量,final 修飾的變量不能被改寫,不能被改寫的意思有兩種,對於基本數據類型來說,final 修飾的變量,其值不能被改變,final 修飾的對象,對象的引用不能被改變,但是對象內部的屬性可以被修改。final 修飾的變量在某種程度上起到了"},{"type":"codeinline","content":[{"type":"text","text":"不可變"}]},{"type":"text","text":"的效果,所以,可以用來保護只讀數據,尤其是在併發編程中,因爲明確的不能再爲 final 變量進行賦值,有利於減少額外的同步開銷。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"修飾方法,final 修飾的方法不能被重寫。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"final 修飾符和 Java 程序性能優化沒有必然聯繫"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"抽象類和接口的區別是什麼"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"抽象類和接口都是 Java 中的關鍵字,抽象類和接口中都允許進行方法的定義,而不用具體的方法實現。抽象類和接口都允許被繼承,它們廣泛的應用於 JDK 和框架的源碼中,來實現多態和不同的設計模式。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不同點在於"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"抽象級別不同"}]},{"type":"text","text":":類、抽象類、接口其實是三種不同的抽象級別,抽象程度依次是 接口 > 抽象類 > 類。在接口中,只允許進行方法的定義,不允許有方法的實現,抽象類中可以進行方法的定義和實現;而類中只允許進行方法的實現,我說的方法的定義是不允許在方法後面出現 "},{"type":"codeinline","content":[{"type":"text","text":"{}"}]}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"使用的關鍵字不同"}]},{"type":"text","text":":類使用 "},{"type":"codeinline","content":[{"type":"text","text":"class"}]},{"type":"text","text":" 來表示;抽象類使用 "},{"type":"codeinline","content":[{"type":"text","text":"abstract class"}]},{"type":"text","text":" 來表示;接口使用 "},{"type":"codeinline","content":[{"type":"text","text":"interface"}]},{"type":"text","text":" 來表示"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"變量"}]},{"type":"text","text":":接口中定義的變量只能是公共的靜態常量,抽象類中的變量是普通變量。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"重寫和重載的區別"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在 Java 中,重寫和重載都是對同一方法的不同表現形式,下面我們針對重寫和重載做一下簡單的區分"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"子父級關係不同"}]},{"type":"text","text":",重寫是針對子級和父級的不同表現形式,而重載是在同一類中的不同表現形式;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"概念不同"}]},{"type":"text","text":",子類重寫父類的方法一般使用 "},{"type":"codeinline","content":[{"type":"text","text":"@override"}]},{"type":"text","text":" 來表示;重寫後的方法其方法的聲明和參數類型、順序必須要與父類完全一致;重載是針對同一類中概念,它要求重載的方法必須滿足下面任何一個要求:方法參數的順序,參數的個數,參數的類型任意一個保持不同即可。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"byte的取值範圍是多少,怎麼計算出來的"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"byte 的取值範圍是 -128 -> 127 之間,一共是 256 位。一個 byte 類型在計算機中佔據一個字節,那麼就是 8 bit,所以最大就是 2^8 = 1111 1111。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Java 中用"},{"type":"codeinline","content":[{"type":"text","text":"補碼"}]},{"type":"text","text":"來表示二進制數,補碼的最高位是符號位,最高位用 0 表示正數,最高位 1 表示負數,正數的補碼就是其"},{"type":"codeinline","content":[{"type":"text","text":"本身"}]},{"type":"text","text":",由於最高位是符號位,所以正數表示的就是 0111 1111 ,也就是 127。最大負數就是 1111 1111,這其中會涉及到兩個 0 ,一個 +0 ,一個 -0 ,+0 歸爲正數,也就是 0 ,-0 歸爲負數,也就是 -128,所以 byte 的範圍就是 -128 - 127。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"HashMap 和 HashTable 的區別"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"相同點"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"HashMap 和 HashTable 都是基於哈希表實現的,其內部每個元素都是 "},{"type":"codeinline","content":[{"type":"text","text":"key-value"}]},{"type":"text","text":" 鍵值對,HashMap 和 HashTable 都實現了 Map、Cloneable、Serializable 接口。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"不同點"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"父類不同:HashMap 繼承了 "},{"type":"codeinline","content":[{"type":"text","text":"AbstractMap"}]},{"type":"text","text":" 類,而 HashTable 繼承了 "},{"type":"codeinline","content":[{"type":"text","text":"Dictionary"}]},{"type":"text","text":" 類"}]}]}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/59/59bc2a6168524f20c30246df1c4a7447.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"空值不同:HashMap 允許空的 key 和 value 值,HashTable 不允許空的 key 和 value 值。HashMap 會把 Null key 當做普通的 key 對待。不允許 null key 重複。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/ee/ee4c83a1173c4f43b82a81c6158b7855.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"線程安全性:HashMap 不是線程安全的,如果多個外部操作同時修改 HashMap 的數據結構比如 add 或者是 delete,必須進行同步操作,僅僅對 key 或者 value 的修改不是改變數據結構的操作。可以選擇構造線程安全的 Map 比如 "},{"type":"codeinline","content":[{"type":"text","text":"Collections.synchronizedMap"}]},{"type":"text","text":" 或者是 "},{"type":"codeinline","content":[{"type":"text","text":"ConcurrentHashMap"}]},{"type":"text","text":"。而 HashTable 本身就是線程安全的容器。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"性能方面:雖然 HashMap 和 HashTable 都是基於"},{"type":"codeinline","content":[{"type":"text","text":"單鏈表"}]},{"type":"text","text":"的,但是 HashMap 進行 put 或者 get􏱤 操作,可以達到常數時間的性能;而 HashTable 的 put 和 get 操作都是加了 "},{"type":"codeinline","content":[{"type":"text","text":"synchronized"}]},{"type":"text","text":" 鎖的,所以效率很差。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/78/7899aa234b6fd5f6a23d2036b2fbb0c8.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"初始容量不同:HashTable 的初始長度是11,之後每次擴充容量變爲之前的 2n+1(n爲上一次的長度)而 HashMap 的初始長度爲16,之後每次擴充變爲原來的兩倍。創建時,如果給定了容量初始值,那麼HashTable 會直接使用你給定的大小,而 HashMap 會將其擴充爲2的冪次方大小。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"HashMap 和 HashSet 的區別"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"HashSet 繼承於 AbstractSet 接口,實現了 Set、Cloneable,、java.io.Serializable 接口。HashSet 不允許集合中出現重複的值。HashSet 底層其實就是 HashMap,所有對 HashSet 的操作其實就是對 HashMap 的操作。所以 HashSet 也不保證集合的順序,也不是線程安全的容器。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"HashMap 的底層結構"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"JDK1.7 中,HashMap 採用"},{"type":"codeinline","content":[{"type":"text","text":"位桶 + 鏈表"}]},{"type":"text","text":"的實現,即使用"},{"type":"codeinline","content":[{"type":"text","text":"鏈表"}]},{"type":"text","text":"來處理衝突,同一 hash 值的鏈表都存儲在一個數組中。但是當位於一個桶中的元素較多,即 hash 值相等的元素較多時,通過 key 值依次查找的效率較低。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/14/146f18709423df79f54178c5fdcc5eec.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以,與 JDK 1.7 相比,JDK 1.8 在底層結構方面做了一些改變,當每個桶中元素大於 8 的時候,會轉變爲紅黑樹,目的就是優化查詢效率。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/79/7973667e289c09cbd6e206489c8c9aca.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"HashMap 的長度爲什麼是 2 的冪次方"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這道題我想了幾天,之前和羣裏小夥伴們探討每日一題的時候,問他們爲什麼 length%hash == (n - 1) & hash,它們說相等的前提是 length 的長度 2 的冪次方,然後我回了一句難道 length 還能不是 2 的冪次方嗎?其實是我沒有搞懂因果關係,因爲 HashMap 的長度是 2 的冪次方,所以使用餘數來判斷在桶中的下標。如果 length 的長度不是 2 的冪次方,小夥伴們可以舉個例子來試試"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":">例如長度爲 9 時候,3 & (9-1) = 0,2 & (9-1) = 0 ,都在 0 上,碰撞了;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這樣會增大 HashMap 碰撞的機率。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"HashMap 多線程操作導致死循環問題"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"HashMap 不是一個線程安全的容器,在高併發場景下,應該使用 "},{"type":"codeinline","content":[{"type":"text","text":"ConcurrentHashMap"}]},{"type":"text","text":",在多線程場景下使用 HashMap 會造成死循環問題(基於 JDK1.7),出現問題的位置在 "},{"type":"codeinline","content":[{"type":"text","text":"rehash"}]},{"type":"text","text":" 處,也就是"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"do {\n Entry next = e.next; // [] tab; Node p; int n, i;\n // 如果table 爲null 或者沒有爲table分配內存,就resize一次\n if ((tab = table) == null || (n = tab.length) == 0)\n n = (tab = resize()).length;\n // 指定hash值節點爲空則直接插入,這個(n - 1) & hash纔是表中真正的哈希\n if ((p = tab[i = (n - 1) & hash]) == null)\n tab[i] = newNode(hash, key, value, null);\n // 如果不爲空\n else {\n Node e; K k;\n // 計算表中的這個真正的哈希值與要插入的key.hash相比\n if (p.hash == hash &&\n ((k = p.key) == key || (key != null && key.equals(k))))\n e = p;\n // 若不同的話,並且當前節點已經在 TreeNode 上了\n else if (p instanceof TreeNode)\n // 採用紅黑樹存儲方式\n e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value);\n // key.hash 不同並且也不再 TreeNode 上,在鏈表上找到 p.next==null\n else {\n for (int binCount = 0; ; ++binCount) {\n if ((e = p.next) == null) {\n // 在表尾插入\n p.next = newNode(hash, key, value, null);\n // 新增節點後如果節點個數到達閾值,則進入 treeifyBin() 進行再次判斷\n if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st\n treeifyBin(tab, hash);\n break;\n }\n // 如果找到了同hash、key的節點,那麼直接退出循環\n if (e.hash == hash &&\n ((k = e.key) == key || (key != null && key.equals(k))))\n break;\n // 更新 p 指向下一節點\n p = e;\n }\n }\n // map中含有舊值,返回舊值\n if (e != null) { // existing mapping for key\n V oldValue = e.value;\n if (!onlyIfAbsent || oldValue == null)\n e.value = value;\n afterNodeAccess(e);\n return oldValue;\n }\n }\n // map調整次數 + 1\n ++modCount;\n // 鍵值對的數量達到閾值,需要擴容\n if (++size > threshold)\n resize();\n afterNodeInsertion(evict);\n return null;\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"HashMap put 方法的核心就是在 "},{"type":"codeinline","content":[{"type":"text","text":"putval"}]},{"type":"text","text":" 方法,它的插入過程如下"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先會判斷 HashMap 中是否是新構建的,如果是的話會首先進行 resize"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然後判斷需要插入的元素在 HashMap 中是否已經存在(說明出現了碰撞情況),如果不存在,直接生成新的k-v 節點存放,再判斷是否需要擴容。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果要插入的元素已經存在的話,說明發生了衝突,這就會轉換成鏈表或者紅黑樹來解決衝突,首先判斷鏈表中的 hash,key 是否相等,如果相等的話,就用新值替換舊值,如果節點是屬於 TreeNode 類型,會直接在紅黑樹中進行處理,如果 hash ,key 不相等也不屬於 TreeNode 類型,會直接轉換爲鏈表處理,進行鏈表遍歷,如果鏈表的 next 節點是 null,判斷是否轉換爲紅黑樹,如果不轉換的話,在遍歷過程中找到 key 完全相等的節點,則用新節點替換老節點"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"ConcurrentHashMap 底層實現"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ConcurrentHashMap 是線程安全的 Map,它也是高併發場景下的首選數據結構,ConcurrentHashMap 底層是使用"},{"type":"codeinline","content":[{"type":"text","text":"分段鎖"}]},{"type":"text","text":"來實現的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"Integer 緩存池"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Integer 緩存池也就是 "},{"type":"codeinline","content":[{"type":"text","text":"IntegerCache"}]},{"type":"text","text":" ,它是 Integer 的靜態內部類。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/2e/2e23ae06586690ce3bce0810a0d17b26.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"它的默認值用於緩存 -128 - 127 之間的數字,如果有 -128 - 127 之間的數字的話,使用 new Integer 不用創建對象,會直接從緩存池中取,此操作會減少堆中對象的分配,有利於提高程序的運行效率。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"例如創建一個 Integer a = 24,其實是調用 Integer 的 "},{"type":"codeinline","content":[{"type":"text","text":"valueOf"}]},{"type":"text","text":" ,可以通過反編譯得出這個結論"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/57/57e47c3900317ad82bb337c9938a03c8.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然後我們看一下 valueOf 方法"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/78/78b6dc03c326ca2b822b89aa2f150c08.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果在指定緩存池範圍內的話,會直接返回緩存的值而不用創建新的 Integer 對象。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"緩存的大小可以使用 "},{"type":"codeinline","content":[{"type":"text","text":"XX:AutoBoxCacheMax"}]},{"type":"text","text":" 來指定,在 VM 初始化時,"},{"type":"codeinline","content":[{"type":"text","text":"java.lang.Integer.IntegerCache.high"}]},{"type":"text","text":" 屬性會設置和保存在 "},{"type":"codeinline","content":[{"type":"text","text":"sun.misc.VM"}]},{"type":"text","text":" 的私有系統屬性中。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"UTF-8 和 Unicode 的關係"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由於每個國家都有自己獨有的字符編碼,所以Unicode 的發展旨在創建一個新的標準,用來映射當今使用的大多數語言中的字符,這些字符有一些不是必要的,但是對於創建文本來說卻是不可或缺的。Unicode 統一了所有字符的編碼,是一個 Character Set,也就是字符集,字符集只是給所有的字符一個唯一編號,但是卻沒有規定如何存儲,不同的字符其存儲空間不一樣,有的需要一個字節就能存儲,有的則需要2、3、4個字節。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"UTF-8 只是衆多能夠對文本字符進行"},{"type":"codeinline","content":[{"type":"text","text":"解碼"}]},{"type":"text","text":"的一種方式,它是一種變長的方式。UTF-8 代表 8 位一組表示 Unicode 字符的格式,使用 1 - 4 個字節來表示字符。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"U+ 0000 ~ U+ 007F: 0XXXXXXX\nU+ 0080 ~ U+ 07FF: 110XXXXX 10XXXXXX\nU+ 0800 ~ U+ FFFF: 1110XXXX 10XXXXXX 10XXXXXX\nU+10000 ~ U+1FFFF: 11110XXX 10XXXXXX 10XXXXXX 10XXXXXX"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以看到,UTF-8 通過開頭的標誌位位數實現了變長。對於單字節字符,只佔用一個字節,實現了向下兼容 ASCII,並且能和 UTF-32 一樣,包含 Unicode 中的所有字符,又能有效減少存儲傳輸過程中佔用的空間。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"項目爲 UTF-8 環境,char c = '中',是否合法"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以,因爲 Unicode 編碼採用 2 個字節的編碼,UTF-8 是 Unicode 的一種實現,它使用可變長度的字符集進行編碼,char c = '中' 是兩個字節,所以能夠存儲。合法。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"Arrays.asList 獲得的 List 應該注意什麼"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Arrays.asList"}]},{"type":"text","text":" 是 Array 中的一個靜態方法,它能夠實現把數組轉換成爲 List 序列,需要注意下面幾點"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Arrays.asList 轉換完成後的 List 不能再進行結構化的修改,什麼是結構化的修改?就是不能再進行任何 List 元素的增加或者減少的操作。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public static void main(String[] args) {\n Integer[] integer = new Integer[] { 1, 2, 3, 4 };\n List integetList = Arrays.asList(integer);\n integetList.add(5);\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"結果會直接拋出"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"Exception in thread \"main\" java.lang.UnsupportedOperationException"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們看一下源碼就能發現問題"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"// 這是 java.util.Arrays 的內部類,而不是 java.util.ArrayList \nprivate static class ArrayList extends AbstractList\n implements RandomAccess, java.io.Serializable"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"繼承 AbstractList 中對 add、remove、set 方法是直接拋異常的,也就是說如果繼承的子類沒有去重寫這些方法,那麼子類的實例去調用這些方法是會直接拋異常的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下面是AbstractList中方法的定義,我們可以看到具體拋出的異常:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public void add(int index, E element) {\n throw new UnsupportedOperationException();\n}\npublic E remove(int index) {\n throw new UnsupportedOperationException();\n}\npublic E set(int index, E element) {\n throw new UnsupportedOperationException();\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"雖然 set 方法也拋出了一場,但是由於 內部類 ArrayList 重寫了 set 方法,所以支持其可以對元素進行修改。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/79/79fa51e164d5816f43748d9ca07ed36b.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Arrays.asList 不支持基礎類型的轉換"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Java 中的基礎數據類型(byte,short,int,long,float,double,boolean)是不支持使用 Arrays.asList 方法去轉換的"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"Collection 和 Collections 的區別"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Collection 和 Collections 都是位於 "},{"type":"codeinline","content":[{"type":"text","text":"java.util"}]},{"type":"text","text":" 包下的類"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Collection 是集合類的父類,它是一個頂級接口,大部分抽象類比如說 "},{"type":"codeinline","content":[{"type":"text","text":"AbstractList"}]},{"type":"text","text":"、"},{"type":"codeinline","content":[{"type":"text","text":"AbstractSet"}]},{"type":"text","text":" 都繼承了 Collection 類,Collection 類只定義一節標準方法比如說 add、remove、set、equals 等,具體的方法由抽象類或者實現類去實現。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Collections 是集合類的工具類,Collections 提供了一些工具類的基本使用"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"sort 方法,對當前集合進行排序, 實現 Comparable 接口的類,只能使用一種排序方案,這種方案叫做自然比較"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"比如實現線程安全的容器 "},{"type":"codeinline","content":[{"type":"text","text":"Collections.synchronizedList"}]},{"type":"text","text":"、 "},{"type":"codeinline","content":[{"type":"text","text":"Collections.synchronizedMap"}]},{"type":"text","text":" 等"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"reverse 反轉,使用 reverse 方法可以根據元素的自然順序 對指定列表按降序進行排序。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"fill,使用指定元素替換指定列表中的所有元素。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"有很多用法,讀者可以翻閱 Collections 的源碼查看,Collections 不能進行實例化,所以 Collections 中的方法都是由 "},{"type":"codeinline","content":[{"type":"text","text":"Collections.方法"}]},{"type":"text","text":" 直接調用。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"你知道 fail-fast 和 fail-safe 嗎"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"fail-fast"}]},{"type":"text","text":" 是 Java 中的一種"},{"type":"codeinline","content":[{"type":"text","text":"快速失敗"}]},{"type":"text","text":"機制,java.util 包下所有的集合都是快速失敗的,快速失敗會拋出 "},{"type":"codeinline","content":[{"type":"text","text":"ConcurrentModificationException"}]},{"type":"text","text":" 異常,fail-fast 你可以把它理解爲一種快速檢測機制,它只能用來檢測錯誤,不會對錯誤進行恢復,fail-fast 不一定只在"},{"type":"codeinline","content":[{"type":"text","text":"多線程"}]},{"type":"text","text":"環境下存在,ArrayList 也會拋出這個異常,主要原因是由於 "},{"type":"text","marks":[{"type":"strong"}],"text":"modCount 不等於 expectedModCount"},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"fail-safe"}]},{"type":"text","text":" 是 Java 中的一種 "},{"type":"codeinline","content":[{"type":"text","text":"安全失敗"}]},{"type":"text","text":" 機制,它表示的是在遍歷時不是直接在原集合上進行訪問,而是先複製原有集合內容,在拷貝的集合上進行遍歷。 由於迭代時是對原集合的拷貝進行遍歷,所以在遍歷過程中對原集合所作的修改並不能被迭代器檢測到,所以不會觸發 ConcurrentModificationException。"},{"type":"codeinline","content":[{"type":"text","text":"java.util.concurrent"}]},{"type":"text","text":" 包下的容器都是安全失敗的,可以在多線程條件下使用,併發修改。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"ArrayList、LinkedList 和 Vector 的區別"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這也是一道老生常談的問題了"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"ArrayList、LinkedList、Vector"},{"type":"text","text":" 都是位於 "},{"type":"codeinline","content":[{"type":"text","text":"java.util"}]},{"type":"text","text":" 包下的工具類,它們都實現了 List 接口。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ArrayList 的底層是動態數組,它是基於數組的特性而演變出來的,所以ArrayList 遍歷訪問非常快,但是增刪比較慢,因爲會涉及到數組的拷貝。ArrayList 是一個非線程安全的容器,在併發場景下會造成問題,如果想使用線程安全的容器的話,推薦使用 "},{"type":"codeinline","content":[{"type":"text","text":"Collections.synchronizedList"}]},{"type":"text","text":";ArrayList 在擴容時會增加 50% 的容量。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"LinkedList 的底層是雙向鏈表,所以 LinkedList 的增加和刪除非常快,只需把元素刪除,把各自的指針指向新的元素即可。但是 LinkedList 遍歷比較慢,因爲只有每次訪問一個元素才能知道下一個元素的值。LinkedList 也是一個非線程安全的容器,推薦使用 "},{"type":"codeinline","content":[{"type":"text","text":"Collections.synchronizedList"}]}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Vector 向量是最早出現的集合容器,Vector 是一個線程安全的容器,它的每個方法都粗暴的加上了 "},{"type":"codeinline","content":[{"type":"text","text":"synchronized"}]},{"type":"text","text":" 鎖,所以它的增刪、遍歷效率都很低。Vector 在擴容時,它的容量會增加一倍。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"Exception 和 Error 有什麼區別"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Exception 泛指的是 "},{"type":"codeinline","content":[{"type":"text","text":"異常"}]},{"type":"text","text":",Exception 主要分爲兩種異常,一種是編譯期出現的異常,稱爲 "},{"type":"codeinline","content":[{"type":"text","text":"checkedException"}]},{"type":"text","text":" ,一種是程序運行期間出現的異常,稱爲 "},{"type":"codeinline","content":[{"type":"text","text":"uncheckedException"}]},{"type":"text","text":",常見的 checkedException 有 "},{"type":"codeinline","content":[{"type":"text","text":"IOException"}]},{"type":"text","text":",uncheckedException 統稱爲 "},{"type":"codeinline","content":[{"type":"text","text":"RuntimeException"}]},{"type":"text","text":",常見的 RuntimeException 主要有"},{"type":"codeinline","content":[{"type":"text","text":"NullPointerException"}]},{"type":"text","text":"、 "},{"type":"codeinline","content":[{"type":"text","text":"IllegalArgumentException"}]},{"type":"text","text":"、"},{"type":"codeinline","content":[{"type":"text","text":"ArrayIndexOutofBoundException"}]},{"type":"text","text":"等,Exception 可以被捕獲。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Error 是指程序運行過程中出現的錯誤,通常情況下會造成程序的崩潰,Error 通常是不可恢復的,Error 不能被捕獲。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"詳細可以參考這篇文章 "},{"type":"link","attrs":{"href":"https://mp.weixin.qq.com/s?_biz=MzU2NDg0OTgyMA==&mid=2247486149&idx=1&sn=fe3ab875ae081bfc47166b68ba2d4bc9&chksm=fc45f736cb327e20ea9c62a6f2aa88928b6666d9cc868ab378a41886417e445faef269017996&token=323525290&lang=zhCN#rd","title":""},"content":[{"type":"text","text":"看完這篇 Exception 和 Error ,和麪試官扯皮就沒問題了"}]},{"type":"text","text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"String、StringBuilder 和 StringBuffer 有什麼區別"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"String 特指的是 Java 中的字符串,String 類位於 "},{"type":"codeinline","content":[{"type":"text","text":"java.lang"}]},{"type":"text","text":" 包下,String 類是由 final 修飾的,String 字符串一旦創建就不能被修改,任何對 String 進行修改的操作都相當於重新創建了一個字符串。String 字符串的底層使用 StringBuilder 來實現的"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"StringBuilder 位於 "},{"type":"codeinline","content":[{"type":"text","text":"java.util"}]},{"type":"text","text":" 包下,StringBuilder 是一非線程安全的容器,StringBuilder 的 append 方法常用於字符串拼接,它的拼接效率要比 String 中 "},{"type":"codeinline","content":[{"type":"text","text":"+"}]},{"type":"text","text":" 號的拼接效率高。StringBuilder 一般不用於併發環境"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"StringBuffer 位於 "},{"type":"codeinline","content":[{"type":"text","text":"java.util"}]},{"type":"text","text":" 包下,StringBuffer 是一個線程安全的容器,多線程場景下一般使用 StringBuffer 用作字符串的拼接"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"StringBuilder 和 StringBuffer 都是繼承於"},{"type":"text","marks":[{"type":"strong"}],"text":"AbstractStringBuilder"},{"type":"text","text":" 類,AbstractStringBuilder 類實現了 StringBuffer 和 StringBuilder 的常規操作。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"動態代理是基於什麼原理"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"代理一般分爲"},{"type":"codeinline","content":[{"type":"text","text":"靜態代理"}]},{"type":"text","text":"和 "},{"type":"codeinline","content":[{"type":"text","text":"動態代理"}]},{"type":"text","text":",它們都是代理模式的一種應用,靜態代理指的是在程序運行前已經編譯好,程序知道由誰來執行代理方法。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"而動態代理只有在程序運行期間才能確定,相比於靜態代理, 動態代理的優勢在於可以很方便的對代理類的函數進行統一的處理,而不用修改每個代理類中的方法。可以說動態代理是基於 "},{"type":"codeinline","content":[{"type":"text","text":"反射"}]},{"type":"text","text":" 實現的。通過反射我們可以直接操作類或者對象,比如獲取類的定義,獲取聲明的屬性和方法,調用方法,在運行時可以修改類的定義。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"動態代理是一種在運行時構建代理、動態處理方法調用的機制。動態代理的實現方式有很多,Java 提供的代理被稱爲 "},{"type":"codeinline","content":[{"type":"text","text":"JDK 動態代理"}]},{"type":"text","text":",JDK 動態代理是基於類的繼承。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"int 和 Integer 的區別"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"int 和 Integer 區別可就太多了"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"int 是 Java 中的基本數據類型,int 代表的是 "},{"type":"codeinline","content":[{"type":"text","text":"整型"}]},{"type":"text","text":",一個 int 佔 4 字節,也就是 32 位,int 的初始值是默認值是 0 ,int 在 Java 內存模型中被分配在棧中,int 沒有方法。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Integer 是 Java 中的基本數據類型的包裝類,Integer 是一個對象,Integer 可以進行方法調用,Integer 的默認值是 null,Integer 在 Java 內存模型中被分配在堆中。int 和 Integer 在計算時可以進行相互轉換,int -> Integer 的過程稱爲 "},{"type":"codeinline","content":[{"type":"text","text":"裝箱"}]},{"type":"text","text":",Integer -> int 的過程稱爲 "},{"type":"codeinline","content":[{"type":"text","text":"拆箱"}]},{"type":"text","text":",Integer 還有 IntegerCache ,會自動緩存 -128 - 127 中的值"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"Java 提供了哪些 I/O 方式"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Java I/O 方式有很多種,傳統的 I/O 也稱爲 "},{"type":"codeinline","content":[{"type":"text","text":"BIO"}]},{"type":"text","text":",主要流有如下幾種"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/83/8314db237f55afba9b94450f91af25f1.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Java I/O 包的實現比較簡單,但是容易出現性能瓶頸,傳統的 I/O 是基於同步阻塞的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"JDK 1.4 之後提供了 "},{"type":"codeinline","content":[{"type":"text","text":"NIO"}]},{"type":"text","text":",也就是位於 "},{"type":"codeinline","content":[{"type":"text","text":"java.nio"}]},{"type":"text","text":" 包下,提供了基於 "},{"type":"text","marks":[{"type":"strong"}],"text":"channel、Selector、Buffer"},{"type":"text","text":"的抽象,可以構建多路複用、同步非阻塞 I/O 程序。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"JDK 1.7 之後對 NIO 進行了進一步改進,引入了 "},{"type":"codeinline","content":[{"type":"text","text":"異步非阻塞"}]},{"type":"text","text":" 的方式,也被稱爲 "},{"type":"codeinline","content":[{"type":"text","text":"AIO(Asynchronous IO)"}]},{"type":"text","text":"。可以用生活中的例子來說明:項目經理交給手下員工去改一個 bug,那麼項目經理不會一直等待員工解決 bug,他肯定在員工解決 bug 的期間給其他手下分配 bug 或者做其他事情,員工解決完 bug 之後再告訴項目經理 bug 解決完了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"談談你知道的設計模式"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一張思維導圖鎮場"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/55/55a7535c7396bdfe64f1f4fcc9dd848d.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"比如全局唯一性可以用 "},{"type":"codeinline","content":[{"type":"text","text":"單例模式"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以使用 "},{"type":"codeinline","content":[{"type":"text","text":"策略模式"}]},{"type":"text","text":" 優化過多的 if...else..."}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"制定標準用 "},{"type":"codeinline","content":[{"type":"text","text":"模版模式"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接手其他人的鍋,但不想改原來的類用 "},{"type":"codeinline","content":[{"type":"text","text":"適配器模式"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用 "},{"type":"codeinline","content":[{"type":"text","text":"組合"}]},{"type":"text","text":" 而不是繼承"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用 "},{"type":"codeinline","content":[{"type":"text","text":"裝飾器"}]},{"type":"text","text":"可以製作加糖、加奶酪的咖啡"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"代理"}]},{"type":"text","text":" 可以用於任何中間商......"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"Comparator 和 Comparable 有什麼不同"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Comparable 更像是自然排序"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Comparator 更像是定製排序"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"同時存在時採用 Comparator(定製排序)的規則進行比較。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對於一些普通的數據類型(比如 String, Integer, Double…),它們默認實現了Comparable 接口,實現了 compareTo 方法,我們可以直接使用。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"而對於一些自定義類,它們可能在不同情況下需要實現不同的比較策略,我們可以新創建 Comparator 接口,然後使用特定的 Comparator 實現進行比較。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"Object 類中一般都有哪些方法"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Object 類是所有對象的父類,它裏面包含一些所有對象都能夠使用的方法"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"hashCode():用於計算對象的哈希碼"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"equals():用於對象之間比較值是否相等"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"toString(): 用於把對象轉換成爲字符串"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"clone(): 用於對象之間的拷貝"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"wait(): 用於實現對象之間的等待"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"notify(): 用於通知對象釋放資源"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"notifyAll(): 用於通知所有對象釋放資源"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"finalize(): 用於告知垃圾回收器進行垃圾回收"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"getClass(): 用於獲得對象類"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"Java 泛型和類型擦除"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"http://softlab.sdut.edu.cn/blog/subaochen/2017/01/generics-type-erasure/","title":""},"content":[{"type":"text","text":"關於 Java 泛型和擦除看着一篇就夠了。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"反射的基本原理,反射創建類實例的三種方式是什麼"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"反射機制就是使 Java 程序在運行時具有"},{"type":"codeinline","content":[{"type":"text","text":"自省(introspect)"}]},{"type":"text","text":" 的能力,通過反射我們可以直接操作類和對象,比如獲取某個類的定義,獲取類的屬性和方法,構造方法等。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"創建類實例的三種方式是"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對象實例.getClass();"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過 Class.forName() 創建"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對象實例.newInstance() 方法創建"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"強引用、若引用、虛引用和幻象引用的區別"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們說的不同的引用類型其實都是邏輯上的,而對於虛擬機來說,主要體現的是對象的不同的"},{"type":"codeinline","content":[{"type":"text","text":"可達性(reachable)"}]},{"type":"text","text":" 狀態和對"},{"type":"codeinline","content":[{"type":"text","text":"垃圾收集(garbage collector)"}]},{"type":"text","text":"的影響。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以通過下面的流程來對對象的生命週期做一個總結"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/1f/1f561f9869ab78c642b434d62ec8e38f.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對象被創建並初始化,對象在運行時被使用,然後離開對象的作用域,對象會變成不可達並會被垃圾收集器回收。圖中用紅色標明的區域表示對象處於強可達階段。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"JDK1.2 介紹了 "},{"type":"codeinline","content":[{"type":"text","text":"java.lang.ref"}]},{"type":"text","text":" 包,對象的生命週期有四個階段:"},{"type":"codeinline","content":[{"type":"text","text":"􏲧強可達􏰛(Strongly Reachable􏰜)"}]},{"type":"text","text":"、"},{"type":"codeinline","content":[{"type":"text","text":"軟可達(Soft Reachable􏰜)"}]},{"type":"text","text":"、"},{"type":"codeinline","content":[{"type":"text","text":"弱可達(Weak Reachable􏰜)"}]},{"type":"text","text":"、 "},{"type":"codeinline","content":[{"type":"text","text":"幻象可達(Phantom Reachable􏰜)"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/f2/f2dfdf28f5c88cab9ec9a9c4a39603ad.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果只討論符合垃圾回收條件的對象,那麼只有三種:軟可達、弱可達和幻象可達。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"軟可達:軟可達就是􏱬我們只能通過軟引用􏳂才能訪問的狀態,軟可達的對象是由 "},{"type":"codeinline","content":[{"type":"text","text":"SoftReference"}]},{"type":"text","text":" 引用的對象,並且沒有強引用的對象。軟引用是用來描述一些還"},{"type":"text","marks":[{"type":"strong"}],"text":"有用但是非必須"},{"type":"text","text":"的對象。垃圾收集器會盡可能長時間的保留軟引用的對象,但是會在發生 "},{"type":"codeinline","content":[{"type":"text","text":"OutOfMemoryError"}]},{"type":"text","text":" 之前,回收軟引用的對象。如果回收完軟引用的對象,內存還是不夠分配的話,就會直接拋出 OutOfMemoryError。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"弱可達:弱可達的對象是 "},{"type":"codeinline","content":[{"type":"text","text":"WeakReference"}]},{"type":"text","text":" 引用的對象。垃圾收集器可以隨時收集弱引用的對象,不會嘗試保留軟引用的對象。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"幻象可達:幻象可達是由 "},{"type":"codeinline","content":[{"type":"text","text":"PhantomReference "}]},{"type":"text","text":" 引用的對象,幻象可達就是沒有強、軟、弱引用進行關聯,並且已經被 finalize 過了,只有幻象引用指向這個對象的時候。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"除此之外,還有強可達和不可達的兩種可達性判斷條件"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"強可達:就是一個對象剛被創建、初始化、使用中的對象都是處於強可達的狀態"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"不可達(unreachable)"}]},{"type":"text","text":":處於不可達的對象就意味着對象可以被清除了。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下面是一個不同可達性狀態的轉換圖"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/80/80bd229684023df3a02c99a88d9f7e56.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"判斷可達性條件,也是 JVM 垃圾收集器決定如何處理對象的一部分考慮因素。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所有的對象可達性引用都是 "},{"type":"codeinline","content":[{"type":"text","text":"java.lang.ref.Reference"}]},{"type":"text","text":" 的子類,它裏面有一個"},{"type":"codeinline","content":[{"type":"text","text":"get()"}]},{"type":"text","text":" 方法,返回引用對象。 如果已通過程序或垃圾收集器清除了此引用對象,則此方法返回 null 。也就是說,除了幻象引用外,軟引用和弱引用都是可以得到對象的。而且這些對象可以人爲"},{"type":"codeinline","content":[{"type":"text","text":"拯救"}]},{"type":"text","text":",變爲強引用,例如把 this 關鍵字賦值給對象,只要重新和引用鏈上的任意一個對象建立關聯即可。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"final、finally 和 finalize() 的區別"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這三者可以說是沒有任何關聯之處,我們上面談到了,final 可以用來修飾類、變量和方法,可以參考上面 final 的那道面試題。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"finally 是一個關鍵字,它經常和 try 塊一起使用,用於異常處理。使用 try...finally 的代碼塊種,finally 部分的代碼一定會被執行,所以我們經常在 finally 方法中用於資源的關閉操作。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"JDK1.7 中,推薦使用 "},{"type":"codeinline","content":[{"type":"text","text":"try-with-resources"}]},{"type":"text","text":" 優雅的關閉資源,它直接使用 try(){} 進行資源的關閉即可,就不用寫 finally 關鍵字了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"finalize 是 Object 對象中的一個方法,用於對象的回收方法,這個方法我們一般不推薦使用,finalize 是和垃圾回收關聯在一起的,在 Java 9 中,將 finalize 標記爲了 "},{"type":"codeinline","content":[{"type":"text","text":"deprecated"}]},{"type":"text","text":", 如果沒有特別原因,不要實現 finalize 方法,也不要指望他來進行垃圾回收。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"內部類有哪些分類,分別解釋一下"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在 Java 中,可以將一個類的定義放在另外一個類的定義內部,這就是"},{"type":"text","marks":[{"type":"strong"}],"text":"內部類"},{"type":"text","text":"。內部類本身就是類的一個屬性,與其他屬性定義方式一致。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"內部類的分類一般主要有四種"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"成員內部類"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"局部內部類"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"匿名內部類"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"靜態內部類"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"靜態內部類"}]},{"type":"text","text":"就是定義在類內部的靜態類,靜態內部類可以訪問外部類所有的靜態變量,而不可訪問外部類的非靜態變量;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"成員內部類"}]},{"type":"text","text":" 就是定義在類內部,成員位置上的非靜態類,就是成員內部類。成員內部類可以訪問外部類所有的變量和方法,包括靜態和非靜態,私有和公有。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"定義在方法中的內部類,就是"},{"type":"codeinline","content":[{"type":"text","text":"局部內部類"}]},{"type":"text","text":"。定義在實例方法中的局部類可以訪問外部類的所有變量和方法,定義在靜態方法中的局部類只能訪問外部類的靜態變量和方法。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"匿名內部類"}]},{"type":"text","text":" 就是沒有名字的內部類,除了沒有名字,匿名內部類還有以下特點:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"匿名內部類必須繼承一個抽象類或者實現一個接口"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"匿名內部類不能定義任何靜態成員和靜態方法。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當所在的方法的形參需要被匿名內部類使用時,必須聲明爲 final。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"匿名內部類不能是抽象的,它必須要實現繼承的類或者實現的接口的所有抽象方法。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"說出幾種常用的異常"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"NullPointerException: 空指針異常"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"NoSuchMethodException:找不到方法"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"IllegalArgumentException:不合法的參數異常"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"IndexOutOfBoundException: 數組下標越界異常"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"IOException:由於文件未找到、未打開或者I/O操作不能進行而引起異常"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ClassNotFoundException :找不到文件所拋出的異常"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"NumberFormatException: 字符的UTF代碼數據格式有錯引起異常;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"InterruptedException: 線程中斷拋出的異常"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"靜態綁定和動態綁定的區別"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一個Java 程序要經過編寫、編譯、運行三個步驟,其中編寫代碼不在我們討論的範圍之內,那麼我們的重點自然就放在了"},{"type":"codeinline","content":[{"type":"text","text":"編譯"}]},{"type":"text","text":" 和 "},{"type":"codeinline","content":[{"type":"text","text":"運行"}]},{"type":"text","text":"這兩個階段,由於編譯和運行階段過程相當繁瑣,下面就我的理解來進行解釋:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Java 程序從源文件創建到程序運行要經過兩大步驟:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1、編譯時期是由編譯器將源文件編譯成字節碼的過程"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2、字節碼文件由Java虛擬機解釋執行"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"綁定"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"綁定就是一個方法的調用與調用這個方法的類連接在一起的過程被稱爲綁定。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"綁定主要分爲兩種:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"靜態綁定 和 動態綁定"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"綁定的其他叫法"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"靜態綁定 == 前期綁定 == 編譯時綁定"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"動態綁定 == 後期綁定 == 運行時綁定"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲了方便區分: 下面統一稱呼爲靜態綁定和動態綁定"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"靜態綁定"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"在程序運行前,也就是編譯時期 JVM 就能夠確定方法由誰調用,這種機制稱爲靜態綁定"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"識別靜態綁定的三個關鍵字以及各自的理解"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果一個方法由 private、static、final 任意一個關鍵字所修飾,那麼這個方法是前期綁定的"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"構造方法也是前期綁定"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"private:private 關鍵字是私有的意思,如果被 private 修飾的方法是無法由本類之外的其他類所調用的,也就是本類所特有的方法,所以也就由編譯器識別此方法是屬於哪個類的"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class Person {\n\n private String talk;\n\n private String canTalk(){\n return talk;\n }\n}\n\nclass Animal{\n\n public static void main(String[] args) {\n Person p = new Person();\n // private 修飾的方法是Person類獨有的,所以Animal類無法訪問(動物本來就不能說話)\n// p.canTalk();\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"final:final 修飾的方法不能被重寫,但是可以由子類進行調用,如果將方法聲明爲 final 可以有效的關閉動態綁定"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class Fruit {\n\n private String fruitName;\n\n final String eatingFruit(String name){\n System.out.println(\"eating \" + name);\n return fruitName;\n }\n}\n\nclass Apple extends Fruit{\n\n // 不能重寫final方法,eatingFruit方法只屬於Fruit類,Apple類無法調用\n// String eatingFruit(String name){\n// super.eatingFruit(name);\n// }\n\n String eatingApple(String name){\n return super.eatingFruit(name);\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"static: static 修飾的方法比較特殊,不用通過 new 出某個類來調用,由"},{"type":"codeinline","content":[{"type":"text","text":"類名.變量名"}]},{"type":"text","text":"直接調用該方法,這個就很關鍵了,new 很關鍵,也可以認爲是開啓多態的導火索,而由類名.變量名直接調用的話,此時的類名是確定的,並不會產生多態,如下代碼:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class SuperClass {\n\n public static void sayHello(){\n \n System.out.println(\"由 superClass 說你好\");\n }\n}\n\npublic class SubClass extends SuperClass{\n\n public static void sayHello(){\n System.out.println(\"由 SubClass 說你好\");\n }\n\n public static void main(String[] args) {\n SuperClass.sayHello();\n SubClass.sayHello();\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"SubClass 繼承 SuperClass 後,在"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/c9/c9a5915ff737e9eeffe296a5c6bfb460.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"是無法重寫 sayHello 方法的,也就是說 sayHello() 方法是對子類隱藏的,但是你可以編寫自己的 sayHello() 方法,也就是子類 SubClass 的sayHello() 方法,由此可見,方法由 static 關鍵詞所修飾,也是編譯時綁定"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"動態綁定"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"在運行時根據具體對象的類型進行綁定"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"除了由 private、final、static 所修飾的方法和構造方法外,JVM 在運行期間決定方法由哪個對象調用的過程稱爲動態綁定"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果把編譯、運行看成一條時間線的話,在運行前必須要進行程序的編譯過程,那麼在編譯期進行的綁定是前期綁定,在程序運行了,發生的綁定就是後期綁定"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class Father {\n\n void drinkMilk(){\n System.out.println(\"父親喜歡喝牛奶\");\n }\n}\n\npublic class Son extends Father{\n\n @Override\n void drinkMilk() {\n System.out.println(\"兒子喜歡喝牛奶\");\n }\n\n public static void main(String[] args) {\n Father son = new Son();\n son.drinkMilk();\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Son 類繼承 Father 類,並重寫了父類的 dringMilk() 方法,在輸出結果得出的是兒子喜歡喝牛奶。那麼上面的綁定方式是什麼呢?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上面的綁定方式稱之爲"},{"type":"codeinline","content":[{"type":"text","text":"動態綁定"}]},{"type":"text","text":",因爲在你編寫 Father son = new Son() 的時候,編譯器並不知道 son 對象真正引用的是誰,在程序運行時期才知道,這個 son 是一個 Father 類的對象,但是卻指向了 Son 的引用,這種概念稱之爲多態,那麼我們就能夠整理出來多態的三個原則:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"繼承 "}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"重寫"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"父類引用指向子類對象"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"也就是說,在 Father son = new Son() ,觸發了動態綁定機制。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"動態綁定的過程"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"虛擬機提取對象的實際類型的方法表;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"虛擬機搜索方法簽名;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"調用方法。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"動態綁定和靜態綁定的特點"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"靜態綁定"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"靜態綁定在編譯時期觸發,那麼它的主要特點是"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1、編譯期觸發,能夠提早知道代碼錯誤"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2、提高程序運行效率"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"動態綁定"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1、使用動態綁定的前提條件能夠提高代碼的可用性,使代碼更加靈活。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2、多態是設計模式的基礎,能夠降低耦合性。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我發起了一個 Github 項目,地址是 https://github.com/crisxuan/bestJavaer,歡迎各位 star,下面是具體介紹"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/37/374fdfc2e24a6de7c5d2faa632240c7b.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章