Java設計模式相關面試
1.介面是什麼?為什麼要使用介面而不是直接使用具體類?
介面用於定義 API。它定義了類必須得遵循的規則。同時,它提供了一種抽象,因為客戶端只使用介面,這樣可以有多重實現,如 List 介面,你可以使用可隨機訪問的 ArrayList,也可以使用方便插入和刪除的 LinkedList。介面中不允許寫程式碼,以此來保證抽象,但是 Java 8 中你可以在介面宣告靜態的預設方法,這種方法是具體的。
2.java中,抽象類與介面之間有什麼區別?
1.一個類可以實現多個介面 ,但卻只能繼承最多一個抽象類。
2.抽象類可以包含具體的方法 , 介面的所有方法都是抽象的。
3.抽象類可以宣告和使用欄位 ,介面則不能,但介面可以建立靜態的final常量。
4.介面的方法都是public的,抽象類的方法可以是public,protected,private或者預設的package;
5.抽象類可以定義建構函式,介面卻不能。
3.除了單例模式,你在生產環境中還用過什麼設計模式?
這需要根據你的經驗來回答。一般情況下,你可以說依賴注入,工廠模式,裝飾模式或者觀察者模式,隨意選擇你使用過的一種即可。不過你要準備回答接下的基於你選擇的模式的問題。
4.什麼事里氏替換原則?
1、開閉原則(Open Close Principle)
開閉原則就是說對擴充套件開放,對修改關閉。在程式需要進行拓展的時候,不能去修改原有的程式碼,實現一個熱插拔的效果。所以一句話概括就是:為了使程式的擴充套件性好,易於維護和升級。想要達到這樣的效果,我們需要使用介面和抽象類,後面的具體設計中我們會提到這點。
2、里氏代換原則(Liskov Substitution Principle)
里氏代換原則(Liskov Substitution Principle LSP)面向物件設計的基本原則之一。 里氏代換原則中說,任何基類可以出現的地方,子類一定可以出現。 LSP是繼承複用的基石,只有當衍生類可以替換掉基類,軟體單位的功能不受到影響時,基類才能真正被複用,而衍生類也能夠在基類的基礎上增加新的行為。里氏代換原則是對“開-閉”原則的補充。實現“開-閉”原則的關鍵步驟就是抽象化。而基類與子類的繼承關係就是抽象化的具體實現,所以里氏代換原則是對實現抽象化的具體步驟的規範。—— From Baidu 百科
3、依賴倒轉原則(Dependence Inversion Principle)
這個是開閉原則的基礎,具體內容:真對介面程式設計,依賴於抽象而不依賴於具體。
4、介面隔離原則(Interface Segregation Principle)
這個原則的意思是:使用多個隔離的介面,比使用單個介面要好。還是一個降低類之間的耦合度的意思,從這兒我們看出,其實設計模式就是一個軟體的設計思想,從大型軟體架構出發,為了升級和維護方便。所以上文中多次出現:降低依賴,降低耦合。
5、迪米特法則(最少知道原則)(Demeter Principle)
為什麼叫最少知道原則,就是說:一個實體應當儘量少的與其他實體之間發生相互作用,使得系統功能模組相對獨立。
6、合成複用原則(Composite Reuse Principle)
原則是儘量使用合成/聚合的方式,而不是使用繼承
5.什麼情況下會違反迪米特法則?為什麼會有這個問題?
迪米特法則建議“只和朋友說話,不要陌生人說話”,以此來減少類之間的耦合。
6.介面卡模式是什麼?什麼時候使用?
介面卡模式(Adapter Pattern)是作為兩個不相容的介面之間的橋樑。這種型別的設計模式屬於結構型模式,它結合了兩個獨立介面的功能。介面卡模式提供對介面的轉換。如果你的客戶端使用某些介面,但是你有另外一些介面,你就可以寫一個適配去來連線這些介面。
7.介面卡模式與裝飾器模式有什麼區別?
雖然介面卡模式和裝飾器模式的結構類似,但是每種模式的出現意圖不同。介面卡模式被用於橋接兩個介面,而裝飾模式的目的是在不修改類的情況下給類增加新的功能。
裝飾者模式:動態地將責任附加到物件上,若要擴充套件功能,裝飾者模提供了比繼承更有彈性的替代方案。
通俗的解釋:裝飾模式就是給一個物件增加一些新的功能,而且是動態的,要求裝飾物件和被裝飾物件實現同一個介面,裝飾物件持有被裝飾物件的例項。
介面卡模式:將一個類的介面,轉換成客戶期望的另一個介面。介面卡讓原本介面不相容的類可以合作無間。
介面卡模式有三種:類的介面卡模式、物件的介面卡模式、介面的介面卡模式。
通俗的說法:介面卡模式將某個類的介面轉換成客戶端期望的另一個介面表示,目的是消除由於介面不匹配所造成的類的相容性問題。
舉例如下:
1、介面卡模式
//file 為已定義好的檔案流
FileInputStream fileInput = new FileInputStream(file);
InputStreamReader inputStreamReader = new InputStreamReader(fileInput);
以上就是介面卡模式的體現,FileInputStream是位元組流,而並沒有字元流讀取字元的一些api,因此通過InputStreamReader將其轉為Reader子類,因此有了可以操作文字的檔案方法。
2、裝飾者模式
BufferedReader bufferedReader=new BufferedReader(inputStreamReader);構造了緩衝字元流,將FileInputStream位元組流包裝為BufferedReader過程就是裝飾的過程,剛開始的位元組流FileInputStream只有read一個位元組的方法,包裝為inputStreamReader後,就有了讀取一個字元的功能,在包裝為BufferedReader後,就擁有了read一行字元的功能。
8.介面卡模式和代理模式之間有什麼不同?
這個問題與前面的類似,介面卡模式和代理模式的區別在於他們的意圖不同。由於介面卡模式和代理模式都是封裝真正執行動作的類,因此結構是一致的,但是介面卡模式用於介面之間的轉換,而代理模式則是增加一個額外的中間層,以便支援分配、控制或智慧訪問。
9.什麼事模板方法模式?
模板方法提供演算法的框架,你可以自己去配置或定義步驟。例如,你可以將排序演算法看做是一個模板。它定義了排序的步驟,但是具體的比較,可以使用 Comparable 或者其語言中類似東西,具體策略由你去配置。列出演算法概要的方法就是眾所周知的模板方法。
10.什麼時候使用訪問者模式?
訪問者模式用於解決在類的繼承層次上增加操作,但是不直接與之關聯。這種模式採用雙派發的形式來增加中間層。
11.什麼時候使用組合模式?
組合模式使用樹結構來展示部分與整體繼承關係。它允許客戶端採用統一的形式來對待單個物件和物件容器。當你想要展示物件這種部分與整體的繼承關係時採用組合模式。
12.繼承和組合之間有什麼不同?
雖然兩種都可以實現程式碼複用,但是組合比繼承共靈活,因為組合允許你在執行時選擇不同的實現。用組合實現的程式碼也比繼承測試起來更加簡單。
13.描述Java中的過載與重寫?什麼時候用過載,什麼時候用重寫?
過載和重寫都允許你用相同的名稱來實現不同的功能,但是過載是編譯時活動,而重寫是執行時活動。你可以在同一個類中過載方法,但是隻能在子類中重寫方法。重寫必須要有繼承。
對有經驗的Java設計師來說,這是一個相當簡單的問題。如果你看到一個類的不同實現有著不同的方式來做同一件事,那麼就應該用重寫(overriding),而過載(overloading)是用不同的輸入做同一件事。在Java中,過載的方法簽名不同,而重寫並不是。
14.Java中,巢狀公共靜態類與頂級類有什麼不同?
類的內部可以有多個巢狀公共靜態類,但是一個 Java 原始檔只能有一個頂級公共類,並且頂級公共類的名稱與原始檔名稱必須一致。
15.OOP中的組合、聚合和關聯有什麼區別?
如果兩個物件彼此有關係,就說他們是彼此相關聯的。組合和聚合是面向物件中的兩種形式的關聯。組合是一種比聚合更強力的關聯。組合中,一個物件是另一個的擁有者,而聚合則是指一個物件使用另一個物件。如果物件 A 是由物件 B 組合的,則 A 不存在的話,B一定不存在,但是如果 A 物件聚合了一個物件 B,則即使 A 不存在了,B 也可以單獨存在。
16.給我一個符合開閉原則的設計模式的例子?
開閉原則要求你的程式碼對擴充套件開放,對修改關閉。這個意思就是說,如果你想增加一個新的功能,你可以很容易的在不改變已測試過的程式碼的前提下增加新的程式碼。有好幾個設計模式是基於開閉原則的,如策略模式,如果你需要一個新的策略,只需要實現介面,增加配置,不需要改變核心邏輯。一個正在工作的例子是 Collections.sort() 方法,這就是基於策略模式,遵循開閉原則的,你不需為新的物件修改 sort() 方法,你需要做的僅僅是實現你自己的 Comparator 介面。
17.使用工廠模式最主要的好處是什麼?你在哪裡使用?
工廠模式的最大好處是增加了建立物件時的封裝層次。如果 你使用工廠來建立物件,之後你可以使用更高階和更高效能的實現來替換原始的產品實現或類,這不需要在呼叫層做任何修改。可以看我的文章工廠模式得更詳細的解釋和和了解更多的好處。
18.工廠模式與抽象工廠模式的區別?
首先來看看這兩者的定義區別:
工廠模式:定義一個用於建立物件的藉口,讓子類決定例項化哪一個類
抽象工廠模式:為建立一組相關或相互依賴的物件提供一個介面,而且無需指定他們的具體類
個人覺得這個區別在於產品,如果產品單一,最合適用工廠模式,但是如果有多個業務品種、業務分類時,通過抽象工廠模式產生需要的物件是一種非常好的解決方式。再通俗深化理解下:工廠模式針對的是一個產品等級結構 ,抽象工廠模式針對的是面向多個產品等級結構的。
再來看看工廠方法模式與抽象工廠模式對比:
工廠方法模式 | 抽象工廠模式 |
針對的是一個產品等級結構 | 針對的是面向多個產品等級結構 |
一個抽象產品類 | 多個抽象產品類 |
可以派生出多個具體產品類 | 每個抽象產品類可以派生出多個具體產品類 |
一個抽象工廠類,可以派生出多個具體工廠類 | 一個抽象工廠類,可以派生出多個具體工廠類 |
每個具體工廠類只能建立一個具體產品類的例項 | 每個具體工廠類可以建立多個具體產品類的例項 |
18.什麼時候使用享元模式?
享元模式通過共享物件來避免建立太多的物件。為了使用享元模式,你需要確保你的物件是不可變的,這樣你才能安全的共享。JDK 中 String 池、Integer 池以及 Long 池都是很好的使用了享元模式的例子。
19 什麼是設計模式?你是否在你的程式碼裡面使用過任何設計模式?
設計模式是世界上各種各樣程式設計師用來解決特定設計問題的嘗試和測試的方法。設計模式是程式碼可用性的延伸。
20 你可以說出幾個在JDK庫中使用的設計模式嗎?
裝飾器設計模式(Decorator design pattern)被用於多個Java IO類中。單例模式(Singleton pattern)用於Runtime,Calendar和其他的一些類中。工廠模式(Factory pattern)被用於各種不可變的類如Boolean,像Boolean.valueOf,觀察者模式(Observer pattern)被用於Swing和很多的事件監聽中。
21.Java中什麼事單例設計模式?用Java寫出執行緒安全的單例
單例物件(Singleton)是一種常用的設計模式。在Java應用中,單例物件能保證在一個JVM中,該物件只有一個例項存在。這樣的模式有幾個好處:
1、某些類建立比較頻繁,對於一些大型的物件,這是一筆很大的系統開銷。
2、省去了new操作符,降低了系統記憶體的使用頻率,減輕GC壓力。
3、有些類如交易所的核心交易引擎,控制著交易流程,如果該類可以建立多個的話,系統完全亂了。(比如一個軍隊出現了多個司令員同時指揮,肯定會亂成一團),所以只有使用單例模式,才能保證核心交易伺服器獨立控制整個流程。
單例模式重點在於在整個系統上共享一些建立時較耗資源的物件。整個應用中只維護一個特定類例項,它被所有元件共同使用。Java.lang.Runtime是單例模式的經典例子。你可以在我的文章Java單例模式的10個問題看到更多的問題和討論。從Java 5開始你可以使用列舉(enum)來實現執行緒安全的單例。
22在Java中,什麼叫觀察者設計模式(observer design pattern)?
觀察者模式是基於物件的狀態變化和觀察者的通訊,以便他們作出相應的操作。簡單的例子就是一個天氣系統,當天氣變化時必須在展示給公眾的檢視中進行反映。這個檢視物件是一個主體,而不同的檢視是觀察者。可以在這篇文章中看到Java觀察者模式的完整例子。
23什麼是責任鏈設計模式?
責任鏈模式(Chain of Responsibility Pattern)為請求建立了一個接收者物件的鏈。這種模式給予請求的型別,對請求的傳送者和接收者進行解耦。這種型別的設計模式屬於行為型模式。在這種模式中,通常每個接收者都包含對另一個接收者的引用。如果一個物件不能處理該請求,那麼它會把相同的請求傳給下一個接收者,依此類推。
Java高階面試設計模式
1. 舉出一個例子,在這種情況你會更傾向於使用抽象類,而不是介面?
這是很常用但又是很難回答的設計面試問題。介面和抽象類都遵循”面向介面而不是實現編碼”設計原則,它可以增加程式碼的靈活性,可以適應不斷變化的需求。下面有幾個點可以幫助你回答這個問題:
- 在一些對時間要求比較高的應用中,傾向於使用抽象類,它會比介面稍快一點。
- 如果希望把一系列行為都規範在類繼承層次內,並且可以更好地在同一個地方進行編碼,那麼抽象類是一個更好的選擇。有時,介面和抽象類可以一起使用,介面中定義函式,而在抽象類中定義預設的實現。
。
2. 設計一個販賣機,可以接收不同的貨幣,出售不同的產品?
這是一個開放設計問題,你可以作為練習,嘗試著寫出設計文件、程式碼和JUnit測試而不是隻是解決這個問題,看看它花了你多少時間得到解決方案和得到需要的原形。理想情況下,這個問題應該可以在3個小時內解決,至少應該得到一個可以執行的版本。
3. 你有一個Smartphone類,可以派生如IPhone、AndroidPhone、WindowsMobilePhone類它還可以是一些有著品牌的手機名稱,你會怎麼設計這個類系統呢。
這是另外一個設計模式練習,你可以應用你的面向物件設計技巧來得到一個設計,這個設計需要足夠靈活能夠支援未來產品的擴充套件,足夠穩定能夠支援在現有模型進行修改。
5. 設計一個ATM機?
我們所有人都使用ATM(自動櫃員機)。想想你會怎麼設計一個ATM?就設計金融系統來說,必須知道它們應該在任何情況下都能夠如期工作。不管是斷電還是其他情況,ATM應該保持 正確的狀態(事務) , 想想 加鎖(locking)、事務(transaction)、錯誤條件(error condition)、邊界條件(boundary condition) 等等。儘管你不能想到具體的設計,但如果你可以指出非功能性需求,提出一些問題,想到關於邊界條件,這些都會是很好的一步。
6. 你正在寫一些類提供市場資料,你知道你可以不定時切換不同的廠商如Reuters、wombat或者直接的批發商,你會如何設計你的市場資料系統。
這是一個非常有趣的設計面試問題,並且真的在一家大的投資銀行問到過,如果你是用Java編碼的話這是一個相當平常的場景。最主要的一點是你要有一個MarketData介面,它會有呼叫端需要的方法如:getBid()、getPrice()、getLevel()等等,而MarketData應該由一個MarketDataProvider通過 依賴注入(dependency injection) 組成。因此,當你修改你的MarketData 提供器(MarketDataProvider)時,呼叫端不會受影響,因為它們是通過MarketData介面或類的方法來訪問的。
7. 在Java中,為什麼不允許從靜態方法中訪問非靜態變數?
你在Java中不能從靜態上下文訪問非靜態資料只是因為非靜態變數是跟具體的物件例項關聯的,而靜態的卻沒有和任何例項關聯。你可以看我的文章為什麼在靜態上下文中不能訪問非靜態變數檢視詳細的討論。
8. 在Java中設計一個併發規則的pipeline?
併發程式設計或併發設計這些天很火,它可以充分利用現在不斷提升的高階處理器的處理能力,而Java成為一個多執行緒語言也從這種情況獲益良多。設計一個併發系統需要記住的最關鍵的點是執行緒安全,不可變性,本地變數和避免使用static或者類變數(instance variables)。你只需要想著每一類都可以同時被多個執行緒同時執行,所以最好的做法就是每一個執行緒都處理自己的資料 ,不跟其他資料互動,並且執行時只需要最小的同步保證。這個問題可以涉及到從最初的討論到完整的類和介面編碼,但只要你記住併發中最重要的點和問題如,競爭條件(race condition)、死鎖(deadlock)、記憶體互動問題(memory interference)、原子性、ThreadLocal變數等,你都可以回答它。