JAVA常见设计模式面试题
一、單例模式
java中單例模式是一種常見的設計模式,單例模式的寫法有好幾種,這里主要介紹三種:懶漢式單例、餓漢式單例、雙重檢查鎖定
1.單例模式有以下特點:
a、單例類只能有一個實例。
b、單例類必須自己創建自己的唯一實例。
c、單例類必須給所有其他對象提供這一實例。
2.代碼特點
a、私有靜態變量
b、私有構造方法
c、公有的靜態訪問方法
3.懶漢式
懶漢式非線程安全
4.餓漢式
//餓漢式單例類.在類初始化時,已經自行實例化 public class Singleton1 {private Singleton1() {}private static final Singleton1 single = new Singleton1();//靜態工廠方法 public static Singleton1 getInstance() {return single;}}餓漢式在類創建的同時就已經創建好一個靜態的對象供系統使用,以后不再改變,所以天生是線程安全的。
5.懶漢式和餓漢式區別
6.雙重檢查鎖定
7.應用場景
a、需要頻繁創建的一些類,使用單例可以降低系統的內存壓力,減少 GC。
a、某類只要求生成一個對象的時候,如一個班中的班長等。
b、某些類創建實例時占用資源較多,或實例化耗時較長,且經常使用。
c、某類需要頻繁實例化,而創建的對象又頻繁被銷毀的時候,如多線程的線程池、網絡連接池等。
e、頻繁訪問數據庫或文件的對象。
二、工廠模式
工廠模式是 Java 中最常用的設計模式之一,工廠模式模式的寫法有好幾種,這里主要介紹三種:簡單工廠模式、工廠模式、抽象工廠模式
1.簡單工廠模式
這里以制造coffee的例子開始工廠模式設計之旅。
我們知道coffee只是一種泛舉,在點購咖啡時需要指定具體的咖啡種類:美式咖啡、卡布奇諾、拿鐵等等。
/*** * 拿鐵、美式咖啡、卡布奇諾等均為咖啡家族的一種產品* 咖啡則作為一種抽象概念* @author Lsj**/ public abstract class Coffee {/*** 獲取coffee名稱* @return*/public abstract String getName();}/*** 美式咖啡* @author Lsj**/ public class Americano extends Coffee {@Overridepublic String getName() {return "美式咖啡";}}/*** 卡布奇諾* @author Lsj**/ public class Cappuccino extends Coffee {@Overridepublic String getName() {return "卡布奇諾";}}/*** 拿鐵* @author Lsj**/ public class Latte extends Coffee {@Overridepublic String getName() {return "拿鐵";}}2.工廠模式
定義了一個創建對象的接口,但由子類決定要實例化的類是哪一個,工廠方法讓類把實例化推遲到了子類。
3.抽象工廠
提供一個接口,用于創建相關或依賴對象的家族,而不需要明確指定具體類。
在上述的場景上繼續延伸:咖啡工廠做大做強,引入了新的飲品種類:茶、 碳酸飲料。中國工廠只能制造咖啡和茶,美國工廠只能制造咖啡和碳酸飲料。
如果用上述工廠方法方式,除去對應的產品實體類還需要新增2個抽象工廠(茶制造工廠、碳酸飲料制造工廠),4個具體工廠實現。隨著產品的增多,會導致類爆炸。
所以這里引出一個概念產品家族,在此例子中,不同的飲品就組成我們的飲品家族, 飲品家族開始承擔創建者的責任,負責制造不同的產品。
/*** 抽象的飲料產品家族制造工廠* @author Lsj**/ public interface AbstractDrinksFactory {/*** 制造咖啡* @return*/Coffee createCoffee();/*** 制造茶* @return*/Tea createTea();/*** 制造碳酸飲料* @return*/Sodas createSodas(); }/*** 中國飲品工廠* 制造咖啡與茶* @author Lsj**/ public class ChinaDrinksFactory implements AbstractDrinksFactory {@Overridepublic Coffee createCoffee() {// TODO Auto-generated method stubreturn new Latte();}@Overridepublic Tea createTea() {// TODO Auto-generated method stubreturn new MilkTea();}@Overridepublic Sodas createSodas() {// TODO Auto-generated method stubreturn null;}}/*** 美國飲品制造工廠* 制造咖啡和碳酸飲料* @author Lsj**/ public class AmericaDrinksFactory implements AbstractDrinksFactory {@Overridepublic Coffee createCoffee() {// TODO Auto-generated method stubreturn new Latte();}@Overridepublic Tea createTea() {// TODO Auto-generated method stubreturn null;}@Overridepublic Sodas createSodas() {// TODO Auto-generated method stubreturn new CocaCola();}}/*** 抽象工廠測試類* @author Lsj**/ public class AbstractFactoryTest {static void print(Drink drink){if(drink == null){System.out.println("產品:--" );}else{System.out.println("產品:" + drink.getName());}}public static void main(String[] args) {AbstractDrinksFactory chinaDrinksFactory = new ChinaDrinksFactory();Coffee coffee = chinaDrinksFactory.createCoffee();Tea tea = chinaDrinksFactory.createTea();Sodas sodas = chinaDrinksFactory.createSodas();System.out.println("中國飲品工廠有如下產品:");print(coffee);print(tea);print(sodas);AbstractDrinksFactory americaDrinksFactory = new AmericaDrinksFactory();coffee = americaDrinksFactory.createCoffee();tea = americaDrinksFactory.createTea();sodas = americaDrinksFactory.createSodas();System.out.println("美國飲品工廠有如下產品:");print(coffee);print(tea);print(sodas);}}4.總結
a、簡單工廠:不能算是真正意義上的設計模式,但可以將客戶程序從具體類解耦。
b、工廠方法:使用繼承,把對象的創建委托給子類,由子類來實現創建方法,可以看作是抽象工廠模式中只有單一產品的情況。
c、抽象工廠:使對象的創建被實現在工廠接口所暴露出來的方法中。
工廠模式可以幫助我們針對抽象/接口編程,而不是針對具體類編程,在不同的場景下按具體情況來使用。
轉載自:https://www.cnblogs.com/carryjack/p/7709861.html
三、代理模式
代理模式:即通過代理對象訪問目標對象,實現目標對象的方法。這樣做的好處是:可以在目標對象實現的基礎上,增強額外的功能操作,實現對目標功能的擴展。
這涉及到一個編程思想:不要隨意去修改別人已經寫好的代碼或者方法(有坑)。如果需要修改,可以通過代理模式實現。
代理模式通常有三種實現寫法:靜態代理、動態代理、Cglib代理
代理模式的UML圖
從UML圖中,可以看出代理類與真正實現的類都是繼承了抽象的主題類,這樣的好處在于代理類可以與實際的類有相同的方法,可以保證客戶端使用的透明性。
1.靜態代理
我們先看針對上面UML實現的例子,再看靜態代理的特點。
Subject接口的實現
實現了Subject接口的兩個類:
public class RealSubject implements Subject {private String name = "byhieg";@Overridepublic void visit() {System.out.println(name);} } public class ProxySubject implements Subject{private Subject subject;public ProxySubject(Subject subject) {this.subject = subject;}@Overridepublic void visit() {subject.visit();} }具體的調用如下:
public class Client {public static void main(String[] args) {ProxySubject subject = new ProxySubject(new RealSubject());subject.visit();} }通過上面的代理代碼,我們可以看出代理模式的特點,代理類接受一個Subject接口的對象,任何實現該接口的對象,都可以通過代理類進行代理,增加了通用性。但是也有缺點,每一個代理類都必須實現一遍委托類(也就是realsubject)的接口,如果接口增加方法,則代理類也必須跟著修改。其次,代理類每一個接口對象對應一個委托對象,如果委托對象非常多,則靜態代理類就非常臃腫,難以勝任。
2.動態代理
動態代理有別于靜態代理,是根據代理的對象,動態創建代理類。這樣,就可以避免靜態代理中代理類接口過多的問題。動態代理是實現方式,是通過反射來實現的,借助Java自帶的java.lang.reflect.Proxy,通過固定的規則生成。
其步驟如下:
編寫一個委托類的接口,即靜態代理的(Subject接口)
實現一個真正的委托類,即靜態代理的(RealSubject類)
創建一個動態代理類,實現InvocationHandler接口,并重寫該invoke方法
在測試類中,生成動態代理的對象。
第一二步驟,和靜態代理一樣,不過說了。第三步,代碼如下:
第四步,創建動態代理的對象
Subject realSubject = new RealSubject(); DynamicProxy proxy = new DynamicProxy(realSubject); ClassLoader classLoader = realSubject.getClass().getClassLoader(); Subject subject = (Subject) Proxy.newProxyInstance(classLoader, new Class[]{Subject.class}, proxy); subject.visit();創建動態代理的對象,需要借助Proxy.newProxyInstance。該方法的三個參數分別是:
ClassLoader loader表示當前使用到的appClassloader。 Class<?>[] interfaces表示目標對象實現的一組接口。 InvocationHandler h表示當前的InvocationHandler實現實例對象。jdk自帶動態代理
java.lang.reflect.Proxy
-作用:動態生成代理類和對象
java.lang.reflect.InvocationHandler(處理器接口)
- 可以通過invoke方法實現對真實角色的代理訪問
- 每次通過Proxy生成代理類對象時,都指定對對應的處理器對象
3.Cglib代理
要實現Cglib代理,必須引入cglib.jar 包,由于Spring-core包中已經包含了cglib功能,且大部分Java項目均引入了spring 相關jar包,這邊使用spring的cglib來講解。(他倆實現方式都是一樣的)
public class CglibProxy implements MethodInterceptor {//目標對象private Object obj;public CglibProxy(Object obj){this.obj=obj;}//給目標對象創建一個代理對象public Object getProxyInstance(){//1.工具類Enhancer en = new Enhancer();//2.設置父類en.setSuperclass(obj.getClass());//3.設置回調函數en.setCallback(this);//4.創建子類(代理對象)return en.create();}@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println("CglibProxy--------->");return method.invoke(obj,objects);} }說明:可以看出,Cglib代理模式實現不需要目標對象一定實現接口,故目標對象如果沒有實現接口,可以使用cglib代理模式。其實Spring的代理模式也是這么實現的。
總結
以上是生活随笔為你收集整理的JAVA常见设计模式面试题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 常用内存分配函数的说明
- 下一篇: 关于FCN的数据集着色说明