设计模式
设计模式,用来让你的代码可扩展、可维护、更容易理解;用抽象去构建框架,用实现去扩展细节,让你的代码高内聚、低耦合。
一 六大设计原则
单一职责原则
迪米特原则
接口隔离原则
里氏替换原则:不要破坏继承体系
依赖倒置原则 面向接口编程
开闭原则:总结性的 对扩展开放,对修改关闭
接口隔离原则和单一职责都是为了提高类的内聚性、降低它们之间的耦合性,体现了封装的思想,但两者是不同的:
单一职责原则注重的是职责,而接口隔离原则注重的是对接口依赖的隔离。
单一职责原则主要是约束类,它针对的是程序中的实现和细节;接口隔离原则主要约束接口,主要针对抽象和程序整体框架的构建。
二 设计模式
1、工厂模式
不给业务端暴露创建的逻辑
工厂模式分为:
简单工厂模式
工厂方法模式 多态性工厂,把不同的产品实现类放到不同的工厂里,
抽象工厂模式
思考:为什么springMvc模式,用工厂创建been?
2、策略模式
策略模式是准备一组算法,并将这组算法封装到一系列的策略类里面
理解多态
多态的实现方式 重新、接口、抽象类和抽象方法
多态存在的三个必要条件
继承
重写
父类引用指向子类对象:Parent p = new Child();
策略模式的主要优点如下。
多重条件语句不易维护,而使用策略模式可以避免使用多重条件语句,如 if...else 语句、switch...case 语句。
策略模式提供了一系列的可供重用的算法族,恰当使用继承可以把算法族的公共代码转移到父类里面,从而避免重复的代码。
策略模式可以提供相同行为的不同实现,客户可以根据不同时间或空间要求选择不同的。
策略模式提供了对开闭原则的完美支持,可以在不修改原代码的情况下,灵活增加新算法。
策略模式把算法的使用放到环境类中,而算法的实现移到具体策略类中,实现了二者的分离。
其主要缺点如下。
客户端必须理解所有策略算法的区别,以便适时选择恰当的算法类。
策略模式造成很多的策略类,增加维护难度
3、单例模式
单例对象的类必须保证只有⼀个实例存在
适用场景: 单例模式只允许创建一个对象,因此节省内存,加快对象访问速度,因此对象需要被公共的场
合使用,如多个模块使用同一个数据源连接对象等等。如:
1. 需要频繁实例化然后销毁的对象。
2. 创建对象时耗时过多或者耗资源过多,但是经常被用到的对象。
3. 有状态的具类对象。
第一种:饿汉模式
public class HungrySingleton {
private static final HungrySingleton instance = new HungrySingleton();
private HungrySingleton() {
}
public static HungrySingleton getInstance() {
return instance;
}
}
优点:没有线程安全问题,简单
缺点:
1)提前初始化会延⻓类加载器加载类的时间;
2)如果不使用会浪费内存空间
3)不能传递参数
第二种:懒汉模式
public class LazySingleton {
private static volatile LazySingleton instance = null; //保证 instance 在所有线程中同步
private LazySingleton() {
} //private 避免类在外部被实例化
public static synchronized LazySingleton getInstance() {
//getInstance 方法前加同步
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
知识点:1、volatile
volatile 线程间可见
防止指令重排
2、 synchronized 锁定方法,保证线程安全
懒汉模式的缺点:
如果编写的是多线程程序,则不要删除上例代码中的关键字 volatile 和 synchronized,否则将存在线程非安全的问题。如果不删除这两个关键字就能保证线程安全,但是每次访问时都要同步,会影响性能,且消耗更多的资源,这是懒汉式单例的缺点。
3、静态方法和静态变量的区别(也是懒汉和饿汉的本质区别)
静态变量和静态代码块是类加载的时候执行
静态方法是调用的时候才会初始化
双重检查锁(double checked locking)
synchronized 由锁方法 变为 锁类
public class SingletonDoubleCheck {
private volatile static SingletonDoubleCheck singletonDoubleCheck;
private SingletonDoubleCheck() {
}
public static SingletonDoubleCheck getInstance() {
if (null == singletonDoubleCheck) {
synchronized (SingletonDoubleCheck.class) {
if (null == singletonDoubleCheck) {
singletonDoubleCheck = new SingletonDoubleCheck();
}
}
}
return singletonDoubleCheck;
}
}
知识点:1、synchronized是Java中的关键字,是一种同步锁。它修饰的对象有以下几种:
1)修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
2) 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
3)修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;
4)修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。
2、双重检索的意义:第一个:保障性能,不用每次加锁
第二个:保证线程安全
3、单例模式会被反射破坏,需要加下面这句话
private Singleton() {
if (null != SingletonClassInstance.instance)
throw new RuntimeException();
}
4、对象的生命周期:
单例的生命周期:spring 容器创建时就被创建,项目停止单例模式才会结束。
多例的生命周期:使用对象的时候才被创建,在jvm中垃圾回收
4、命令模式
分为抽象命令类、具体命令类、调用者类、接受者类
命令模式的优点
1. 降低耦合度;
2. 新的命令可以很容易的加入到系统中;
3. 可以比较容易的设计一个命令队列和宏命令(组合命令);
4. 可以方便的实现队请求的undo和redo;
命令模式的缺点
1. 使⽤命令模式可能会导致某些系统有过多的具体命令类。因为针对每㇐个命令都需
要设计㇐个具体命 令类, 因此某些系统可能需要⼤量具体命令类,这将影响命令模
式的使⽤。
5、代理模式
使用场景:AOP 日志打印、异常处理、事务
1、静态代理
分为抽象角色、真实角色、代理角色(引用真实角色,在调用真实角色前后增强代码)。
优点:可以做到在不修改目标对象功能的基础上,对目标对象进行扩展。
缺点:
1)每一个代理类都要实现一遍目标类的接口,如果目标类增加了一个接口,那么代理类也必须跟着添加。
2)每一个代理类对应一个目标类对象,如果要代理的类对象比较多,那么代理类也会很多,代码会比较臃肿。
2、动态代理
1)Jdk动态代理
通过反射实现,能有效解决静态代理中代理类过多而代码臃肿的问题。
1> 必须是接口
if (!interfaceClass.isInterface()) {
7 throw new IllegalArgumentException(
8 interfaceClass.getName() + " is not an interface");
9 }
2>实现动态代理类实现InvocationHandler接,并重写该invoke方法。
cjlib动态代理
动态代理类实现MethodInterceptor接口
如果目标对象没有实现接口,只能用cjlib动态代理。
原理:
CGLIB是一个强大的高性能的代码生成包,底层是通过使用一个小而快的字节码处理框架ASM,它可以在
运行期扩展Java类与实现Java接口
Enhancer是CGLIB的字节码增强器,可以很方便的对类进行拓展
使用的是继承
final修饰的类、方法不能使用cglib代理(如:wait() getClass);
创建代理对象的步骤:
1、生成代理类的二进制字节码文件
2、加载二进制字节码,生成Class对象( 例如使用Class.forName()放法 )