使用设计模式重构整体代码,可以提高代码的复用性、扩展性,减少代码冗余。
创建型模式:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式;
结构型模式:适配器模式、装饰模式、代理模式、外观模式、桥接模式、组合模式、享元模式;
行为型模式:策略模式、模板方法模式、观察者模式、迭代器模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
在Java应用程序中,一个类只有一个实例存在。
优点:能够节约当前堆内存,不需要频繁new对象,能够快速访问。
缺点:当多个线程访问同一个单例对象的时候可能会存在线程安全问题。
package com.singleton;
import java.io.Serializable;
/**
* @ClassName Slovenly
* @Description 1.懒汉式:线程不安全
* @Author honey-袁小康
* @Date 2020/6/10 22:06
* @Version 1.0
*/
public class Slovenly implements Serializable {
private static Slovenly slovenly = null;
/**
* 私有化构造函数
*/
private Slovenly() {
}
/**
* 懒汉式:当真正需要该对象的时候才会创建该对象
*/
public static Slovenly getInstance() {
if (slovenly == null) {
slovenly = new Slovenly();
}
return slovenly;
}
}
package com.singleton;
/**
* @ClassName SlovenlyLock
* @Description 2.加锁懒汉式:线程安全,效率低
* @Author honey-袁小康
* @Date 2020/6/10 22:15
* @Version 1.0
*/
public class SlovenlyLock {
private static SlovenlyLock slovenlyLock;
private SlovenlyLock() {
}
public static synchronized SlovenlyLock getInstance() {
if (slovenlyLock == null) {
slovenlyLock = new SlovenlyLock();
}
return slovenlyLock;
}
}
package com.singleton;
/**
* @ClassName DoubleInspectionLock
* @Description 3.双重检验锁:线程安全,效率比较高
* @Author honey-袁小康
* @Date 2020/6/10 22:22
* @Version 1.0
*/
public class DoubleInspectionLock {
private static DoubleInspectionLock doubleInspectionLock;
private DoubleInspectionLock() {
}
public static DoubleInspectionLock getInstance() {
if (doubleInspectionLock == null) {
synchronized (DoubleInspectionLock.class) {
if (doubleInspectionLock == null) {
doubleInspectionLock = new DoubleInspectionLock();
}
}
}
return doubleInspectionLock;
}
}
package com.singleton;
/**
* @ClassName Hungry
* @Description 饿汉式:先天性线程安全,浪费内存空间
* @Author honey-袁小康
* @Date 2020/6/10 22:34
* @Version 1.0
*/
public class Hungry {
/**
* 当class文件被加载的时候就创建该对象
*/
private Hungry() {
}
private static Hungry hungry = new Hungry();
public static Hungry getInstance() {
return hungry;
}
}
package com.singleton;
/**
* @ClassName HungryConstant
* @Description
* @Author honey-袁小康
* @Date 2020/6/10 22:39
* @Version 1.0
*/
public class HungryConstant {
private HungryConstant() {
}
public static final HungryConstant HUNGRYCONSTANT = new HungryConstant();
}
package com.singleton;
/**
* @ClassName StaticCodeBlock
* @Description 静态代码块
* @Author honey-袁小康
* @Date 2020/6/10 22:45
* @Version 1.0
*/
public class StaticCodeBlock {
private StaticCodeBlock() {
}
private static StaticCodeBlock staticCodeBlock;
static {
staticCodeBlock = new StaticCodeBlock();
}
public static StaticCodeBlock getInstance() {
return staticCodeBlock;
}
}
package com.singleton;
/**
* @ClassName StaticInnerClass
* @Description 静态内部类
* @Author honey-袁小康
* @Date 2020/6/10 23:50
* @Version 1.0
*/
public class StaticInnerClass {
private StaticInnerClass() {
}
private static class SingletonHolder {
private static final StaticInnerClass STATIC_INNER_CLASS = new StaticInnerClass();
}
public static StaticInnerClass getInstance() {
return SingletonHolder.STATIC_INNER_CLASS;
}
}
package com.singleton;
/**
* @ClassName Enum
* @Description
* @Author honey-袁小康
* @Date 2020/6/10 23:45
* @Version 1.0
*/
public enum Enum {
/**
* 枚举单例
*/
INSTANCE;
public void getInstance() {
System.out.println("getInstance");
}
}
使用反射和序列化可以破解单例。
try {
Class<?> aClass = Class.forName("com.crack.PreventCracking");
Constructor<?> constructor = aClass.getDeclaredConstructor();
constructor.setAccessible(true);
PreventCracking preventCracking2 = (PreventCracking) constructor.newInstance();
PreventCracking preventCracking1 = PreventCracking.getInstance();
System.out.println(preventCracking1 == preventCracking2);
} catch (Exception e) {
e.printStackTrace();
}
package com.crack;
/**
* @ClassName PreventCracking
* @Description 防止反射破解单例
* @Author honey-袁小康
* @Date 2020/6/10 23:08
* @Version 1.0
*/
public class PreventCracking {
private static PreventCracking preventCracking = null;
private PreventCracking() throws Exception {
if (preventCracking != null) {
throw new Exception("该对象已经被创建,请勿重复创建!");
} else {
preventCracking = this;
}
}
public static PreventCracking getInstance() throws Exception {
if (preventCracking == null) {
preventCracking = new PreventCracking();
}
return preventCracking;
}
}
// 使用序列化破解单例
FileOutputStream fos = null;
ObjectOutputStream oos = null;
ObjectInputStream ois = null;
try {
// 1.将对象序列化存入到本地文件中
fos = new FileOutputStream("d:/code/slovenly.txt");
oos = new ObjectOutputStream(fos);
Slovenly slovenly1 = Slovenly.getInstance();
oos.writeObject(slovenly1);
//2.从硬盘中反序列化对象到内存中
ois = new ObjectInputStream(new FileInputStream("d:/code/slovenly.txt"));
Slovenly slovenly2 = (Slovenly) ois.readObject();
System.out.println(slovenly1 == slovenly2);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (ois != null) {
ois.close();
}
if (oos != null) {
oos.close();
}
if (fos != null) {
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
package com.singleton;
import java.io.Serializable;
/**
* @ClassName Slovenly
* @Description 1.懒汉式:线程不安全
* @Author honey-袁小康
* @Date 2020/6/10 22:06
* @Version 1.0
*/
public class Slovenly implements Serializable {
private static Slovenly slovenly = null;
/**
* 私有化构造函数
*/
private Slovenly() {
}
/**
* 懒汉式:当真正需要该对象的时候才会创建该对象
*/
public static Slovenly getInstance() {
if (slovenly == null) {
slovenly = new Slovenly();
}
return slovenly;
}
/**
* 序列化生产回调方法 通过该方法实现反序列化生产单例对象
*
* @return Object
*/
public Object readResolve() {
return slovenly;
}
}
工厂模式提供了一种创建对象的最佳方式。在工厂模式中,在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。实现了创建者和调用者分离,工厂模式分为简单工厂、工厂方法、抽象工厂、静态工厂。
Spring IOC创建bean时使用了BeanFactory。
优点:代码结构简单;获取产品的过程更加简单;满足了开闭原则,即对拓展开放,对修改关闭;降低程序的耦合性;减少重复冗余代码;减少了使用者因为创建逻辑导致的错误。
缺点:拓展较繁琐,拓展时,需同时改动抽象工厂和工厂实现类。
代理模式主要对方法执行之前与之后实现增强。
代理模式主要包含三个角色,即抽象主题角色(Subject)、委托类角色(被代理类角色Proxied)以及代理类角色(Proxy),如下图所示:
抽象主题角色:可以是接口,也可以是抽象类;
委托类角色:真实主题角色,业务逻辑的具体执行者;
代理类角色:内部含有对真实对象RealSubject的引用,负责对真实主题角色的调用,并在真实主题角色处理前后做预处理和后处理。
代理模式分为静态代理模式和动态代理模式;动态代理模式又分为JDK动态代理和Cglib动态代理。
由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。
动态代理不需要写代理类对象,通过程序自动生成,而静态代理需要我们自己写代理类对象。
动态代理在实现阶段不用关心代理类,而在运行阶段才指定哪一个对象。
动态代理类的源码是在程序运行期间由JVM根据反射等机制动态生成。
利用拦截器机制,必须实现InvocationHandler接口中的invoke方法实现对目标方法增强。
使用ASM字节码技术,生成子类对目标方法实现增强。
Cglib依赖于ASM字节码技术,直接生成class文件,再使用类加载器读取到程序中;
使用fastclass对被代理类的方法建立索引文件,不需要依赖于反射查找到目标方法,所以效率比Jdk动态代理高。
使用@Async注解时,应当单独使用一个类进行调用。
使用@Transaction注解时,应当单独使用一个类进行调用。
不改变原有代码的基础之上,新增附加功能。
(1)抽象组件:定义一个抽象接口,来规范准备附加功能的类
(2)具体组件:将要被附加功能的类,实现抽象构件角色接口
(3)抽象装饰者:持有对具体构件角色的引用并定义与抽象构件角色一致的接口
(4)具体装饰:实现抽象装饰者角色,负责对具体构件添加额外功能。
多级缓存设计;mybatis中的一级与二级缓存;IO流。
Zk的事件监听;分布式配置中心刷新配置文件;业务中群发不同渠道的消息。
定义:使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。其过程实际上是一个递归调用。
优点:
责任链模式的最主要功能就是:动态组合,请求者和接受者解耦。
请求者和接受者松散耦合:请求者不需要知道接受者,也不需要知道如何处理。每个职责对象只负责自己的职责范围,其他的交给后继者。各个组件间完全解耦。
动态组合职责:责任链模式会把功能分散到单独的职责对象中,然后在使用时动态的组合形成链,从而可以灵活的分配职责对象,也可以灵活的添加改变对象职责。
缺点:
产生很多细粒度的对象:因为功能处理都分散到了单独的职责对象中,每个对象功能单一,要把整个流程处理完,需要很多的职责对象,会产生大量的细粒度职责对象。
不一定能处理:每个职责对象都只负责自己的部分,这样就可能出现某个请求,即使把整个链走完,都没有职责对象处理它。这就需要提供默认处理,并且注意构造链的有效性。
策略模式是对算法的包装,是把使用算法的责任和算法本身分割开来,委派给不同的对象管理,最终可以实现解决多重if判断问题。
定义策略接口->实现不同的策略类->利用多态或其他方式调用策略
核心设计要点:
AbstractClass : 抽象类,定义并实现一个模板方法。这个模板方法定义了算法的骨架,而逻辑的组成步骤在相应的抽象操作中,推迟到子类去实现。
ConcreteClass : 实现类,实现父类所定义的一个或多个抽象方法。
外观模式(Facade):他隐藏了系统的复杂性,并向客户端提供了一个可以访问系统的接口。这种类型的设计模式属于结构型模式。为子系统中的一组接口提供了一个统一的访问接口,这个接口使得子系统更容易被访问或者使用。
简单来说,该模式就是把一些复杂的流程封装成一个接口供给外部用户更简单的使用。这个模式中,设计到3个角色。
1)门面角色:外观模式的核心。它被客户角色调用,它熟悉子系统的功能。内部根据客户角色的需求预定了几种功能的组合。
2)子系统角色:实现了子系统的功能。它对客户角色和Facade时未知的。它内部可以有系统内的相互交互,也可以由供外界调用的接口。
3)客户角色:通过调用Facede来完成要实现的功能。
状态模式允许一个对象在其内部状态改变的时候改变其行为。这个对象看上去就像是改变了它的类一样。