1.设计模式的六大原则
(1)开闭原则(Open Close
Principle)
对扩展开放,对修改关闭。
(2)里氏代换原则(Liskov Substitution Principle)
里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。子类可以扩展父类的功能,但不能改变父类原有的功能。类B继承类A时,除添加新方法完成新功能外,尽量不要重写父类A的方法,也尽量不要重载父类A的方法。
(3)依赖倒转原则(Dependence
Inversion Principle)
这个原则是开闭原则的基础,具体内容:针对接口编程,依赖于抽象而不依赖于具体。
(4)接口隔离原则(Interface Segregation Principle)
使用多个隔离的接口,比使用单个接口要好。它还有另外一个意思是:降低类之间的耦合度。
(5)迪米特法则,又称最少知道原则(Demeter
Principle)
一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立。
(6)合成复用原则(Composite Reuse Principle)
尽量使用合成/聚合的方式,而不是使用继承。
2.单例模式(singleton)
一个类只能有一个实例。
(1)懒汉式,线程不安全
publicclass Singleton {
private staticSingleton instance;
privateSingleton (){}
public staticSingleton getInstance(){
if (instance == null) {
instance = new Singleton();
}
returninstance;
}
}
接下来介绍的几种实现方式都支持多线程,但是在性能上有所差异。
(2)懒汉式,线程安全
效率很低,99%情况下不需要同步。第一次调用才初始化,避免内存浪费。
publicclass Singleton {
private staticSingleton instance;
privateSingleton (){}
public staticsynchronized SingletongetInstance() {
if(instance == null) {
instance= new Singleton();
}
returninstance;
}
}
(3)饿汉式
优点:没有加锁,执行效率会提高。
缺点:类加载时就初始化,浪费内存。
publicclass Singleton {
private staticSingleton instance = newSingleton();
privateSingleton (){}
public staticSingleton getInstance(){
return instance;
}
}
(4)双检锁/双重校验锁(DCL,即double-checked locking)
描述:这种方式采用双锁机制,安全且在多线程情况下能保持高性能。
public
classSingleton{
privatevolatilestaticSingletonsingleton;
privateSingleton(){}
publicstaticSingletongetInstance(){
if(singleton== null){
synchronized (Singleton.class){
if(singleton== null){
singleton= newSingleton();
}
}
}
returnsingleton;
}
}
(5)静态内部类
描述:这种方式是Singleton类被装载了,instance不一定被初始化。因为SingletonHolder类没有被主动使用,只有显示通过调用getInstance方法时,才会显示装SingletonHolder类,从而实例化instance。
publicclassSingleton {
privateSingleton() {}
privatestaticclassSingletonHolder {
privatestaticSingletonINSTANCE=newSingleton();
}
publicstaticSingleton getInstance() {
returnSingletonHolder.INSTANCE;
}
}
(6)枚举
描述:这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。这种方式是Effective
Java作者Josh Bloch提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。不过,由于JDK1.5之后才加入enum特性,用这种方式写不免让人感觉生疏。
publicenumSingleton{
INSTANCE;
public void whateverMethod() {
}
}
3.观察者模式
观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
Java内置的观察者模式是通过继承父类实现观察者模式的几个主要函数:
Observerable(可被观察的)是一个父类(class)
ØaddObserver()添加观察者
ØdeleteObserver()删除观察者
ØnotifyObservers()通知观察者
ØsetChanged()确认更改
Observer(观察者)是一个接口(interface)
Øupdate()更新观察者数据
ØsetChanged()->notifyObservers()必须要先使用setChanged(),再使用notifyObservers(),即先确认提交,再通知观察者。
观察者的更新接口update(Observerable
o, Object arg),即可以使用推(push)也可以使用拉(pull);如果notifyObservers(arg)传递参数,则为推的方法,如果没有参数,则为拉的方式,即使用get()方法获取。
缺点:java.util.Observable是一个类而不是接口。
推和拉模式的区别
1)推模式主要是被观察者行为状态改变的时候,推送给观察者,观察者直接拿到相应的信息进行处理。
2)拉模型前半部分和推模型一样,也是通过推送的方式把相应的信息传递给观察者。和推模型不同的是:
Ø推模型传递的是被观察者的一小部分的公开信息,而拉模型是将被观察者本身作为参数传递给观察者,也就是将被观察者所有的公开信息都暴露给了观察者。
Ø当观察者拿到被观察者实例时,自己根据所需获取自己感兴趣的那部分数据,按需获取。
4.代理模式
代理模式作用:为其他对象提供一种代理以控制对这个对象的访问。
代理模式的动机简单点说就是,有时不方便或者不能直接访问一个对象时,可以通过一个代理对象来实现间接访问。在现实中有很多类似的例子:不想走那么远去火车站买票,就去近一点的代售点买票。不方便约明星谈合作,就和明星的经纪人谈。没有带现金,于是用手机微信作为代理买单。
结构
代理模式主要包括三个角色:
ØSubject,对象的接口
ØRealSubject,实际对象
ØProxySubject,代理对象,持有实际对象的引用
静态代理
代理对象与实际对象实现相同的接口;代理对象持有实际对象的引用,调用代理对象方法最终会调用实际对象的实现。
动态代理
动态代理是指在运行时动态生成代理类。
动态代理的优势在于可以很方便的对代理类的方法进行统一的处理,而不用修改每个代理类的方法。
Java动态代理机制的优缺点
优点:
Ø解耦代理类与实际类,InvocationHandler中传入的实际对象是不确定的,代理类不知道代理的具体是哪个对象。
Ø减少代码量,代理多个接口时,InvocationHandler实现可以不变。
Ø可以实现AOP
缺点:
Ø生成代理对象时使用了反射,性能有所下降。
ØJava动态代理只能根据接口创建,不能根据类或抽象类创建。
JDK中生成代理对象的API
java.lang.reflect.Proxy:动态代理机制的主类,提供一组静态方法为一组接口动态的生成对象和代理类。
java.lang.reflect.InvocationHandler:调用处理器接口,自定义invoke方法,用于实现对于真正委托类的代理访问。
每一个动态代理类都必须要实现InvocationHandler这个接口。代理类所在包:java.lang.reflect.Proxy。JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接收三个参数:
newProxyInstance(ClassLoaderloader, Class[] interfaces,InvocationHandler h )
注意该方法是在Proxy类中是静态方法,且接收的三个参数依次为:
ØClassLoader
loader:指定当前目标对象使用类加载器,获取加载器的方法是固定的;
ØClass[]
interfaces:目标对象实现的接口的类型,使用泛型方式确认类型;
ØInvocationHandler
h:事件处理执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入。
interface UserService{
public String getName(int id);
public Integer getAge(int id);
}
class UserServiceImplimplements UserService {
@Override
public String getName(int id) {
System.out.println("------getName------");
return "Tom";
}
@Override
public Integer getAge(int id) {
System.out.println("------getAge------");
return 10;
}
}
classMyInvocationHandler implements InvocationHandler {
private Object target;
MyInvocationHandler() {
super();
}
MyInvocationHandler(Object target) {
super();
this.target = target;
}
@Override
public Objectinvoke(Object o, Method method, Object[] args)throws Throwable{
if("getName".equals(method.getName())){
System.out.println("++++++before " + method.getName() +"++++++");
Object result =method.invoke(target, args);
System.out.println("++++++after " + method.getName() +"++++++");
return result;
}else{
Object result =method.invoke(target, args);
return result;
}
}
}
public class Test{
public static void main(String[] args){
UserService userService = newUserServiceImpl();
InvocationHandler invocationHandler =new MyInvocationHandler(userService);
UserService userServiceProxy =(UserService)Proxy.newProxyInstance(userService.getClass().getClassLoader(),
userService.getClass().getInterfaces(), invocationHandler);
System.out.println(userServiceProxy.getName(1));
System.out.println(userServiceProxy.getAge(1));
}
}
Cglib代理
静态代理和动态代理模式都是要求目标对象实现一个接口,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这时候就可以使用以目标对象子类的方式类实现代理,这种方法就叫做Cglib代理。
Cglib代理也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展。JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口,如果想代理没有实现接口的类就可以使用Cglib实现。Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展Java类与实现Java接口,底层采用asm字节码生成框架生成代理类的字节码。它被许多AOP的框架使用。
注意:由于CGLib采用动态创建子类的方式生成代理对象,所以不能对目标类中的final方法进行代理。
5.工厂模式
所有的工厂都是用来封装对象的创建。
(1)简单工厂模式:可以根据传入的参数的不同,返回不同的对象。又称静态工厂方法,违背了开闭原则。
缺点:
a.如果需要新添加条件,那么只能加if-else或者switch加case。这就是硬编码,也违反了开闭原则。
b.条件过多,那么就不利于维护。所以使用在条件不多的情况下。
(2)工厂方法模式:工厂方法模式即是对简单工厂模式的改进。我们首先需要定义一个抽象的工厂类,然后会有N多个具体工厂类来继承抽象工厂类,这些具体的工厂类负责生成具体的对象。
那么,这样做有什么好处呢?答:这样当我们引进新的类型的时候,就不需要修改原有的东西,我们只需要添加新的具体工厂类,继承于抽象工厂类即可。这样做也更符合开闭原则。
缺点:类的个数增多。优点:符合开闭,容易扩展。
(3)抽象工厂模式:提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。(超级工厂,工厂的工厂)
工厂方法和抽象工厂的区别:工厂方法只有一个抽象产品类,而抽象工厂模式有多个。
6.java io用到何种设计模式(装饰器模式)
装饰器模式允许向一个现有的对象添加新的功能,同时又不改变其结构。装饰器模式是一种可以非常灵活的动态扩展类功能的设计模式,它采用组合的方式取代继承,使得各个功能的扩展更加独立和灵活。
装饰器模式的基本角色:
被装饰类接口:Component
具体被装饰类:ConcreteComponent
装饰器抽象类:Decorator,这个类可有可无具体看情况。
具体的装饰器类:ConcreteDecorator,具体装饰器类实现真正的动态添加类的功能的作用。
关键在于抽象装饰器类Decorator中的维护了一个Component对象。这样,在客户端创建的时候,就可以把被装饰者传入到装饰者中,装饰者就有机会对目标进行装饰,也就是扩展功能。
7.适配器模式(Adapter Pattern)
适配器模式把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。
最基本的适配器模型的UML图如下:
Client:客户端,需要调用request方法实现自己的功能
Target:接口。
Adapter:适配器类。实现Target接口,但在实现中,转而调用Adaptee中方法。
适配器本质:转换匹配,复用功能。
何时使用适配器模式:
1)如果你想要使用一个已经存在的类,但是它的接口不符合你的需求,这种情况可以使用适配器模式,来把已有的实现转化成你需要的接口。
2)如果你想创建一个可以复用的类,这个类可能和一些不兼容的类一起工作,这种情况下可以使用适配器模式,需要什么适配什么。
从定义上看装饰模式是对核心对象或者功能的扩展,适配器模式是把对象或者功能放到一个新对象中引用。举个例子,现在书城卖道德经的书,有线装版,有精装版,有日文版,有英文版,其中线装版和精装版就是装饰模式,日文版和英文版就是适配器模式。
适配器模式的特点在于兼容;装饰器模式特点在于增强。
8.设计模式在JDK中的应用
Singleton(单例模式)
作用:保证类只有一个实例;提供一个全局访问点
JDK中体现:
(1)Runtime(Java运行时环境,采用饿汉式)
建造者模式
主要用来简化一个复杂对象的构建。作用:
(1)将构造逻辑提到单独的类中
(2)分离类的构造逻辑和表现
JDK中体现
1)StringBuilder和StringBuffer的append()方法使用了建造者模式。
StringBuilder把构建者的角色交给了其父类AbstractStringBuilder
2)《Effective Java》item2:遇到多个构造器参数时要考虑用Builder
Prototype(原型)
作用:通过创建的方法返回一个包含相同属性的不同实例
(1)复制对象
(2)浅复制、深复制
JDK中体现:Object.clone;Cloneable
Adapter(适配器)
作用:使不兼容的接口相容
JDK中体现:
(1)java.io.InputStreamReader(InputStream)
(2)java.io.OutputStreamWriter(OutputStream)
(3)java.util.Arrays#asList()
Decorator(装饰器)
作用:为类添加新的功能;防止类继承带来的爆炸式增长
JDK中体现:
(1)java.io包
(2)java.util.Collections#synchronizedList(List)
策略模式
使用这个模式来将一组算法封装成一系列对象。通过传递这些对象可以灵活的改变程序的功能。
(1)java.util.Comparator#compare()
(2)Arrays.sort、Collections.sort
享元模式
享元模式主要用于减少创建对象的数量,以减少内存占用和提高性能。享元模式尝试重用现有的同类对象,如果未找到匹配的对象,则创建新对象。
(1)在Java语言中,String类型就是使用了享元模式。String对象是final类型,对象一旦创建就不可改变。在Java中字符串常量都是存在常量池中的,Java会确保一个字符串常量在常量池中只有一个拷贝。String a="abc",其中"abc"就是一个字符串常量。
(2)Integer.valueOf(int i);Character.valueOf(char c)
代理模式
(1)透明调用被代理对象,无须知道复杂实现细节
(2)增加被代理类的功能
JDK中体现:
(1)java.lang.reflect.Proxy
(2)RMI
迭代器模式
迭代器模式是与集合共生共死的,一般来说,我们只要实现一个集合,就需要同时提供这个集合的迭代器,就像java中的Collection,List、Set、Map等,这些集合都有自己的迭代器。假如我们要实现一个这样的新的容器,当然也需要引入迭代器模式,给我们的容器实现一个迭代器。
Observer(观察者)
作用:通知对象状态改变
JDK中体现:
(1)java.util.Observer,Observable
(2)Swing中的Listener
Null Object(空对象)
作用:不需每次判空,对待空值,如同对待一个相同接口的对象
JDK中体现:
java.util.Collections#emptyList()
java.util.Collections#emptyMap()
java.util.Collections#emptySet()
工厂方法模式
java.lang.Proxy.newProxyInstance()
java.lang.Class.forName(String className)
静态工厂方法
java.lang.Boolean.valueOf(String)