1、Java学习手册:Java基础知识点
2、Java学习手册:Java面向对象面试问题
3、Java学习手册:Java集合、泛型面试问题
4、Java学习手册:Java并发与多线程面试问题
5、Java学习手册:Java虚拟机面试问题
6、Java学习手册:Java IO面试问题
7、Java学习手册:Java反射机制面试问题
8、Java学习手册:Java网络编程面试问题
9、Java学习手册:Java异常面试问题
10、Java学习手册:Java设计模式面试问题
11、Java学习手册:Java数据库面试问题
设计模式的研究和应用是以依赖倒转原则为指导原则的。设计模式是面向对象设计中常见问题的类级与方法级的解决方案,设计模式是一种模式,在面向对象的语言中,它运用类与它们的方法来达到目标。设计模式是一套反复使用、为多数人知晓、经过分类编目的、代码设计经验的总结。使用设计模式的目的是为了代码复用,避免程序大量的修改,同时使代码易于理解,并且保证代码的可靠性。
本部分参考《Java设计模式(第二版)》,作者:Steven John Metsker 和 William C. Wake,书中一共描述了23种设计模式并将其分为5大类别,也就是GoF(Gang of Four)23种经典的设计结构。
设计模式分为3类,分别是:创建型设计模式、结构型设计模式、行为型设计模式。
创建型设计模式:与对象创建有关。包括单例模式,工厂方法模式,抽象工厂模式,建造者模式,原型模式。
结构型设计模式:结构性设计模式是从程序的结构上解决模块之间的耦合问题。包括适配器模式,代理模式,装饰模式,外观模式,桥接模式,组合模式和享元模式。
行为型设计模式:主要处理类或对象如何交互及如何分配职责。包括策略模式,模板方法模式,观察者模式,迭代器模式,责任链模式,命令模式,备忘录模式,状态模式,访问者模式,中介模式,解析器模式。
- | 创建型 | 结构型 | 行为型 |
---|---|---|---|
类 | Factory Method(工厂方法) | Adapter_Class(适配器类) | Interpreter(解释器)、Template Method(模板方法) |
对象 | Abstract Factory(抽象工厂)、Builder(构造者)、Prototype(原型)、Singleton(单例) | Adapter_Object(适配器对象)、Bridge(桥接)、Composite(组合)、Decorate(装饰)、Facade(外观)、Flyweight(享元)、Proxy(代理) | Chain of Responsibility(职责链)、Command(命令)、Iterator(迭代器)、Mediator(中介者)、Memento(备忘录)、Observer(观察者)、State(状态)、Strategy(策略)、Visitor(访问者模式) |
常见的设计模式有工厂模式(Factory Pattern)、单例模式(Singleton Pattern)、适配器模式(Adapter Pattern)、享元模式(Flyweight Pattern)以及观察者模式(Observer Pattern)。
适配器模式的意图在于,使用不同接口的类所提供的服务为客户端提供它所期望的接口。
适配器模式能够将已经实现的方法适配为客户端需要的方法。
适配器模式把一个类的接口转化为客户端所期望的另一种接口,从而使原本因接口不匹配而无法一起工作的两个类能够一起工作。适配器类可以根据所传递的参数返还一个合适的实例给客户端。
适配器模式主要适用于①希望可以复用一些现存的类,但是接口又与复用环境要求不一致“的情况,在遗留代码复用、类库迁移等方面非常有用。同时适配器有对象适配器和类适配器两种,但是类适配器采用”多继承“的实现方式,会引起程序的高耦合,所以一般不推荐使用;而对象适配器采用”对象组合“的方式,耦合度低,应用范围更广。② 两类所做的事情相同或相似,但是具有不同的接口。
解决的问题:原本由于接口不兼容而不能一起工作的那些类可以在一起工作
(1)类的适配器
一般情况下,创建C类,通过继承A类实现B接口,在C类中实现方法,将A类适配为满足B接口的需要。
类的适配器,通过子类进行适配,新的适配类实现了需要得接口,并继承自现有的类。而当需要适配的一组方法并非定义在接口中,这种方式就不奏效了。
(2)对象适配器
一般情况下,创建C类,通过继承A类满足A类的需要,然后再去使用B类的实例对象,将B类适配为符合A类的需要。
通过继承你所需要的类,可以创建一个对象适配器,利用现有类的实例对象,满足所需方法。
(3)区分
类的适配器继承自现有的类,同时实现目标接口;对象适配器继承自目标类,同时引用现有的类。
小结:
适配器模式使我们可以重用一个现有的类,以满足客户端的需要。
当客户端通过接口表达其需求时,通常可以创建一个实现了该接口的新类,同时使该类继承自现有类。这种方式即类的适配器,它能够将客户端的调用转换为对现有类方法的调用。
当客户端没有指定它所需要的接口时,就需要创建一个新的客户端子类,它将使用现有类的实例。这张方式通过创建一个对象适配器,将客户端的调用指向现有类的实例。如果我们不需要(或不能)重写客户端可能调用的方法时,这种方式可能存在一定的危险性。
优点:
① 通过适配器用户可以调用统一的接口;
②复用了现有的类,解决了现有类和复用环境要求不一致的情况;
③同一个适配器可以把适配者类和它的子类都适配到目标接口中;
缺点:
对于对象适配器来说,更换适配器实现过程比较复杂;
Java学习手册:单例模式——写出一个线程安全的单例类
单例模式的意图是为了确保一个类有且仅有一个实例,并为它提供一个全局访问点。
使用场景:(1) 保证一个类仅有一个实例,而且客户可以从一个众所周知的接口访问它时;(2)当这个唯一实例应该是通过子类化可扩展的类并且客户应该无需更改代码就能使用一个扩展的实例时。(3)单例对象通常作为程序中存放配置信息的载体,因为它能保证其他对象读到一致的信息。
实现方案:建立一个类,其构造方法是私有的,只有一个getInstance方法是public的,通过这个方法获取该类的唯一实例,即让类自身保存这个唯一的实例。
单例模式确保某一个类只有一个实例,而且自行实例化,并向整个系统提供这个实例单例模式。单例模式只应在有真正的”单一实例“需求时才可使用。
优点:(1)对唯一实例的受控访问;(2)缩小命名空间;(3) 允许操作和表示的精化;(4)允许可变数目的实例;(5)比类操作更灵活。
小结:
单例模式保证了类仅有一个实例,并为其提供了一个全局访问点。通过延迟初始化(),一个单例对象是达到此目的的通用做法。在多线程环境下,必须小心管理线程间的写作,因为它们访问单例对象方法与数据的时间,可能只有毫厘之差。
对象具有唯一性,并不意味着使用了单例模式。单例模式通常通过隐藏构造函数,提供对象创建的唯一入口点,从而将类的职责集中在类的单个实例中。
(1)实现单例,有哪些需要注意的地方?
针对普通方法:
观察者模式的意图是在多个对象之间定义一对多的依赖关系,当一个对象的状态发生改变时,会通知依赖于它的对象,并根据新状态做出相应的反应。
观察者模式(也被称为发布\订阅模式)提供了避免组件之间紧密耦合的另一种方法,它将观察者和被观察者对象分开。在这个模式中,一个对象提供一种方法允许其他对象通过注册这个方法来观察自己。当被观察者的对象更改时,它会将消息发送到已注册的观察者。这些观察者使用该信息执行的操作与可操作的对象无关,结果是对象可以相互对话,而不必了解原因。
例如,用户界面可以是观察者,业务数据是被观察者,用户界面观察业务数据的变化,发现数据变化时,就显示在界面上。
面向对象设计的一个原则是:系统中的每个类都将重点放在某一个功能上,而不在其他方面。一个对象只做一件事情,并且将它做好。观察者模式在模块之间划定了清晰的界限,提高了应用程序的可维护性和重用性。
使用场景:
①当一个对象的改变需要改变其他对象时,而且它不知道具体有多少个对象需要改变;
②一个抽象类型有两个方面,一个方面依赖于另一个方面,这是用观察者模式可以将这两两封装在独立的对象中使它们各自独立地改变和使用。
优点:
①观察者模式接触了主体和具体观察者之间的耦合,让耦合的双方都依赖于抽象模式,而不是依赖于具体。从而使得各自的变化都不会影响另一边的变化。
②实现了表示层和梳理逻辑的分离,并定义了稳定的更新消息传递机制,类别清晰,并抽象了更新接口,使得可以有各种各样不同的表示层(表示层)。
注:(面试)
面试官:设计一个观察者模式?
我:采用Map数据结构来设计观察者模式
面试官:那你如何保证线程安全
我:Map集合中有线程安全的HashTable,可以采用HashTable,此外在响应数据的时候,可以对相应的代码块用Sychronized修饰。
职责链模式的目的是通过给予多个对象处理请求的机会,以解除请求的发送者与接收者之间的耦合。
(1)现实中的职责链模式
当一个人负责某项任务时,可以选择自己做或者是让别人做,这就是现实中的职责链模式。
职责链可以帮助我们简化客户端代码,尤其当客户端代码不清楚对象组中哪个对象负责处理查询请求时。如果事先没有建立职责链模式,也需要借助其他方法来简化之前复杂的设计。
(2)重构为职责链模式
如果发现客户端代码在发出调用请求前对调用做了判断,就应该通过代码重构来改善代码设计。
职责链模式的意图在于减轻调用者的压力,使它们无须了解哪个对象可以处理调用请求。
(3)固定职责链
通过固定职责链,我们的对象模型变得更加健壮,代码也更为简洁。
(4)没有组合结构的职责链模式
职责链模式也可以用于不带组合结构的对象模型。
小结:
在运用职责链模式时,客户端不必事先知道对象集合中哪个对象可提供自己需要的服务。当客户端发出调用请求后,该请求会沿着职责链转发请求,直到找到提供该服务的对象为止。这就可以降低客户端与提供服务对象之间的耦合度。
构建者模式的意图是将类的构建逻辑转移到类的实例化外部。
(1)常规的构建者
引入构建者模式,就可以提供一个中间对象来存储数据,直到准备好所有数据后,再根据对象的数据来构造目标对象。
(2)在约束条件下的构建者
在面对不同的错误参数时,构建者对象会进行不同的错误处理。
(3)可容错的构建者
优点:
1、构建复杂对象
2、解耦构造方法与属性
3、更灵活的属性设置方法、易于扩展
缺点:
1、会产生多余的builder对象,对内存造成负担
小结:
构建者模式将复杂对象的构建逻辑从对象本身抽离了出来,这样能够简化复杂的对象。构建者关注目标类的构建过程,目标类关注合法实例的业务本身。这样做的好处是在实例化目标类前,确保得到的是一个有效的对象,并且不会让构建逻辑出现在目标类本身。构建者构建类对象的过程通常是分步骤地,这使得该模式通常被应用于解析文本以创建对象的场景。
工厂方法模式的意图是定义一个用于创建对象的接口,并控制返回哪个类的实例。
工厂模式专门负责实例化有大量公共接口的类。工厂模式可以动态地决定将哪一个类实例化,而不必事先知道每次都需要实例化哪个类。客户类和工厂类是分开的。消费者无论什么时候需要某种产品,需要做的只是向工厂提出请求即可。消费者无需修改就可以接纳新产品。当然也有缺点,就是当产品修改时,工厂类也要做相应的修改。
工厂模式主要是为创建对象提供过渡接口,以便将创建对象的具体过程屏蔽并隔离起来,达到提高灵活性的目的。
工厂模式可分为三类,并且从上到下逐步抽象,更具一般性。
①简单工厂模式(SImple Factory):由⼀个具体的类去创建其他类的实例,⽗类是相同的,⽗类是具体的。不利于产生系列产品。
②工厂方法模式(Factory Method):又称多形性工厂,有⼀个抽象的⽗类定义公共接⼝,⼦类负责⽣成具体的对象,这样做的⽬的是将类的实例化操作延迟到⼦类中完成。
③抽象工厂模式(Abstract Factory):又称工具箱,产生产品族,但不利于产生新的产品。提供⼀个创建⼀系列相关或相互依赖对象的接⼝,⽽⽆须指定他们具体的类。它针对的是有多个产品的等级结构。而工厂方法模式针对的是⼀个产品的等级结构。
工厂模式分为简单工厂模式、工厂方法模式、抽象工厂模式。
(1) 简单工厂模式(静态工厂方法模式)
简单工厂模式的工厂类是根据提供给它的参数,返回的是几个可能产品中的一个类的实例,一般情况下,它返回的类都有一个公共的父类和公共的方法。
简单工厂模式涉及到工厂角色、抽象产品角色以及具体产品角色等三个角色:
工厂类:担任这个角色的是工厂方法模式的核心,含有与应用紧密相连的商业逻辑。工厂类在客户端的直接调用下创建产品对象。
抽象产品:有工厂模式所创建的对象的父类,或创建对象共同拥有的接口。当模式产生的具体产品之间有逻辑关系的时候可以把共同的部分抽象为父类,当模式产生的具体产品之间没有逻辑关系的时候可以定义一个接口来担任抽象产品的这个角色。
具体产品:工厂方法模式所创建的任何对象都是这个角色的实例。
优点:实现了对责任的分割,按需创建合适的实例对象。工厂方法模式将实例化哪些对象以及如何实例化这些对象的细节隐藏起来,使得获取对象实例很方便。
缺点:将对时机的判断和对哪一种具体产品的判断逻辑混合在一起,使得系统将来进行功能扩展的时候较为困难(部分支持“开—闭原则”)。
在Java中的应用:DataFormat类实现了简单工厂模式。
(2) 工厂方法模式
工厂方法模式是类的创建模式,其用意是定义一个用于创建产品对象的工厂的接口,而将实际创建工作推迟到工厂接口的子类中。属于简单工厂模式的进一步抽象和推广。
工厂方法模式有抽象工厂角色、具体工厂角色、抽象产品角色、具体产品角色。
抽象工厂角色:与应用程序无关。任何在模式中创建对象的工厂类都必须实现这个接口。
具体工厂角色:与应用程序无关,实现了抽象工厂角色,并且受到应用程序的调用用以创建产品对象。
抽象产品角色:工厂方法模式所创建的对象的超类型。
具体产品角色:实现了抽象产品角色所声明的接口。工厂方法模式所创建的每一个对象都是这个具体产品角色的实例。
//工厂方法模式的实现
//抽象产品角色
public interface Moveable{
viod run();
}
//具体产品角色
public class Plane implements Moveable{
@Override
public void run(){
System.out.println("Plane...");
}
}
//抽象工厂角色
public abstract class VehicleFactory{
abstract Moveable create();
}
//具体工厂角色
public class PlaneFactory extends VehicleFactory{
public Moveable create(){
return new Plane();
}
}
与简单工厂模式相比工厂方法模式把具体创建那个产品和如何创建产品的行为交给具体工厂类去实现,这样就完全支持“开闭原则”了。当增加新产品时只需要添加一个继承自抽象产品的具体产品类和一个继承自抽象工厂类的具体工厂类就可以了。
可以看出⼯⼚⽅法的加⼊,使得对象的数量成倍增⻓。当产品种类⾮常多时,会出现⼤量的与之对应的⼯⼚对象,这不是我们所希望的。因为如果不能避免这种情况,可以考虑使⽤简单⼯⼚模式与⼯⼚⽅法模式相结合的⽅式来减少⼯⼚类:即对于产品树上类似的种类(⼀般是树的叶⼦中互为兄弟的)使⽤简单⼯⼚模式来实现。
(3) 抽象工厂模式
抽象工厂模式是所有形态的工厂模式中最为抽象和最具一般性的一种形态。抽象工厂模式是指当有多个抽象角色时使用的一种工厂模式,抽象工厂模式可以向客户端提供一个接口,是客户端在不必指定产品的具体的情况下,创建多个产品族中的产品对象。
抽象工厂角色:担任这个角色的是工厂方法模式的核心,与应用系统的商业逻辑无关,通常使用Java接口或抽象类。
具体工厂类角色:这角色直接在客户端的调用下创建产品的实例。这个产品含有选择合适的产品对象的逻辑,而这个逻辑是与应用系统的商业逻辑紧密相关的。通常使用Java类。
抽象产品角色:担任这个角色的类是工厂方法模式所创建的对象的父类,或它们共同拥有的接口。通常使用Java接口或抽象类。
具体产品角色:抽象工厂模式所创建的任何产品对象都是某一个具体产品类的实例。这是客户端最终需要的东西,其内部一定充满了应用系统的商业逻辑。
//抽象工厂模式的实现
//抽象工厂类
public abstract class AbstractFactory{
public abstract Vehicle createVehicle();
public abstract Weapon createWeapon();
public abstract Food createFood();
}
//具体工厂类,其中Food、Vehicle、Weapon是抽象类
public class DefaultFactory extends AbstractFactory{
@Override
public Food createFood(){
return new Apple();
}
@Override
public Vehicle createVehicle(){
return new Car();
}
@Override
public Weapon createWeapon(){
return new AK47();
}
}
//测试类
public class Test{
public static void main(String[] args){
AbstractFactory f = new DefaultFactory();
Vehicle v = f.createVehicle();
v.run();
Weapon w = f.createWeapon();
w.shoot();
Food a = f.createFood();
a.printName();
}
}
抽象工厂模式的适用场景:
① 一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节;
② 系统的产品有多于一个的产品族(所谓产品族指的是位于不同产品等级结构中,功能相关联的产品组成的家族),而系统只消费其中某一族的产品;
③ 同属于同一个产品族的产品是在一起使用的。
例子:对银行创建账户这个例子来说,客户对银行创建账户的具体过程并不需要了解,银行只需要按照客户的要求生成相应的账户即可。
一个类做一件事情,避免职责过多。比如这种情况是不太好的,在一个Activity中既有bean文件,又有http请求,还有adapter等等,这就导致我们需要修改任何一个东西的时候都会导致Activity的改变,这样一来就有多个引起它变化的原因,不符合单一职责原则
对于扩展是开放的,对于修改是封闭的。尽量做到面对需求的改变时,我们的代码能保持相对稳定,通过扩展的方式应对变化,而不是修改原有代码实现
里氏替换原则是实现开放封闭原则的重要方式之一,我们知道,使用基类的地方都可以使用子类去实现,因为子类拥有基类的所有方法,所以在程序设计中尽量使用基类类型对对象进行定义,在运行时确定子类类型。
依赖倒置原则针对的是模块之间的依赖关系,高层模块指调用端,底层模块指具体的实现类,抽象指接口或抽象类,细节就是实现类。该原则的具体表现就是模块间的依赖通过抽象发生,直线类之间不发生直接依赖关系,依赖通过接口或抽象类产生,降低耦合,比如MVP模式下,View层和P层通过接口产生依赖关系
迪米特原则要求我们在设计系统时,尽量减少对象之间的交互
接口隔离原则的关键是接口以及这个接口要小,如何小呢,也就是我们要为专门的类创建专门的接口,这个接口只对它有效,不要试图让一个接口包罗万象,要建立最小的依赖关系
1、诺瓦科技2020届校园招聘——请写出一个线程安全的单例类
2、请列举出在JDK中几个常用的设计模式?
单例模式(Singleton pattern)用于Runtime,Calendar和其他的一些类中。工厂模式(Factory pattern)被用于各种不可变的类如 Boolean,像Boolean.valueOf,观察者模式(Observer pattern)被用于 Swing 和很多的事件监听中。装饰器设计模式(Decorator design pattern)被用于多个 Java IO 类中。
3、什么是设计模式?你是否在你的代码里面使用过任何设计模式?
设计模式是世界上各种各样程序员用来解决特定设计问题的尝试和测试的方法。设计模式是代码可用性的延伸。
4、Java 中什么叫单例设计模式?请用Java 写出线程安全的单例模式
单例模式重点在于在整个系统上共享一些创建时较耗资源的对象。整个应用中只维护一个特定类实例,它被所有组件共同使用。Java.lang.Runtime是单例模式的经典例子。从 Java 5 开始你可以使用枚举(enum)来实现线程安全的单例。
5、在 Java 中,什么叫观察者设计模式(observer design pattern)?
观察者模式是基于对象的状态变化和观察者的通讯,以便他们作出相应的操作。简单的例子就是一个天气系统,当天气变化时必须在展示给公众的视图中进行反映。这个视图对象是一个主体,而不同的视图是观察者。
6、使用工厂模式最主要的好处是什么?在哪里使用?
工厂模式的最大好处是增加了创建对象时的封装层次。如果你使用工厂来创建对象,之后你可以使用更高级和更高性能的实现来替换原始的产品实现或类,这不需要在调用层做任何修改。
7、举一个用 Java 实现的装饰模式(decorator design pattern)?它是作用于对象层次还是类层次?
装饰模式增加强了单个对象的能力。Java IO 到处都使用了装饰模式,典型例子就是 Buffered 系列类如BufferedReader和BufferedWriter,它们增强了Reader和Writer对象,以实现提升性能的 Buffer 层次的读取和写入。