- 請寫出您熟悉的幾種設計模式,並做簡單介紹。
答:
工廠設計模式:程序在接口和子類之間加入了一個過渡端,通過此過渡端可以動態取得
實現了共同接口的子類實例化對象。
代理設計模式:指由一個代理主題來操作真實主題,真實主題執行具體的業務操作,而
代理主題負責其他相關業務的處理。比如生活中的通過代理訪問網絡,客戶通過網絡代
理連接網絡(具體業務),由代理服務器完成用戶權限和訪問限制等與上網相關的其他操
作(相關業務)
適配器模式:如果一個類要實現一個具有很多抽象方法的接口,但是本身只需要實現接
口中的部分方法便可以達成目的,所以此時就需要一個中間的過渡類,但此過渡類又不
希望直接使用,所以將此類定義為抽象類最為合適,再讓以后的子類直接繼承該抽象類
便可選擇性的覆寫所需要的方法,而此抽象類便是適配器類。 - 寫出你用過的設計模式,並至少寫出 2 種模式的類圖或關鍵代碼。
工廠設計模式:
思路說明:由一個工廠類根據傳入的參數(一般是字符串參數),動態決定應該
創建哪一個產品子類(這些產品子類繼承自同一個父類或接口)的實例,並以父類
形式返回
優點:客戶端不負責對象的創建,而是由專門的工廠類完成;客戶端只負責對象的
調用,實現了創建和調用的分離,降低了客戶端代碼的難度;
缺點:如果增加和減少產品子類,需要修改簡單工廠類,違背了開閉原則;如果產
品子類過多,會導致工廠類非常的龐大,違反了高內聚原則,不利於后期維護。
public class SimpleFactory {
public static Product createProduct(String pname){
Product product=null;
if("p1".equals(pname)){
product = new Product1();
}else if("p2".equals(pname)){
product = new Product2();
}else if("pn".equals(pname)){
product = new ProductN();
}
return product;
} }
單例模式
/**
- 餓漢式的單例模式
- 在類加載的時候創建單例實例,而不是等到第一次請求實例的時候的時候創
建 - 1、私有 的無參數構造方法Singleton(),避免外部創建實例
- 2、私有靜態屬性instance
- 3、公有靜態方法getInstance()
/
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton(){ }
public static Singleton getInstance(){
return instance; } }
/* - 懶漢式的單例模式
在類加載的時候不創建單例實例,只有在第一次請求實例的時候的時候創建
/
public class Singleton {
private static Singleton instance;
private Singleton(){ }
/ - 多線程情況的單例模式,避免創建多個對象
*/
public static Singleton getInstance(){
if(instance null){//避免每次加鎖,只有第一次沒有創建對象時才加
鎖
synchronized(Singleton.class){//加鎖,只允許一個線程進入
if(instancenull){ //只創建一次對象
instance = new Singleton();
} } }
return instance;
}}
- 列出除 Singleton 外的常用的 3 種設計模式,並簡單描述
答:
工廠模式:工廠模式是 Java 中最常用的設計模式之一。這種類型的設計模式屬於
創建型模式,它提供了一種創建對象的最佳方式。在工廠模式中,我們在創建對象時不
會對客戶端暴露創建邏輯,並且是通過使用一個共同的接口來指向新創建的對象。
適配器模式:適配器模式是作為兩個不兼容的接口之間的橋梁。這種類型的設計模
式屬於結構型模式,它結合了兩個獨立接口的功能。這種模式涉及到一個單一的類,該
類負責加入獨立的或不兼容的接口功能。
模板模式:在模板模式中,一個抽象類公開定義了執行它的方法的方式/模板。它的
尚學堂 Java 面試題大全及參考答案
子類可以按需要重寫方法實現,但調用將以抽象類中定義的方式進行。 - Action 是單實例還是多實例,為什么?
答:
struts2 中 action 是多例的,即一個 session 產生一個 action
背景:
- Struts2 會對每一個請求,產生一個 Action 的實例來處理.
- Spring 的 Ioc 容器管理的 bean 默認是單實例的.
首先從數據安全性的問題上考慮,我們的 Action 應該保證是多例的,這樣才不會出現數
據問題。但是如果有的 action 比如只有 admin 才能操作,或者某些 action,全站公用
一個來提高性能,這樣的話,就可以使用單例模式。
不過幸好,Spring 的 bean 可以針對每一個設置它的 scope,所以,上面的問題就不是
問題了。如果用單例,就在 spring 的 action bean 配置的時候設置 scope=”prototype”
如果是單例的話,若出現兩個用戶都修改一個對象的屬性值,則會因為用戶修改時間不同,
兩個用戶訪問得到的屬性不一樣,操作得出的結果不一樣.
舉個例子:有一塊布長度 300cm,能做一件上衣(用掉 100cm)和一件褲子(用掉 200cm);
甲和乙同時訪問得到的長度都是 300cm,
甲想做上衣和褲子,他先截取100cm去做上衣,等上衣做完再去做褲子,而乙這時正好也拿
100cm 去做上衣,那好,等甲做完上衣再做褲子的時候發現剩下的布(100cm)已經不夠做
褲子了…..這就是影響系統的性能,解決的辦法就是給甲和乙一人一塊300cm的布,就不會
出現布被別人偷用的事情,也是就單實例和多實例的區別
如果設置成單例,那么多個線程會共享一個 ActionContext 和 ValueStack,這樣並發訪
問的時候就會出現問題了
struts 2 的 Action 是多實例的並非單例,也就是每次請求產生一個 Action 的對象。原
因是:struts 2 的 Action 中包含數據,例如你在頁面填寫的數據就會包含在 Action 的
成員變量里面。如果 Action 是單實例的話,這些數據在多線程的環境下就會相互影響,
例如造成別人填寫的數據被你看到了。所以 Struts2 的 Action 是多例模式的。
問題出現了,可以讓 Struts2 的 action 變成單例模式么?
Struts2 中,可以使用注解開發,在 Action 上@Scope(“prototype”) 指定為多例 , 默
認為 singleton()單例)
基本上 action 的 scope 需要是 prototype,就是每次請求都建立新的線程
不寫的話,默認是 singleton 了
- 寫一個單例類
答:單例模式主要作用是保證在 Java 應用程序中,一個類只有一個實例存在。下面給出
兩種不同形式的單例:
第一種形式:餓漢式單例
package com.bjsxt;
public class Singleton {
private Singleton(){}
private static Singleton instance = new Singleton();
public static Singleton getInstance(){
return instance;
}
}
第二種形式:懶漢式單例
package com.bjsxt;
public class Singleton {
private static Singleton instance = null;
private Singleton() {}
public static synchronized Singleton getInstance(){
if (instance==null) instance=newSingleton();
return instance;
}
}
單例的特點:外界無法通過構造器來創建對象,該類必須提供一個靜態方法向外界提供
該類的唯一實例。
【補充】用 Java 進行服務器端編程時,使用單例模式的機會還是很多的,服務器上的資
源都是很寶貴的,對於那些無狀態的對象其實都可以單例化或者靜態化(在內存中僅有
唯一拷貝),如果使用了 spring 這樣的框架來進行對象托管,Spring 的 IoC 容器在默認
情況下對所有托管對象都是進行了單例化處理的。 - 說說你所熟悉或聽說過的設計模式以及你對設計模式的看法
答:在 GoF 的《Design Patterns: Elements of Reusable Object-Oriented Software》
中給出了三類(創建型[對類的實例化過程的抽象化]、結構型[描述如何將類或對象結合
在一起形成更大的結構]、行為型[對在不同的對象之間划分責任和算法的抽象化])共 23
種設計模式,包括:Abstract Factory(抽象工廠模式),Builder(建造者模式),Factory
Method(工廠方法模式),Prototype(原始模型模式),Singleton(單例模式);Facade
(門面模式),Adapter(適配器模式),Bridge(橋梁模式),Composite(合成模式),
Decorator(裝飾模式),Flyweight(享元模式),Proxy(代理模式);Command(命
令模式),Interpreter(解釋器模式),Visitor(訪問者模式),Iterator(迭代子模式),
Mediator(調停者模式),Memento(備忘錄模式),Observer(觀察者模式),State
(狀態模式),Strategy(策略模式),Template Method(模板方法模式), Chain Of
Responsibility(責任鏈模式)。
所謂設計模式,就是一套被反復使用的代碼設計經驗的總結(情境中一個問題經過證實
的一個解決方案)。使用設計模式是為了可重用代碼、讓代碼更容易被他人理解、保證代
碼可靠性。設計模式使人們可以更加簡單方便的復用成功的設計和體系結構。將已證實
的技術表述成設計模式也會使新系統開發者更加容易理解其設計思路。
【補充】設計模式並不是像某些地方吹噓的那樣是遙不可及的編程理念,說白了設計模
式就是對面向對象的編程原則的實踐,面向對象的編程原則包括:
• 單一職責原則:一個類只做它該做的事情。(單一職責原則想表達的就是“高內聚”,
寫代碼最終極的原則只有六個字“高內聚、低耦合”,就如同葵花寶典或辟邪劍譜的
中心思想就八個字“欲練此功必先自宮”,所謂的高內聚就是一個代碼模塊只完成一
項功能,在面向對象中,如果只讓一個類完成它該做的事,而不涉及與它無關的領域
就是踐行了高內聚的原則,這個類就只有單一職責。我們都知道一句話叫“因為專注,
所以專業”,一個對象如果承擔太多的職責,那么注定它什么都做不好。這個世界上
任何好的東西都有兩個特征,一個是功能單一,好的相機絕對不是電視購物里面賣的
那種一個機器有一百多種功能的,它基本上只能照相;另一個是模塊化,好的自行車
是組裝車,從減震叉、剎車到變速器,所有的部件都是可以拆卸和重新組裝的,好的
乒乓球拍也不是成品拍,一定是底板和膠皮可以拆分和自行組裝的,一個好的軟件系
統,它里面的每個功能模塊也應該是可以輕易的拿到其他系統中使用的,這樣才能實
現軟件復用的目標。)
• 開閉原則:軟件實體應當對擴展開放,對修改關閉。(在理想的狀態下,當我們需要為
一個軟件系統增加新功能時,只需要從原來的系統派生出一些新類就可以,不需要修
324
尚學堂 Java 面試題大全及參考答案
改原來的任何一行代碼。要做到開閉有兩個要點:①抽象是關鍵,一個系統中如果沒
有抽象類或接口系統就沒有擴展點;②封裝可變性,將系統中的各種可變因素封裝到
一個繼承結構中,如果多個可變因素混雜在一起,系統將變得復雜而換亂,如果不清
楚如何封裝可變性,可以參考《設計模式精解》一書中對橋梁模式的講解的章節。)
• 依賴倒轉原則:面向接口編程。(該原則說得直白和具體一些就是聲明方法的參數類型、
方法的返回類型、變量的引用類型時,盡可能使用抽象類型而不用具體類型,因為抽
象類型可以被它的任何一個子類型所替代,請參考下面的里氏替換原則。)
• 里氏替換原則:任何時候都可以用子類型替換掉父類型。(關於里氏替換原則的描述,
Barbara Liskov 女士的描述比這個要復雜得多,但簡單的說就是能用父類型的地方就
一定能使用子類型。里氏替換原則可以檢查繼承關系是否合理,如果一個繼承關系違
背了里氏替換原則,那么這個繼承關系一定是錯誤的,需要對代碼進行重構。例如讓
貓繼承狗,或者狗繼承貓,又或者讓正方形繼承長方形都是錯誤的繼承關系,因為你
很容易找到違反里氏替換原則的場景。需要注意的是:子類一定是增加父類的能力而
不是減少父類的能力,因為子類比父類的能力更多,把能力多的對象當成能力少的對
象來用當然沒有任何問題。)
• 接口隔離原則:接口要小而專,絕不能大而全。(臃腫的接口是對接口的污染,既然接
口表示能力,那么一個接口只應該描述一種能力,接口也應該是高度內聚的。例如,
琴棋書畫就應該分別設計為四個接口,而不應設計成一個接口中的四個方法,因為如
果設計成一個接口中的四個方法,那么這個接口很難用,畢竟琴棋書畫四樣都精通的
人還是少數,而如果設計成四個接口,會幾項就實現幾個接口,這樣的話每個接口被
復用的可能性是很高的。Java 中的接口代表能力、代表約定、代表角色,能否正確
的使用接口一定是編程水平高低的重要標識。)
• 合成聚合復用原則:優先使用聚合或合成關系復用代碼。(通過繼承來復用代碼是面向
對象程序設計中被濫用得最多的東西,因為所有的教科書都無一例外的對繼承進行了
鼓吹從而誤導了初學者,類與類之間簡單的說有三種關系,IS-A 關系、HAS-A 關系、
USE-A 關系,分別代表繼承、關聯和依賴。其中,關聯關系根據其關聯的強度又可
以進一步划分為關聯、聚合和合成,但說白了都是 HAS-A 關系,合成聚合復用原則
想表達的是優先考慮 HAS-A 關系而不是 IS-A 關系復用代碼,原因嘛可以自己從百度
上找到一萬個理由,需要說明的是,即使在 Java 的 API 中也有不少濫用繼承的例子,
例如 Properties 類繼承了 Hashtable 類,Stack 類繼承了 Vector 類,這些繼承明顯
就是錯誤的,更好的做法是在 Properties 類中放置一個 Hashtable 類型的成員並且
將其鍵和值都設置為字符串來存儲數據,而 Stack 類的設計也應該是在 Stack 類中放
一個 Vector 對象來存儲數據。記住:任何時候都不要繼承工具類,工具是可以擁有
並可以使用的(HAS/USE),而不是拿來繼承的。)
• 迪米特法則:迪米特法則又叫最少知識原則,一個對象應當對其他對象有盡可能少的
了解。(迪米特法則簡單的說就是如何做到“低耦合”,門面模式和調停者模式就是
對迪米特法則的踐行。對於門面模式可以舉一個簡單的例子,你去一家公司洽談業務,
你不需要了解這個公司內部是如何運作的,你甚至可以對這個公司一無所知,去的時
候只需要找到公司入口處的前台美女,告訴她們你要做什么,她們會找到合適的人跟
你接洽,前台的美女就是公司這個系統的門面。再復雜的系統都可以為用戶提供一個
簡單的門面,Java Web 開發中作為前端控制器的 Servlet 或 Filter 不就是一個門面
嗎,瀏覽器對服務器的運作方式一無所知,但是通過前端控制器就能夠根據你的請求
得到相應的服務。調停者模式也可以舉一個簡單的例子來說明,例如一台計算機,CPU、
內存、硬盤、顯卡、聲卡各種設備需要相互配合才能很好的工作,但是如果這些東西
都直接連接到一起,計算機的布線將異常復雜,在這種情況下,主板作為一個調停者
的身份出現,它將各個設備連接在一起而不需要每個設備之間直接交換數據,這樣就
減小了系統的耦合度和復雜度。迪米特法則用通俗的話來將就是不要和陌生人打交道,
如果真的需要,找一個自己的朋友,讓他替你和陌生人打交道。) - Java 企業級開發中常用的設計模式有哪些?
答: 按照分層開發的觀點,可以將應用划分為:表示層、業務邏輯層和持久層,每一層
都有屬於自己類別的設計模式。
表示層設計模式:
- Interceptor Filter:攔截過濾器,提供請求預處理和后處理的方案,可以對請求和響
應進行過濾。 - Front Controller:通過中央控制器提供請求管理和處理,管理內容讀取、安全性、
視圖管理和導航等功能。Struts 2 中的 StrutsPrepareAndExecuteFilter、Spring MVC
中的 DispatcherServlet 都是前端控制器,后者如下圖所示: - View Helper:視圖幫助器,負責將顯示邏輯和業務邏輯分開。顯示的部分放在視圖
組件中,業務邏輯代碼放在幫助器中,典型的功能是內容讀取、驗證與適配。 - Composite View:復合視圖。
業務邏輯層設計模式: - Business Delegate:業務委托,減少表示層和業務邏輯層之間的耦合。
- Value Object:值對象,解決層之間交換數據的開銷問題。
- Session Façade:會話門面,隱藏業務邏輯組件的細節,集中工作流程。
- Value Object Assembler:靈活的組裝不同的值對象
- Value List Handler:提供執行查詢和處理結果的解決方案,還可以緩存查詢結果,
從而達到提升性能的目的。 - Service Locator:服務定位器,可以查找、創建和定位服務工廠,封裝其實現細節,
減少復雜性,提供單個控制點,通過緩存提高性能。
持久層設計模式:
Data Access Object:數據訪問對象,以面向對象的方式完成對數據的增刪改查。
【補充】如果想深入的了解 Java 企業級應用的設計模式和架構模式,可以參考這些書
籍: 《Pro Java EE Spring Patterns》、《POJO in Action》、《Patterns of Enterprise
Application Architecture》。
- 你在開發中都用到了那些設計模式?用在什么場合?
答:面試被問到關於設計模式的知識時,可以揀最常用的作答,例如:
- 工廠模式:工廠類可以根據條件生成不同的子類實例,這些子類有一個公共的抽象父
類並且實現了相同的方法,但是這些方法針對不同的數據進行了不同的操作(多態方法)。
當得到子類的實例后,開發人員可以調用基類中的方法而不必考慮到底返回的是哪一個
子類的實例。 - 代理模式:給一個對象提供一個代理對象,並由代理對象控制原對象的引用。實際開
發中,按照使用目的的不同,代理可以分為:遠程代理、虛擬代理、保護代理、Cache
代理、防火牆代理、同步化代理、智能引用代理。 - 適配器模式:把一個類的接口變換成客戶端所期待的另一種接口,從而使原本因接口
不匹配而無法在一起使用的類能夠一起工作。 - 模板方法模式:提供一個抽象類,將部分邏輯以具體方法或構造器的形式實現,然后
聲明一些抽象方法來迫使子類實現剩余的邏輯。不同的子類可以以不同的方式實現這些
抽象方法(多態實現),從而實現不同的業務邏輯。
除此之外,還可以講講上面提到的門面模式、橋梁模式、單例模式、裝潢模式(Collections
工具類里面的 synchronizedXXX 方法把一個線程不安全的容器變成線程安全容器就是
對裝潢模式的應用,而 Java IO 里面的過濾流(有的翻譯成處理流)也是應用裝潢模式
的經典例子)等,反正原則就是揀自己最熟悉的用得最多的作答,以免言多必失。
- 什么是設計模式,設計模式的作用。
設計模式是一套被反復使用的、多數人知曉、經過分類編目的優秀代碼設計經驗的總結。
特定環境下特定問題的處理方法。
1)重用設計和代碼 重用設計比重用代碼更有意義,自動帶來代碼重用
2)提高擴展性 大量使用面向接口編程,預留擴展插槽,新的功能或特性很容易加
入到系統中來
3)提高靈活性 通過組合提高靈活性,可允許代碼修改平穩發生,對一處修改不會
波及到其他模塊
4) 提高開發效率 正確使用設計模式,可以節省大量的時間 - 23 種經典設計模式都有哪些,如何分類。
- 寫出簡單工廠模式的示例代碼
package com.bjsxt;
public class SimpleFactory {
public static Product createProduct(String pname){
Product product=null;
面向對象設計原
則 •單一職責原則
•開閉原則
•里氏替代原則
•依賴注入原則
•接口分離原則
•迪米特原則
•組合/聚合復用原
則
創建型模式 5+1
•簡單工廠模式
•工廠方法模式
•抽象工廠模式
•創建者模式
•原型模式
•單例模式
結構型模式 7 •外觀模式
•適配器模式
•適配器模式
•代理模式
•裝飾模式
•橋接模式
•組合模式
•享元模式
行為型模式 11
•模板方法模式
•觀察者模式
•狀態模式
•策略模式
•職責鏈模式
•命令模式
•訪問者模式
•調停者模式
•備忘錄模式
•迭代器模式
•解釋器模式
if("p1".equals(pname)){
product = new Product();
}else if("p2".equals(pname)){
product = new Product();
}else if("pn".equals(pname)){
product = new Product();
}
return product; } }
基本原理:由一個工廠類根據傳入的參數(一般是字符串參數),動態決定應該創建哪一
個產品子類(這些產品子類繼承自同一個父類或接口)的實例,並以父類形式返回
優點:客戶端不負責對象的創建,而是由專門的工廠類完成;客戶端只負責對象的調用,
實現了創建和調用的分離,降低了客戶端代碼的難度;
缺點:如果增加和減少產品子類,需要修改簡單工廠類,違背了開閉原則;如果產品子
類過多,會導致工廠類非常的龐大,違反了高內聚原則,不利於后期維護 - 請對你所熟悉的一個設計模式進行介紹
分析:建議挑選有一定技術難度,並且在實際開發中應用較多的設計模式。可以挑選裝
飾模式和動態代理模式。此處挑選動態代理設計模式。
講解思路:生活案例引入、技術講解、優缺點分析、典型應用。
1、生活案例引入:送生日蛋糕:
MM 們要過生日了,怎么也得表示下吧。最起碼先送個蛋糕。蛋糕多種多樣了。巧克力,
冰淇淋,奶油等等。這都是基本的了,再加點額外的裝飾,如蛋糕里放點花、放賀卡、
放點干果吃着更香等等。
分析:
方案 1:如果采用繼承會造成大量的蛋糕子類
方案 2:蛋糕作為主體,花,賀卡,果仁等是裝飾者,需要時加到蛋糕上。要啥我就加
啥。
技術講解
裝飾模式(別名 Wrapper)是在不必改變原類文件和使用繼承的情況下,動態的擴展一
個對象的功能。它通過創建一個包裝對象,也就是裝飾來包裹真實對象,提供了比繼承
更具彈性的代替方案。
裝飾模式一般涉及到的角色
抽象構建角色(Component):給出一個抽象的接口,以規范准備接受附加責任的對象。
具體的構建角色(ConcreteComponent):定義一個將要接受附加責任的類。
抽象的裝飾角色 (Decorator):持有一個抽象構建(Component)角色的引用,並定義一個
與抽象構件一致的接口。
具體的裝飾角色(ConcreteDecorator):負責給構建對象“貼上”附加的責任。
3、優缺點分析
優點
1)Decorator 模式與繼承關系的目的都是要擴展對象的功能,但是 Decorato 更多
的靈活性。
2)把類中的裝飾功能從類中搬移出去,這樣可以簡化原有的類。有效地把類的核心
功能和裝飾功能區分開了。
3)通過使用不同的具體裝飾類以及這些裝飾類的排列組合,可創造出很多不同行為
的組合。
缺點
這種比繼承更加靈活機動的特性,也同時意味着更加多的復雜性。
裝飾模式會導致設計中出現許多小類,如果過度使用,會使程序變得很復雜。
符合的設計原則:
多用組合,少用繼承。利用繼承設計子類的行為是在編譯時靜態決定的,且所有的
子類都會繼承到相同的行為。如能夠利用組合擴展對象的行為,就可在運行時動態進行
擴展。
類應設計的對擴展開放,對修改關閉。
4、典型應用
java IO 中需要完成對不同輸入輸出源的操作,如果單純的使用繼承這一方式,無疑需要
很多的類。比如說,我們操作文件需要一個類,實現文件的字節讀取需要一個類,實現
文件的字符讀取又需要一個類....一次類推每個特定的操作都需要一個特定的類。這無疑
會導致大量的 IO 繼承類的出現。顯然對於編程是很不利的。而是用裝飾模式則可以很好
的解決這一問題,在裝飾模式中:節點流(如 FileInputStream)直接與輸入源交互,
之后通過過濾流(FilterInputStream)進行裝飾,這樣獲得的 io 對象便具有某幾個的功
能,很好的拓展了 IO 的功能。