设计模式面试题(爪哇程序员)
接口是什么?为什么要使用接口而不是直接使用具体类?
接口用于定义 API。它定义了类必须得遵循的规则。同时,它提供了一种抽象,因为客户端只 使用接口,这样可以有多重实现,如 List 接口,你可以使用可随机访问的ArrayList,也可以 使用方便插入和删除的 LinkedList。接口中不允许写代码,以此来保证抽象,但是 Java 8 中 你可以在接口声明静态的默认方法,这种方法是具体的。
设计模式六大原则?
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)
原则是尽量使用合成/聚合的方式,而不是使用继承
Java怎么实现单例模式?
- 懒汉式:懒加载,线程不安全
public class Singleton {
private static Singleton singleton;
private Singleton() { }
public static Singleton getInstance() {
if (singleton == null)
singleton = new Singleton();
return singleton;
}
}
- 懒汉式线程安全版:同步效率低
public class Singleton {
private static Singleton singleton;
private Singleton() {
}
public synchronized static Singleton getInstance() {
if (singleton == null) singleton = new Singleton();
return singleton;
}
}
- 饿汉式:
public class Singleton {
private static Singleton singleton = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return singleton;
}
}
- 饿汉式变种:
public class Singleton {
private static Singleton singleton;
static {
singleton = new Singleton();
}
private Singleton() {
}
public static Singleton getInstance() {
return singleton;
}
}
- 静态内部类方式:利用 JVM 的加载机制,当使用到 SingletonHolder 才会进行初始化。
public class Singleton {
private Singleton() {
}
private static class SingletonHolder {
private static final Singleton singleton = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.singleton;
}
}
- 枚举:
public enum Singletons {
INSTANCE; // 此处表示单例对象里面的各种方法
public void Method() {
}
}
- 双重校验锁:
public class Singleton {
private volatile static Singleton singleton;
private Singleton() {
}
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
设计模式的类型
根据设计模式的参考书 Design Patterns – Elements of Reusable Object-Oriented Software(中文译名:设计模式 – 可复用的面向对象软件元素) 中所提到的,总共有 23 种设 计模式。这些模式可以分为三大类:创建型模式(Creational Patterns)、结构型模式 (Structural Patterns)、行为型模式(Behavioral Patterns)。当然,我们还会讨论另一 类设计模式:J2EE 设计模式。
序 号 | 模式 & 描述 | 包括 |
---|---|---|
1 | 创建型模式 这些设计模 式提供了一种在创建对 象的同时隐藏创建逻辑 的方式,而不是使用 new 运算符直接实例化 对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。 | 工厂模式(Factory Pattern)抽象工厂模式(Abstract Factory Pattern)单例模式(Singleton Pattern)建造 者模式(Builder Pattern)原型模式(Prototype Pattern) |
2 | 结构型模式 这些设计模 式关注类和对象的组 合。继承的概念被用来 组合接口和定义组合对 象获得新功能的方式。 | 适配器模式(Adapter Pattern)桥接模式(Bridge Pattern)过滤器模式(Filter、Criteria Pattern)组合 模式(Composite Pattern)装饰器模式(Decorator Pattern)外观模式(Facade Pattern)享元模式 (Flyweight Pattern)代理模式(Proxy Pattern) |
3 | 行为型模式 这些设计模 式特别关注对象之间的 通信。 | 责任链模式(Chain of Responsibility Pattern)命令模 式(Command Pattern)解释器模式(Interpreter Pattern)迭代器模式(Iterator Pattern)中介者模式 (Mediator Pattern)备忘录模式(Memento Pattern)观察者模式(Observer Pattern)状态模式 (State Pattern)空对象模式(Null Object Pattern) 策略模式(Strategy Pattern)模板模式(Template Pattern)访问者模式(Visitor Pattern) |
4 | J2EE 模式 这些设计模 式特别关注表示层。这 些模式是由 Sun Java Center 鉴定的。 | MVC 模式(MVC Pattern)业务代表模式(Business Delegate Pattern)组合实体模式(Composite Entity Pattern)数据访问对象模式(Data Access Object Pattern)前端控制器模式(Front Controller Pattern)拦截过滤器模式(Intercepting Filter Pattern)服务定位器模式(Service Locator Pattern) 传输对象模式(Transfer Object Pattern) |
说说你所熟悉或听说过的 j2ee 中的几种常用模式?
IO 流的装饰器模式,Web 过滤器的责任链模式,Spring 的单例模式和工厂模式,
Spring 中根据不同配置方式进行初始化的策略模式
简述一下你了解的 Java 设计模式(总结)
标星号的为常用设计模式
★单例模式:保证某个类只能有一个唯一实例,并提供一个全局的访问点。
★简单工厂:一个工厂类根据传入的参数决定创建出那一种产品类的实例。
工厂方法:定义一个创建对象的接口,让子类决定实例化那个类。
抽象工厂:创建一组相关或依赖对象族,比如创建一组配套的汉堡可乐鸡翅。
★建造者模式:封装一个复杂对象的构建过程,并可以按步骤构造,最后再build。
★原型模式:通过复制现有的实例来创建新的实例,减少创建对象成本(字段需要复杂计算或者创 建成本高)。
★适配器模式:将一个类的方法接口转换成我们希望的另外一个接口。
★组合模式:将对象组合成树形结构以表示“部分-整体”的层次结构。(无限层级的知识点树)
★装饰模式:动态的给对象添加新的功能。
★代理模式:为对象提供一个代理以增强对象内的方法。
亨元(蝇量)模式:通过共享技术来有效的支持大量细粒度的对象(Integer中的少量缓存)。
★外观模式:对外提供一个统一的方法,来访问子系统中的一群接口。
桥接模式:将抽象部分和它的实现部分分离,使它们都可以独立的变化(比如插座和充电器,他们 之间相插是固定的, 但是至于插座是插在220V还是110V,充电器是充手机还是pad可以自主选择)。
★模板方法模式:定义一个算法步骤,每个小步骤由子类各自实现。
解释器模式:给定一个语言,定义它的文法的一种表示,并定义一个解释器。
★策略模式:定义一系列算法,把他们封装起来,并且使它们可以相互替换。
★状态模式:允许一个对象根据其内部状态改变而改变它的行为。
★观察者模式:被观测的对象发生改变时通知它的所有观察者。
备忘录模式:保存一个对象的某个状态,以便在适当的时候恢复对象。
中介者模式:许多对象利用中介者来进行交互,将网状的对象关系变为星状的(最少知识原则)。
命令模式:将命令请求封装为一个对象,可用于操作的撤销或重做。
访问者模式:某种物体的使用方式是不一样的,将不同的使用方式交给访问者,而不是给这个物 体。(例如对铜的使用,造币厂 造硬币。雕刻厂造铜像,不应该把造硬币和造铜像的功能交给铜自己实现,这样才能解耦)
★责任链模式:避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象 连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。
迭代器模式:一种遍历访问聚合对象中各个元素的方法,不暴露该对象的内部结构。
适配器模式是什么?什么时候使用?
适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式 属于结构型模式,它结合了两个独立接口的功能。适配器模式提供对接口的转换。如果你的客户端使用某些接口,但是你有另外一些接口,你就可以写一个适配去来连接这些接口。
适配器模式与装饰器模式有什么区别?
虽然适配器模式和装饰器模式的结构类似,但是每种模式的出现意图不同。适配器模式被用于 桥接两个接口,而装饰模式的目的是在不修改类的情况下给类增加新的功能。
装饰者模式:动态地将责任附加到对象上,若要扩展功能,装饰者模提供了比继承更有弹性的替代方案。
通俗的解释:装饰模式就是给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被 装饰对象实现同一个接口,装饰对象持有被装饰对象的实例。
适配器模式:将一个类的接口,转换成客户期望的另一个接口。适配器让原本接口不兼容的类 可以合作无间。
适配器模式有三种:类的适配器模式、对象的适配器模式、接口的适配器模式。 通俗的说法:适配器模式将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由 于接口不匹配所造成的类的兼容性问题。
举例如下:
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一行字符的功能。
适配器模式和代理模式之间有什么不同?
这个问题与前面的类似,适配器模式和代理模式的区别在于他们的意图不同。由于适配器模式 和代理模式都是封装真正执行动作的类,因此结构是一致的,但是适配器模式用于接口之间的 转换,而代理模式则是增加一个额外的中间层,以便支持分配、控制或智能访问。
OOP中的组合、聚合和关联有什么区别?
如果两个对象彼此有关系,就说他们是彼此相关联的。组合和聚合是面向对象中的两种形式的 关联。组合是一种比聚合更强力的关联。组合中,一个对象是另一个的拥有者,而聚合则是指 一个对象使用另一个对象。如果对象 A 是由对象 B 组合的,则 A 不存在的话,B一定不存在, 但是如果 A 对象聚合了一个对象 B,则即使 A 不存在了,B 也可以单独存在。
给我一个符合开闭原则的设计模式的例子?
开闭原则要求你的代码对扩展开放,对修改关闭。这个意思就是说,如果你想增加一个新的功 能,你可以很容易的在不改变已测试过的代码的前提下增加新的代码。有好几个设计模式是基 于开闭原则的,如策略模式,如果你需要一个新的策略,只需要实现接口,增加配置,不需要 改变核心逻辑。一个正在工作的例子是 Collections.sort() 方法,这就是基于策略模式,遵循 开闭原则的,你不需为新的对象修改 sort() 方法,你需要做的仅仅是实现你自己的 Comparator 接口。
工厂模式与抽象工厂模式的区别?
首先来看看这两者的定义区别:
工厂模式:定义一个用于创建对象的借口,让子类决定实例化哪一个类
抽象工厂模式:为创建一组相关或相互依赖的对象提供一个接口,而且无需指定他们的具体类
个人觉得这个区别在于产品,如果产品单一,最合适用工厂模式,但是如果有多个业务品 种、业务分类时,通过抽象工厂模式产生需要的对象是一种非常好的解决方式。再通俗深化理 解下:工厂模式针对的是一个产品等级结构 ,抽象工厂模式针对的是面向多个产品等级结构 的。
再来看看工厂方法模式与抽象工厂模式对比:
工厂方法模式 | 抽象工厂模式 |
---|---|
针对的是一个产品等级结构 | 针对的是面向多个产品等级结构 |
一个抽象产品类 | 多个抽象产品类 |
可以派生出多个具体产品类 | 每个抽象产品类可以派生出多个具体产品类 |
一个抽象工厂类,可以派生出多个具体工厂类 | 一个抽象工厂类,可以派生出多个具体工厂类 |
每个具体工厂类只能创建一个具体产品类的实例 | 每个具体工厂类可以创建多个具体产品类的实例 |
举出一个例子,在这种情况你会更倾向于使用抽象类,而不是接口?
这是很常用但又是很难回答的设计面试问题。接口和抽象类都遵循”面向接口而不是实现编码” 设计原则,它可以增加代码的灵活性,可以适应不断变化的需求。下面有几个点可以帮助你回 答这个问题:
-
在一些对时间要求比较高的应用中,倾向于使用抽象类,它会比接口稍快一点。
-
如果希望把一系列行为都规范在类继承层次内,并且可以更好地在同一个地方进行编 码,那么抽象类是一个更好的选择。有时,接口和抽象类可以一起使用,接口中定义函 数,而在抽象类中定义默认的实现。
Dubbo 源码使用了哪些设计模式?
责任链模式:责任链中的每个节点实现 Filter 接口,然后由 ProtocolFilterWrapper,将所有 Filter 串连起来。
Dubbo 的许多功能都是通过 Filter 扩展实现的,比如监控、日志、缓存、安全、telnet 以及 RPC 本身都是。
观察者模式:消费者在初始化的时候回调用 subscribe 方法,注册一个观察者,如果观察者引 用的服务地址列表发生改变,就会通过 NotifyListener 通知消费者。
装饰器模式:比如 ProtocolFilterWrapper 类是对 Protocol 类的修饰。
工厂模式:如 ExtenstionLoader.getExtenstionLoader(Protocol.class).getAdaptiveExtenstion()。
Spring 当中用到了哪些设计模式?
模板方法模式:例如 jdbcTemplate,通过封装固定的数据库访问比如获取 connection、获 取 statement,关闭 connection、关闭 statement 等 然后将特殊的 sql 操作交给用户自己实现。
策略模式:Spring 在初始化对象的时候,可以选择单例或者原型模式。
简单工厂:Spring 中的 BeanFactory 就是简单工厂模式的体现,根据传入一个唯一的标识来 获得 bean 对象。
工厂方法模式:一般情况下,应用程序有自己的工厂对象来创建 bean.如果将应用程序自己的工 厂对象交给 Spring 管理, 那么 Spring 管理的就不是普通的 bean,而是工厂 Bean。
单例模式:保证全局只有唯一一个对象。 适配器模式:SpringAOP 的 Advice 有如下:BeforeAdvice、AfterAdvice、AfterAdvice, 而需要将这些增强转为 aop 框架所需的 对应的拦截器 MethodBeforeAdviceInterceptor、AfterReturningAdviceInterceptor、 ThrowsAdviceInterceptor。
代理模式:Spring 的 Proxy 模式在 aop 中有体现,比如 JdkDynamicAopProxy 和 Cglib2AopProxy。
装饰者模式:如 HttpServletRequestWrapper,自定义请求包装器包装请求,将字符编码转 换的工作添加到 getParameter()方法中。
观察者模式:如启动初始化 Spring 时的 ApplicationListener 监听器。
877.请列举出在 JDK 中几个常用的设计模式?
单例模式(Singleton pattern)用于 Runtime,Calendar 和其他的一些类中。 工厂模式(Factory pattern)被用于各种不可变的类如 Boolean,像 Boolean.valueOf,观察者模式(Observer pattern)被用于 Swing 和很多的事件监听中。 装饰器设计模式(Decorator design pattern)被用于多个 Java IO 类中。
878.什么是设计模式?你是否在你的代码里面使用过任何设计模式?
设计模式是世界上各种各样程序员用来解决特定设计问题的尝试和测试的方法。设计模式是代码可用性的延伸
879.Java 中什么叫单例设计模式?请用 Java 写出线程安全的单例模式
单例模式重点在于在整个系统上共享一些创建时较耗资源的对象。整个应用中只维护一个特定类实例,它被所有组件共同使用。Java.lang.Runtime 是单例模式的经典例子。从 Java 5 开始你可以使用枚举(enum)来实现线程安全的单例。
880.在 Java 中,什么叫观察者设计模式(observer design pattern)?
观察者模式是基于对象的状态变化和观察者的通讯,以便他们作出相应的操作。简单的例子就是一个天气系统,当天气变化时必须在展示给公众的视图中进行反映。这个视图对象是一个主体,而不同的视图是观察者。
881.使用工厂模式最主要的好处是什么?在哪里使用?
工厂模式的最大好处是增加了创建对象时的封装层次。如果你使用工厂来创建对象,之后你可以使用更高级和更高性能的实现来替换原始的产品实现或类,这不需要在调用层做任何修改。
882.举一个用 Java 实现的装饰模式(decorator design pattern)?它是作用于对象层次还是类 层次?
装饰模式增加强了单个对象的能力。Java IO 到处都使用了装饰模式,典型例子就是Buffered 系列类如 BufferedReader 和 BufferedWriter,它们增强了 Reader 和 Writer 对象,以实现提升性能的 Buffer 层次的读取和写入。
883.在 Java 中,为什么不允许从静态方法中访问非静态变量?
Java 中不能从静态上下文访问非静态数据只是因为非静态变量是跟具体的对象实例关联的,而静态的却没有和任何实例关联。
884.设计一个 ATM 机,请说出你的设计思路?
比如设计金融系统来说,必须知道它们应该在任何情况下都能够正常工作。不管是断电还是其他情况,ATM 应该保持正确的状态(事务) , 想想 加锁(locking)、事务(transaction)、错误条件(error condition)、边界条件(boundary condition) 等等。尽管你不能想到具体的设计,但如果你可以指出非功能性需求,提出一些问题,想到关于边界条件,这些都会是很好的。
885.在 Java 中,什么时候用重载,什么时候用重写?
如果你看到一个类的不同实现有着不同的方式来做同一件事,那么就应该用重写(overriding),而重载(overloading)是用不同的输入做同一件事。在 Java 中,重载的方法签名不同,而重写并不是。
886.举例说明什么情况下会更倾向于使用抽象类而不是接口?
接口和抽象类都遵循”面向接口而不是实现编码”设计原则,它可以增加代码的灵活性,可以适应不断变化的需求。下面有几个点可以帮助你回答这个问题: 在 Java 中,你只能继承一个类,但可以实现多个接口。所以一旦你继承了一个类,你就失去了继承其他类的机会了。 接口通常被用来表示附属描述或行为如:Runnable、Clonable、Serializable 等等,因此当你使用抽象类来表示行为时,你的类就不能同时是 Runnable 和 Clonable(注:这里的意思是指如果把 Runnable 等实现为抽象类的情况),因为在 Java 中你不能继承两个类,但当你使用接口时,你的类就可以同时拥有多个不同的行为。 在一些对时间要求比较高的应用中,倾向于使用抽象类,它会比接口稍快一点。 如果希望把一系列行为都规范在类继承层次内,并且可以更好地在同一个地方进行编码,那么抽象类是一个更好的选择。有时,接口和抽象类可以一起使用,接口中定义函数,而在抽象类中定义默认的实现