JVM 底層 與 GC(Garbage Collection) 的面試問題
39) List、Set、Map 和 Queue 之間的區(qū)別?
List 是一個有序集合,允許元素重復(fù)。它的某些實現(xiàn)可以提供基于下標(biāo)值的常量訪問時間,但是這不是 List 接口保證的。Set 是一個無序集合。
40)poll() 方法和 remove() 方法的區(qū)別?
poll() 和 remove() 都是從隊列中取出一個元素,但是 poll() 在獲取元素失敗的時候會返回空,但是 remove() 失敗的時候會拋出異常。
41)Java 中 LinkedHashMap 和
PriorityQueue 的區(qū)別是什么?
PriorityQueue 保證最高或者最低優(yōu)先級的的元素總是在隊列頭部,但是 LinkedHashMap 維持的順序是元素插入的順序。當(dāng)遍歷一個 PriorityQueue 時,沒有任何順序保證,但是 LinkedHashMap 課保證遍歷順序是元素插入的順序。
42)ArrayList 與 LinkedList 的不區(qū)別?
最明顯的區(qū)別是 ArrrayList 底層的數(shù)據(jù)結(jié)構(gòu)是數(shù)組,支持隨機訪問,而 LinkedList 的底層數(shù)據(jù)結(jié)構(gòu)書鏈表,不支持隨機訪問。使用下標(biāo)訪問一個元素,ArrayList 的時間復(fù)雜度是 O(1),而 LinkedList 是 O(n)。
43)用哪兩種方式來實現(xiàn)集合的排序?
你可以使用有序集合,如 TreeSet 或 TreeMap,你也可以使用有順序的的集合,如 list,然后通過 Collections.sort() 來排序。
44)Java 中怎么打印數(shù)組?
你可以使用 Arrays.toString() 和 Arrays.deepToString() 方法來打印數(shù)組。由于數(shù)組沒有實現(xiàn) toString() 方法,所以如果將數(shù)組傳遞給 System.out.println() 方法,將無法打印出數(shù)組的內(nèi)容,但是 Arrays.toString() 可以打印每個元素。
45) Hashtable 與 HashMap 有什么不同之處?
這兩個類有許多不同的地方,下面列出了一部分:
a) Hashtable 是 JDK 1 遺留下來的類,而 HashMap 是后來增加的。
b)Hashtable 是同步的,比較慢,但 HashMap 沒有同步策略,所以會更快。
c)Hashtable 不允許有個空的 key,但是 HashMap 允許出現(xiàn)一個 null key。
46)Java 中的 HashSet,內(nèi)部是如何工作的?
HashSet 的內(nèi)部采用 HashMap來實現(xiàn)。由于 Map 需要 key 和 value,所以所有 key 的都有一個默認 value。類似于 HashMap,HashSet 不允許重復(fù)的 key,只允許有一個null key,意思就是 HashSet 中只允許存儲一個 null 對象。
47)寫一段代碼在遍歷 ArrayList 時移除一個元素?
該問題的關(guān)鍵在于面試者使用的是 ArrayList 的 remove() 還是 Iterator 的 remove()方法。這有一段示例代碼,是使用正確的方式來實現(xiàn)在遍歷的過程中移除元素,而不會出現(xiàn) ConcurrentModificationException 異常的示例代碼。
48)我們能自己寫一個容器類,然后使用 for-each 循環(huán)碼?
可以,你可以寫一個自己的容器類。如果你想使用 Java 中增強的循環(huán)來遍歷,你只需要實現(xiàn) Iterable 接口。如果你實現(xiàn) Collection 接口,默認就具有該屬性。
49)ArrayList 和 HashMap 的默認大小是多數(shù)?
在 Java 7 中,ArrayList 的默認大小是 10 個元素,HashMap 的默認大小是16個元素(必須是2的冪)。這就是 Java 7 中 ArrayList 和 HashMap 類的代碼片段:
// from ArrayList.java JDK 1.7
private static final int DEFAULT_CAPACITY = 10;
//from HashMap.java JDK 7
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
50)有沒有可能兩個不相等的對象有有相同的 hashcode?
有可能,兩個不相等的對象可能會有相同的 hashcode 值,這就是為什么在 hashmap 中會有沖突。相等 hashcode 值的規(guī)定只是說如果兩個對象相等,必須有相同的hashcode 值,但是沒有關(guān)于不相等對象的任何規(guī)定。
51)兩個相同的對象會有不同的的 hash code 嗎?
不能,根據(jù) hash code 的規(guī)定,這是不可能的。
52)我們可以在 hashcode() 中使用隨機數(shù)字嗎?
不行,因為對象的 hashcode 值必須是相同的。參見答案獲取更多關(guān)于 Java 中重寫 hashCode() 方法的知識。
53)Java 中,Comparator 與
Comparable 有什么不同?
Comparable 接口用于定義對象的自然順序,而 comparator 通常用于定義用戶定制的順序。Comparable 總是只有一個,但是可以有多個 comparator 來定義對象的順序。
54)為什么在重寫 equals 方法的時候需要重寫 hashCode 方法?
因為有強制的規(guī)范指定需要同時重寫 hashcode 與 equal 是方法,許多容器類,如 HashMap、HashSet 都依賴于 hashcode 與 equals 的規(guī)定。
Java 最佳實踐的面試問題
55)Java 中,編寫多線程程序的時候你會遵循哪些最佳實踐?
a)給線程命名,這樣可以幫助調(diào)試。
b)最小化同步的范圍,而不是將整個方法同步,只對關(guān)鍵部分做同步。
c)如果可以,更偏向于使用 volatile 而不是 synchronized。
d)使用更高層次的并發(fā)工具,而不是使用 wait() 和 notify() 來實現(xiàn)線程間通信,如 BlockingQueue,CountDownLatch 及 Semeaphore。
e)優(yōu)先使用并發(fā)集合,而不是對集合進行同步。并發(fā)集合提供更好的可擴展性。
56)說出幾點 Java 中使用 Collections 的最佳實踐?
a)使用正確的集合類,例如,如果不需要同步列表,使用 ArrayList 而不是 Vector。
b)優(yōu)先使用并發(fā)集合,而不是對集合進行同步。并發(fā)集合提供更好的可擴展性。
c)使用接口代表和訪問集合,如使用List存儲 ArrayList,使用 Map 存儲 HashMap 等等。
d)使用迭代器來循環(huán)集合。
e)使用集合的時候使用泛型。
57)說出在 Java 中使用線程的最佳實踐?
a)對線程命名
b)將線程和任務(wù)分離,使用線程池執(zhí)行器來執(zhí)行 Runnable 或 Callable。
c)使用線程池
58)說出 IO 的最佳實踐?
a)使用有緩沖區(qū)的 IO 類,而不要單獨讀取字節(jié)或字符。
b)使用 NIO 和 NIO2
c)在 finally 塊中關(guān)閉流,或者使用 try-with-resource 語句。
d)使用內(nèi)存映射文件獲取更快的 IO。
59)列出應(yīng)該遵循的 JDBC 最佳實踐?
a)使用批量的操作來插入和更新數(shù)據(jù)
b)使用 PreparedStatement 來避免 SQL 異常,并提高性能。
c)使用數(shù)據(jù)庫連接池
d)通過列名來獲取結(jié)果集,不要使用列的下標(biāo)來獲取。
60)說出幾條 Java 中方法重載的最佳實踐?
a)不要重載這樣的方法:一個方法接收 int 參數(shù),而另個方法接收 Integer 參數(shù)。
b)不要重載參數(shù)數(shù)量一致,而只是參數(shù)順序不同的方法。
c)如果重載的方法參數(shù)個數(shù)多于 5 個,采用可變參數(shù)。
Date、Time 及 Calendar 的面試題
61)在多線程環(huán)境下,SimpleDateFormat 是線程安全的嗎?
不是,非常不幸,DateFormat 的所有實現(xiàn),包括 SimpleDateFormat 都不是線程安全的,因此你不應(yīng)該在多線程序中使用,除非是在對外線程安全的環(huán)境中使用,如 將 SimpleDateFormat 限制在 ThreadLocal 中。如果你不這么做,在解析或者格式化日期的時候,可能會獲取到一個不正確的結(jié)果。因此,從日期、時間處理的所有實踐來說,我強力推薦 joda-time 庫。
62)Java 中如何格式化一個日期?如格式化為 ddMMyyyy 的形式?
Java 中,可以使用 SimpleDateFormat 類或者 joda-time 庫來格式日期。DateFormat 類允許你使用多種流行的格式來格式化日期。參見答案中的示例代碼,代碼中演示了將日期格式化成不同的格式,如 dd-MM-yyyy 或 ddMMyyyy。
關(guān)于 OOP 和設(shè)計模式的面試題
63)接口是什么?為什么要使用接口而不是直接使用具體類?
接口用于定義 API。它定義了類必須得遵循的規(guī)則。同時,它提供了一種抽象,因為客戶端只使用接口,這樣可以有多重實現(xiàn),如 List 接口,你可以使用可隨機訪問的 ArrayList,也可以使用方便插入和刪除的 LinkedList。接口中不允許寫代碼,以此來保證抽象,但是 Java 8 中你可以在接口聲明靜態(tài)的默認方法,這種方法是具體的。
64)Java 中,抽象類與接口之間有什么不同?
Java 中,抽象類和接口有很多不同之處,但是最重要的一個是 Java 中限制一個類只能繼承一個類,但是可以實現(xiàn)多個接口。抽象類可以很好的定義一個家族類的默認行為,而接口能更好的定義類型,有助于后面實現(xiàn)多態(tài)機制。關(guān)于這個問題的討論請查看答案。
65)除了單例模式,你在生產(chǎn)環(huán)境中還用過什么設(shè)計模式?
這需要根據(jù)你的經(jīng)驗來回答。一般情況下,你可以說依賴注入,工廠模式,裝飾模式或者觀察者模式,隨意選擇你使用過的一種即可。不過你要準(zhǔn)備回答接下的基于你選擇的模式的問題。
66)適配器模式是什么?什么時候使用?
適配器模式提供對接口的轉(zhuǎn)換。如果你的客戶端使用某些接口,但是你有另外一些接口,你就可以寫一個適配去來連接這些接口。
67)構(gòu)造器注入和 setter 依賴注入,那種方式更好?
每種方式都有它的缺點和優(yōu)點。構(gòu)造器注入保證所有的注入都被初始化,但是 setter 注入提供更好的靈活性來設(shè)置可選依賴。如果使用 XML 來描述依賴,Setter 注入的可讀寫會更強。經(jīng)驗法則是強制依賴使用構(gòu)造器注入,可選依賴使用 setter 注入。
68)依賴注入和工程模式之間有什么不同?
雖然兩種模式都是將對象的創(chuàng)建從應(yīng)用的邏輯中分離,但是依賴注入比工程模式更清晰。通過依賴注入,你的類就是 POJO,它只知道依賴而不關(guān)心它們怎么獲取。使用工廠模式,你的類需要通過工廠來獲取依賴。因此,使用 DI 會比使用工廠模式更容易測試。
69)適配器模式和裝飾器模式有什么區(qū)別?
雖然適配器模式和裝飾器模式的結(jié)構(gòu)類似,但是每種模式的出現(xiàn)意圖不同。適配器模式被用于橋接兩個接口,而裝飾模式的目的是在不修改類的情況下給類增加新的功能。
70)適配器模式和代理模式之前有什么不同?
這個問題與前面的類似,適配器模式和代理模式的區(qū)別在于他們的意圖不同。由于適配器模式和代理模式都是封裝真正執(zhí)行動作的類,因此結(jié)構(gòu)是一致的,但是適配器模式用于接口之間的轉(zhuǎn)換,而代理模式則是增加一個額外的中間層,以便支持分配、控制或智能訪問。
71)什么是模板方法模式?
模板方法提供算法的框架,你可以自己去配置或定義步驟。例如,你可以將排序算法看做是一個模板。它定義了排序的步驟,但是具體的比較,可以使用 Comparable 或者其語言中類似東西,具體策略由你去配置。列出算法概要的方法就是眾所周知的模板方法。
72)什么時候使用訪問者模式?
訪問者模式用于解決在類的繼承層次上增加操作,但是不直接與之關(guān)聯(lián)。這種模式采用雙派發(fā)的形式來增加中間層。
73)什么時候使用組合模式?
組合模式使用樹結(jié)構(gòu)來展示部分與整體繼承關(guān)系。它允許客戶端采用統(tǒng)一的形式來對待單個對象和對象容器。當(dāng)你想要展示對象這種部分與整體的繼承關(guān)系時采用組合模式。
74)繼承和組合之間有什么不同?
雖然兩種都可以實現(xiàn)代碼復(fù)用,但是組合比繼承共靈活,因為組合允許你在運行時選擇不同的實現(xiàn)。用組合實現(xiàn)的代碼也比繼承測試起來更加簡單。
75)描述 Java 中的重載和重寫?
重載和重寫都允許你用相同的名稱來實現(xiàn)不同的功能,但是重載是編譯時活動,而重寫是運行時活動。你可以在同一個類中重載方法,但是只能在子類中重寫方法。重寫必須要有繼承。
76)Java 中,嵌套公共靜態(tài)類與頂級類有什么不同?
類的內(nèi)部可以有多個嵌套公共靜態(tài)類,但是一個 Java 源文件只能有一個頂級公共類,并且頂級公共類的名稱與源文件名稱必須一致。
77) OOP 中的 組合、聚合和關(guān)聯(lián)有什么區(qū)別?
如果兩個對象彼此有關(guān)系,就說他們是彼此相關(guān)聯(lián)的。組合和聚合是面向?qū)ο笾械膬煞N形式的關(guān)聯(lián)。組合是一種比聚合更強力的關(guān)聯(lián)。組合中,一個對象是另一個的擁有者,而聚合則是指一個對象使用另一個對象。如果對象 A 是由對象 B 組合的,則 A 不存在的話,B一定不存在,但是如果 A 對象聚合了一個對象 B,則即使 A 不存在了,B 也可以單獨存在。
78)給我一個符合開閉原則的設(shè)計模式的例子?
開閉原則要求你的代碼對擴展開放,對修改關(guān)閉。這個意思就是說,如果你想增加一個新的功能,你可以很容易的在不改變已測試過的代碼的前提下增加新的代碼。有好幾個設(shè)計模式是基于開閉原則的,如策略模式,如果你需要一個新的策略,只需要實現(xiàn)接口,增加配置,不需要改變核心邏輯。一個正在工作的例子是 Collections.sort() 方法,這就是基于策略模式,遵循開閉原則的,你不需為新的對象修改 sort() 方法,你需要做的僅僅是實現(xiàn)你自己的 Comparator 接口。
79)什么時候使用享元模式?
享元模式通過共享對象來避免創(chuàng)建太多的對象。為了使用享元模式,你需要確保你的對象是不可變的,這樣你才能安全的共享。JDK 中 String 池、Integer 池以及 Long 池都是很好的使用了享元模式的例子。