设计模式是Java面试必考必问的,今天给大家总结19道常见的设计模式面试题及答案,希望对大家所有帮助@mikechen
什么是设计模式
设计模式是对面向对象设计中反复出现的问题的解决方案,由Erich Gamma等人从建筑设计领域引入到计算机科学中来的。
设计模式可以提升代码重用性、可读性、可扩展性、可靠性,还能降低系统中类与类之间的耦合度。
为什么要学设计模式?
设计模式的价值,主要分为如下4点:
1.代码重用性
相同功能的代码不用多次编写
2.可读性
设计模式使程序易读,编程规范性,便于其他程序员的阅读和理解
3.可扩展性
设计模式能使编写的程序具有良好的可扩展性,满足系统设计的开闭原则。比如策略模式,就是将不同的算法封装在子类中,在需要添加新的算法时,只需添加新的子类,实现规定的接口,即可在不改变现有系统源码的情况下加入新的系统行为。
4.可靠性
当我们增加新的功能后,对原来的功能没有影响
设计模式有哪些原则?
主要分为7大原则:
1.单一职责原则
对于类来说,一个类应该只负责一项职责
2.接口隔离原则
避免其实现类污染
3.依赖倒转(倒置)原则
1)高层模块不应该依赖底层模块,二者都应该依赖其抽象
2)抽象不应该依赖细节,细节应该依赖抽象
3)依赖关系传递的3中方式
- 接口传递
- 构造方法传递
- setter方法传递
4.里氏替换原则
子类尽量不要重写父类的方法
5.开闭原则
修改关闭,扩展开发
6.迪米特法则
一个对象应该对其他对象保持最少的了解
又叫最少知道原则,即一个类对自己依赖的类知道越少越好。
7.合成复用原则
尽量使用组合/聚合,不要用继承
设计模式有哪些种类?
主要分为三类:
1、创建型模式
用来描述 “如何创建对象”,它的主要特点是 “将对象的创建和使用分离”。
2、结构型模式
用来描述如何将类或对象按照某种布局组成更大的结构。
3、行为型模式
用来识别对象之间的常用交流模式以及如何分配职责。
创建型模式有哪些?
创建型模式,共五种:
1)工厂方法模式:定义一个用于创建对象的接口,让子类决定实例化哪一个类,也就是将创建工作推迟到子类来完成。
2)抽象工厂模式:提供一个创建一系列相关或者相互依赖对象的接口,而无需指定它们(指的是这些相关或者相互依赖的对象)的具体的类。
3)生成器模式:将一个复杂对象的构建和它的表示相分离,使得同样的构建过程可以创建出不同的表示。
4)原型模式:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
5)单例模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点。换句话来说就是,单例模式通过将单例类的构造器访问修饰符设置为private,禁止客户直接通过new的方式来实例化单例类;与此同时,单例类提供一个静态访问点来获取到通过内部创建出的唯一单例类对象。
结构型模式有哪些?
结构型模式共七种:
1.适配器模式:将一个类的接口转换成客户希望的另外一种接口,这样就能实现已有接口的复用。适配器主要有类适配器和对象适配器两种实现方式,通常情况下,推荐优先使用对象适配器方式。
2.桥接模式:将抽象部分与实现部分分离,使它们都可以独立地变化。它主要用于应对多维度变化点问题,通过对象组合的方式,可以极大地减少子类的数目,同时还能让不同维度独立扩展变化。
3.组合模式:将对象组合成树形结构以表示“整合-部分”的层次结构,从而使得用户对单个对象和组合对象的使用具有一致性,也就是客户端能够透明地无区别地操作两者。
4.装饰模式:动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式相比生成子类更为灵活。
5.外观模式:为子系统中的一组接口提供一个一致的接口,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。这样原来需要客户直接与复杂的子系统打交道、交互,现在这一过程将完全将交由外观对象来完成,极大地方便了客户端的调用。
6.享元模式:运用共享技术有效地支持大量细粒度的对象。享元模式关键是将对象的内部状态和外部状态分离,尽可能地对“稳定”的内部状态进行共享,而将会随运用场景而改变的状态通过外部状态传入。
7.代理模式:为其他对象提供一种代理以控制对这个对象的访问。主要是在客户端和目标对象间增加一层间接层,通过这个间接层来完成对目标对象的种种控制操作,所以也就形成了不同功能类型的代理呢,比如远程代理、保护代理和虚代理等等。
行为型模式有哪些?
行为型模式共十一种:
- 策略模式
- 模板方法模式
- 观察者模式
- 迭代子模式
- 责任链模式
- 命令模式
- 备忘录模式
- 状态模式
- 访问者模式
- 中介者模式
- 解释器模式
常用的设计模式有哪些?
开发中常用到的模式如下:
singleton单例模式,用来减少重复创建对象;
factory工厂模式,用来解耦;
iterator迭代器模式,用来遍历对象;
observer观察者模式,用来收发消息;
templete模板模式,用来避免执行相同的操作;
strategy策略模式,用来定义算法等。
什么是单例模式?
一个类只允许创建唯一一个对象(或者实例),那这个类就是一个单例类,这种设计模式就叫作单例设计模式,简称单例模式。
单例模式主要是为了避免因为创建了多个实例造成资源的浪费,且多个实例由于多次调用容易导致结果出现错误,而使用单例模式能够保证整个应用中有且只有一个实例。
单例模式的优缺点
优点:
1.由于系统中内存只存在一个对象,因此可以节约系统的的资源,对于一些频繁的创建和销毁的对象单例模式无意可以提供系统的性能;
2.避免对资源的多重占用;
3.提供了对唯一实例的受控访问;
缺点:
1.由于单利模式中没有抽象层,因此单例类的扩展有很大的困难;
2.不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误,不能保存彼此的状态;
3.单例类职责过重,在一定程度上违背了单一职责。
单例模式的实现方式
单例的实现方式主要分为以下几种:
1.饿汉式
/** * 单例模式饿汉式 * @author mikechen */ public class SingletonHungry { private static SingletonHungry instance = new SingletonHungry(); //构造方法设置成私有 private SingletonHungry() { } public static SingletonHungry getInstance() { return instance; } }
①特点:在类加载的同时就已经创建好对象,加载速度块
②优点:实现简单,执行效率高,线程安全
③缺点:类加载时就初始化,可能占用不必要内存浪费
2.懒汉式
/** * 懒加载实现单例模式 * * @author mikechen */ public class SingletonLazy { private static SingletonLazy instance; //构造方法设置成私有 private SingletonLazy() {} public static synchronized SingletonLazy getInstance() { if (instance == null) instance = new SingletonLazy(); return instance; } }
①特点:在使用时才进行初始化,即懒加载
②优点:只在第一次使用的时候调用,一定程度上节省了资源
③缺点:第一次加载需要进行及时实例化,反应稍慢,最大的问题是每次调用getInstance 都进行同步,造成不必要的同步开销
3.双重检测
/** * Double Check Lool (DCL)实现单例 * * @author mikechen */ public class SingletonDCL { private volatile static SingletonDCL instance = null; //构造方法私有 private SingletonDCL() { } public static SingletonDCL getInstance() { //进行两次非空判断 ,第一层是为了避免不必要的同步 if (instance == null) { //获取Singleton3.class的锁,避免实例化多次 synchronized (SingletonDCL.class) { if (instance == null) { instance = new SingletonDCL(); } } } return instance; } }
①特点:使用volatile关键字保证底层指令执行顺序
②优点:入口处判断null,可以省去每次加锁的耗费,提升性能
③缺点:第一次加载反应稍慢,也由于Java内存模型的原因偶尔会失败,在高并发环境下也有一定的缺陷
4.静态内部类
/** * 静态内部类实现单例模式 * * @author mikechen */ public class SingletonStatic { private SingletonStatic() { } public static SingletonStatic getInstance() { return SingLineHolder.instance; } private static class SingLineHolder { private static final SingletonStatic instance = new SingletonStatic(); } }
①在类内部有一个静态内部类
②优点:确保线程安全,也能够保证单例对象的唯一性,同时也延迟了单例的实例化
③缺点:无法传参
5.枚举
/** * 枚举实现单例模式 * * @author mikechen */ public enum SingletonEnum { INSTANCE; SingletonEnum() {} public void getName() {} }
最佳的单例实现模式就是枚举模式,利用枚举的特性,让JVM来帮我们保证线程安全和单一实例的问题。
除此之外,写法还特别简单,线程安全的同时,还有防止反序列化生成多个对象。
单例模式的应用场景
1.美国总统
生活中的例子,美国总统的职位是Singleton,在任何时刻只能由一个现任的总统。
2.连接池
比如数据库连接池的设计一般也是采用单例模式,数据库连接池属于重量级资源,一个应用中只需要保留一份即可,既节省了资源又方便管理,所以数据库连接池采用单例模式进行设计会是一个非常好的选择。
3.线程池
多线程的线程池的设计一般也是采用单例模式,这是由于线程池要方便对池中的线程进行控制。
4.资源文件
Windows的Task Manager(任务管理器)就是很典型的单例模式,想想看,是不是,你能打开两个任务管理器吗?
windows的Recycle Bin(回收站)也是典型的单例应用,在整个系统运行过程中,回收站一直维护着仅有的一个实例。
应用程序的日志应用,一般都何用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。
5.配置文件
Web应用的配置对象的读取,一般也应用单例模式,这个是由于配置文件是共享的资源。
6.计数器
网站的计数器,一般也是采用单例模式实现,否则难以同步。
什么是工厂方法模式?
工厂方法模式(英语:Factory method pattern),它属于类创建型模式,也被称为多态工厂模式,其定义了一个创建某种产品的接口,但由子类决定要实例化的产品是哪一个,从而把产品的实例化推迟到子类。
在工厂方法模式中,核心的工厂类不再负责所有的产品的创建,而是将具体创建的工作交给子类去做,这个核心类则摇身一变,成为了一个抽象工厂角色,仅负责给出具体工厂子类必须实现的接口,而不接触产品类被实例化的细节。
工厂方法模式的结构?
工厂方法模式包含如下4类角色:
1.Product:抽象产品
它是定义产品的接口,是工厂方法模式所创建对象的超类型,也就是产品对象的公共父类
2.ConcreteProduct:具体产品
这个角色实现了抽象产品角色所声明的接口,工厂方法模式所创建的每一个对象都是某个具体产品角色的实例。
3.Factory:抽象工厂
抽象工厂是工厂方法模式的核心,所有创建对象的工厂类都必须实现该接口。
4.ConcreteFactory:具体工厂
它是抽象工厂类的子类,实现了抽象工厂中定义的工厂方法,工厂方法模式所创建的每一个对象都是某个具体产品角色的实例。
工厂方法模式的优缺点?
1.优点
1)遵循开闭原则
无需更改现有客户端代码,新增一种产品时,只需要增加相应的具体产品类和相应的工厂子类即可,而简单工厂模式需要修改工厂类的判断逻辑。
2)遵循单一职责原则
每个具体工厂类只负责创建对应的产品,而简单工厂中的工厂类存在复杂的switch逻辑判断。
3)不使用静态工厂方法
不使用静态工厂方法,可以形成基于继承的等级结构,简单工厂模式的工厂类使用静态工厂方法。
2.缺点
每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。
工厂方法模式的应用场景
首先,工厂方法模式是new一个对象的替代品,所以在所有需要生成对象的地方都可以使用,但是需要慎重地考虑是否要增加一个工厂类进行管理,增加代码的复杂度。
其次,需要灵活的、可扩展的框架时,可以考虑采用工厂方法模式。
再次,当每个对象的创建逻辑都比较复杂的时候,为了避免设计一个过于庞大的简单工厂类时,将创建逻辑拆分得更细,每个对象的创建逻辑独立到各自的工厂类中。
什么是观察者模式?
观察者模式(Observer),又叫发布-订阅模式(Publish/Subscribe),定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并自动更新。
这种设计模式的思想应用比较广泛,比如在Redis的主从服务器中,当从服务器完成了各种步骤进入与主服务器命令传播的步骤之后,每次主服务器接收到一次外部命令写入,都会把该命令广播给所有的从服务器,背后的实现本质都是观察者的设计思路。
这种一对多的关系就是观察者模式,其中观察者是各个从服务器,被观察者是主服务器。
观察者模式的模型
观察者模式的主要角色如下:
1.抽象主题(Subject)角色
它是指被观察的对象,又称为主题,定义了一个观察者集合,一个观察目标可以接受任意数量的观察者来观察,它提供一系列方法来增加和删除观察者对象。
2.具体主题(Concrete Subject)角色
也叫具体目标类,它实现抽象目标中的通知方法,当具体主题的内部状态发生改变时,通知所有注册过的观察者对象。
3.抽象观察者(Observer)角色
它是一个抽象类或接口,它包含了一个更新自己的抽象方法,当接到具体主题的更改通知时被调用。
4.具体观察者(Concrete Observer)角色
实现抽象观察者中定义的抽象方法,以便在得到目标的更改通知时更新自身的状态。
什么是策略模式?
策略(Strategy)模式:该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。
策略是个形象的表述,所谓策略就是方案,日常生活中,要实现目标,有很多方案,每一个方案都被称之为一个策略。
策略模式的应用场景
- 多个类只有算法或行为上稍有不同的场景
- 算法需要自由切换的场景
- 需要屏蔽算法规则的场景
- 出行方式,自行车、汽车等,每一种出行方式都是一个策略
- 商场促销方式,打折、满减等
陈睿mikechen
10年+大厂架构经验,资深技术专家,就职于阿里巴巴、淘宝、百度等一线互联网大厂。
关注「mikechen」公众号,获取更多技术干货!
后台回复【面试】即可获取《史上最全阿里Java面试题总结》,后台回复【架构】,即可获取《阿里架构师进阶专题全部合集》