定义:指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点。属于创建型模式。
饿汉式单例 被反射、序列化破坏懒汉式单例 被反射、序列化破坏容器式单例 被反射、序列化破坏枚举式单例不会被反射、序列化破坏在静态块中有map存放 map.put(“name”,枚举值),反序列化也是通过这个拿到对象,所以不会被序列化破坏枚举单例内部不让通过反射来创建对象Constructor.newInstance(Constructor.JAVA:417) 会直接抛出异常
1.1 饿汉式单例优点:执行效率高,性能高,没有任何的锁缺点:某些情况下回造成内存浪费。(类的数量没法控制的时候,类在加载的时候就初始化了,即使用不到也会被初始化,就会造成内存的浪费)。代码实现:
public class HungrySingleton {
private static final HungrySingleton singleton = new HungrySingleton();
private HungrySingleton() {
}
public static HungrySingleton getSingleton() {
return singleton;
}
}
优点:节省内存缺点:线程不安全,如果多个线程同时调用创建单例的方法,会创建多个对象代码实现:以下代码已加上synchronized关键字,可以保证线程安全
public class LazySimpleSingleton {
private static LazySimpleSingleton singleton = null;
private LazySimpleSingleton() {
}
public synchronized static LazySimpleSingleton getSingleton() {
if (singleton == null) {
singleton = new LazySimpleSingleton();
}
return singleton;
}
}
优点:性能高了,线程安全了缺点:可读性难度大,代码不优雅代码实现:
public class LazyDoubleCheckSingleton {
//这里添加volatile关键字 可以避免指令重排序造成的线程紊乱问题
private volatile static LazyDoubleCheckSingleton singleton = null;
private LazyDoubleCheckSingleton() {
}
public LazyDoubleCheckSingleton getSingleton() {
//检查是否要阻塞
if (singleton == null) {
synchronized (LazyDoubleCheckSingleton.class) {
//检查是否为空
if (singleton == null) {
singleton = new LazyDoubleCheckSingleton();
//这里会发生指令重排序
}
}
}
return singleton;
}
}
指令重排序:1.开辟堆空间 2.对开辟的空间进行初始化 3.将内存空间的地址赋值给变量(instance) 这三步走完才是创建完一个对象。如果不加volatile关键字,可能这三步就会错乱,导致指令重排序。
1.4 静态内部类单例优点:利用了JAVA本身的语法特点,性能高,避免了内存浪费缺点:能够本反射破坏代码实现:
public class LazyStaticInnerClassSingleton {
private LazyStaticInnerClassSingleton() {
//避免反射破坏
if (InnerClassSingleton.innerClassSingleton != null) {
throw new RuntimeException("不能非法访问");
}
}
public static LazyStaticInnerClassSingleton getSingleton() {
return InnerClassSingleton.innerClassSingleton;
}
//在这里使用的时候才会调用静态内部类 InnerClassSingleton.innerClassSingleton;
private static class InnerClassSingleton {
private static final LazyStaticInnerClassSingleton innerClassSingleton = new LazyStaticInnerClassSingleton();
}
}
优点:最优雅的写法,提供性能,防止反射破坏单例,防止线程破坏单例,被官方推荐。缺点:不能大面积创建对象,容器造成内存浪费,序列化会破坏单例。代码实现:
public enum EnumSingleton {
INSTANCE;
private Object data;
public static EnumSingleton getInstance() {
return INSTANCE;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
枚举单例内部不让通过反射来创建对象,Constructor.newInstance(Constructor.java:417) 会直接抛出异常
1.6 容器式单例优点:解决了大面积创建对象的问题,不会造成内存浪费,是对枚举单例的优化,是spring ioc使用的单例方式。缺点:线程不安全代码实现:以下代码中已加入双重检索,从而可以避免线程安全的问题。
public class ContainerSingleton {
private static Map<String, Object> containerMap = new ConcurrentHashMap<String, Object>();
private ContainerSingleton() {
}
public static Object getInstance(String className) {
Object instance = null;
if (!containerMap.containsKey(className)) {
synchronized (ContainerSingleton.class){
if (!containerMap.containsKey(className)){
try {
Class<?> aClass = Class.forName(className);
instance = aClass.newInstance();
containerMap.put(className, instance);
} catch (Exception e) {
e.printStackTrace();
}
}
}
return instance;
} else {
return containerMap.get(className);
}
}
}
优点:避免序列化破坏单例缺点:代码实现:
public class SerializableSingleton {
private static final SerializableSingleton singleton = new SerializableSingleton();
private SerializableSingleton() {
}
public static SerializableSingleton getSingleton() {
return singleton;
}
//加入此方法可以解决被序列化破坏的单例
//具体要看一下 ObjectInputStream中的readObject()方法 下的 readObject0方法中的 checkResolve方法中的readOrdinaryObject方法
// if (obj != null && handles.lookupException(passHandle) ==null
//&& desc.hasReadResolveMethod()
//在这个方法处理之后就不会再重新创建了 就直接返回singleton对象了
private Object readResolve() {
return singleton;
}
}
反序列化是不走构造函数的,是直接使用字节码重组的
1.8 ThreadLocal单例在同一个线程下单例,不同的线程下会创建不同的对象代码实现:
public class ThreadLocalSingleton {
private static final ThreadLocal<ThreadLocalSingleton> threadLocalSingleton = new ThreadLocal<ThreadLocalSingleton>() {
@Override
protected ThreadLocalSingleton initialValue() {
return new ThreadLocalSingleton();
}
};
private ThreadLocalSingleton() {
}
public static ThreadLocalSingleton getInstance() {
return threadLocalSingleton.get();
}
}
- 私有构造器
- 保存线程安全
- 延迟加载
- 防止序列化反序列化破坏单例
- 防止反射攻击单例
Spring中AbstractFactoryBean类下的getObject方法Mybatis中ErrorContextServletContext、ServletConfig、ApplicationContext、DBPool、BeanFactory、Runtime、Rpc
2、工厂模式2.1 简单工厂模式定义:指由一个工厂对象决定创建出哪一种产品类的实例,属于创建型模式,但是不属于23中设计模式。
使用场景:
- 工厂类负责创建的对象较少
- 应用层只需要传入工厂类的参数,对于如何创建对象的逻辑不需要关心。
源码中实现场景:
- Calendar.getInstance();
- LoggerFactory.getLogger(String name);
- LoggerFactory.getLogger(Class<?> clazz);
代码实现:
public interface ICourse {
void record();
}
public class CourseImpl implements ICourse {
@Override
public void record() {
System.out.println("正在学习简单工厂模式");
}
}
public class JavaCourse implements ICourse{
@Override
public void record() {
System.out.println("正在学习JAVA课程");
}
}
public class SimpleFactory {
public ICourse create(Class clazz) {
try {
if (clazz != null) {
return (ICourse) clazz.newInstance();
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
定义:是指定义一个创建对象的接口,让实现这个接口类来决定实例化哪个类,工厂方法模式让类的实例化推迟到子类中进行,属于创建型模式。使用场景:
- 创建对象需要大量重复的代码时建议使用
- 应用层不依赖于产品类实例如何被创建、实现等细节,一个类通过其子类来指定创建哪个对象。
源码中实现场景:
- LoggerFactory.getLogger();
代码实现:
public interface ICourse {
void record();
}
public class JavaCourse implements ICourse {
@Override
public void record() {
System.out.println("正在学习JAVA 工厂方法模式");
}
}
public class PythonCourse implements ICourse {
@Override
public void record() {
System.out.println("正在学习Python 工厂方法模式");
}
}
public interface ICourseFactory {
ICourse create();
}
public class JavaCourseFactory implements ICourseFactory {
@Override
public ICourse create() {
return new JavaCourse();
}
}
public class PythonCourseFactory implements ICourseFactory {
@Override
public ICourse create() {
return new PythonCourse();
}
}
public class MethodFactoryMain {
public static void main(String[] args) {
ICourseFactory factory = new JavaCourseFactory();
ICourse iCourse = factory.create();
iCourse.record();
}
}
定义:指提供创建一系列相关或相互依赖对象的接口,无序指定它们具体的类,属于创建型模式。使用场景:
- 应用层不依赖于产品类实例如何被创建、实现等细节
- 强调一系列相关产品对象一起使用创建对象需要大量重复代码
- 提供一个产品类的库,所有产品以同样的接口出现,从而使客户端不依赖于具体实现。
源码中实现场景:
- spring中DefaultListableBeanFactory类
代码实现:
public interface ICourse {
void record();
}
public interface INote {
void write();
}
public interface IVideo {
void video();
}
public interface ICourseFactory {
INote createNote();
ICourse createCource();
IVideo createVideo();
}
public class JavaCourse implements ICourse {
@Override
public void record() {
System.out.println("course 正在学习JAVA 抽象工厂模式");
}
}
public class JavaNote implements INote {
@Override
public void write() {
System.out.println("note 正在学习抽象工厂方法模式");
}
}
public class JavaVideo implements IVideo {
@Override
public void video() {
System.out.println("video 正在学习抽象工厂模式");
}
}
public class JavaCourseFactory implements ICourseFactory {
@Override
public INote createNote() {
return new JavaNote();
}
@Override
public ICourse createCource() {
return new JavaCourse();
}
@Override
public IVideo createVideo() {
return new JavaVideo();
}
}
public class PythonCourse implements ICourse{
@Override
public void record() {
System.out.println("PythonCourse正在学习抽象工厂模式");
}
}
public class PythonNote implements INote{
@Override
public void write() {
System.out.println("python 正在学习抽象工厂方法");
}
}
public class PythonVideo implements IVideo{
@Override
public void video() {
System.out.println("PythonVideo 正在学习抽象工厂模式");
}
}
public class PythonFactory implements ICourseFactory{
@Override
public INote createNote() {
return new PythonNote();
}
@Override
public ICourse createCource() {
return new PythonCourse();
}
@Override
public IVideo createVideo() {
return new PythonVideo();
}
}
public class AbstractFactoryMain {
public static void main(String[] args) {
ICourseFactory factory = new JavaCourseFactory();
factory.createCource().record();
factory.createNote().write();
factory.createVideo().video();
ICourseFactory pythonFactory = new PythonFactory();
pythonFactory.createCource().record();
pythonFactory.createNote().write();
pythonFactory.createVideo().video();
}
}
指为其他对象提供一种代理,以控制对这个对象的访问,代理对象和目标对象之间起到中介作用,属于结构性模式。
3.1 静态代理模式代码实现:
public interface IPerson {
void findFriend();
}
public class Uncle implements IPerson {
private XiaoPengYou xiaoPengYou;
public Uncle(XiaoPengYou xiaoPengYou) {
this.xiaoPengYou = xiaoPengYou;
}
@Override
public void findFriend() {
System.out.println("小朋友找到了叔叔");
xiaoPengYou.findFriend();
System.out.println("叔叔帮助小朋友找到了小伙伴");
}
}
public class XiaoPengYou implements IPerson {
@Override
public void findFriend() {
System.out.println("小朋友想要找个小伙伴");
}
}
public class StaticProxyMain {
public static void main(String[] args) {
Uncle uncle = new Uncle(new XiaoPengYou());
uncle.findFriend();
}
}
spring中的代理选择原则:
- 当Bean有实现接口时,Spring就会用jdk动态代理
- 当Bean没有实现接口时,Spring就会用cglib动态代理
- Spring可以通过配置强制使用cglib,只需要Spring配置文件中添加以下代码
<aop:aspectj-autoproxy proxy-target-class="true"/>
Cglib动态代理代理和JDK动态代理区别
- Cglib是继承的方式,覆盖父类的方法,JDK采用的是实现的方式,必须要求代理的目标对象一定要实现一个接口,通过生成字节码重组成一个新的类。
- JDKProxy对于用户而言,依赖更强,调用也更复杂。Cglib对目标类没有任何要求
- Cglib效率更高,性能也更高,底层没有用到反射。JDKProxy生成逻辑较为简单,执行效率低,每次都要用到反射。
- Cglib目标代理类不能有final修饰的方法,忽略final修饰的方法。
实现原理:
- 动态生成源码.java文件
- Java文件输出到磁盘,保存为文件Proxy0.java
- 把.java文件编译成Proxy0.class文件
- 把生成的.class文件加载到JVM中
- 返回新的代理对象
代码实现:
public interface IPerson {
void study();
}
public class Student implements IPerson {
@Override
public void study() {
System.out.println("学生想学习动态代理");
}
}
public class Teacher implements IPerson {
@Override
public void study() {
System.out.println("老师想学习动态代理");
}
}
public class ProxyClient implements InvocationHandler {
private Object target;
public Object getTarget(Object target) {
this.target = target;
Class<?> clazz = target.getClass();
//类加载器 Object this代表当前类(就是实现InvocationHandler这个接口的类)
return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object invoke = method.invoke(this.target, args);
return invoke;
}
}
public class DynamicProxyMain {
public static void main(String[] args) {
ProxyClient proxyClient = new ProxyClient();
// IPerson target = (IPerson)proxyClient.getTarget(new Student());
// target.study();
//
// IPerson target1 = (IPerson)proxyClient.getTarget(new Teacher());
// target1.study();
//jdk代理再被jdk代理 start 使用同一个InvocationHandler
// IPerson target2 = (IPerson)proxyClient.getTarget(new Student());
// target2.study();
// IPerson target3 = (IPerson)proxyClient.getTarget(target2);
// target3.study();
//jdk代理再被jdk代理 end
//jdk代理再被jdk代理 start 使用不同的InvocationHandler
ProxyClient proxyClient1 = new ProxyClient();
IPerson target4 = (IPerson) proxyClient.getTarget(new Student());
target4.study();
IPerson target5 = (IPerson) proxyClient1.getTarget(target4);
target5.study();
//jdk代理再被jdk代理 end
}
}
实现原理:
- 实现MethodInterceptor接口,
- 重写intercept方法
代码实现:
public interface IPerson {
void study();
}
public class Student {
public void study() {
System.out.println("学生想学习动态代理");
}
}
public class Teacher {
public void study() {
System.out.println("老师想学习动态代理");
}
}
public class CgLibProxy implements MethodInterceptor {
public Object getInstance(Class clazz) {
//cglib 创建代理对象的类库
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
Object result = methodProxy.invokeSuper(o, objects);
return result;
}
}
public class CglibProxyMain {
public static void main(String[] args) {
CgLibProxy cgLibProxy = new CgLibProxy();
Student instance = (Student) cgLibProxy.getInstance(Student.class);
instance.study();
Teacher instance1 = (Teacher) cgLibProxy.getInstance(Teacher.class);
instance1.study();
}
}
1.jdk代理后的类再次被jdk代理?jdk不能再次被jdk代理,因为会造成handler调用死循环。如果使用不同的实现InvocationHandler的代理类是可以的,mybatis中的多重插件就是这么做的2.jdk代理后的类再次被cglib代理?jdk生成的代理类是final,cglib生成的代理类是要去继承目标类的,final修饰的类不能被继承,所以不能被代理3.cglib代理后的类再次被cglib代理?会报方法名重复 Duplicate method name “newInstance” with signature4.cglib代理后的类再次被jdk代理?类的签名已经改变,没有目标方法了总结,只有jdk代理能再次被jdk代理,必须使用不同的InvocationHandler,其他都不可以
3.3.2 什么样类不能被代理?1.对于jdk,必须有接口实现2.final修飾的方法不能被cglib代理3.方法不是public的
3.3.3 接口能够被代理吗?接口能被代理,比如mybatis中的dao接口就是这样做
4、模板方法模式定义:又叫模板模式,是指定义一个算法的骨架,并允许子类为其中的一个或者多个步骤提供实现。模板方法使得子类不改变算法结构的情况下,重新定义算法的某些步骤。属于行为型设计模式使用场景:
- 一次性实现算法不变的部门,将可变的部分留给子类去实现。
- 各子类中公共的行为被提取出来并集中到一个公共的父类中,避免代码重复。
源码中实现场景:
- jdbcTemplate
- AbstractList 中的get方法
- HttpServlet中的doGet方法和doPost方法
- mybatis中的BaseExecutor下的doQuery方法
优点1.利用模板模式将相同的处理逻辑代码放到抽象父类中,可以提高代码复用性2.将不同的代码不同的子类中,通过对子类的扩展增加新的行为,提高代码的扩展性3.把不变的行为写在父类上,去除子类的重复代码,提供了一个很好的代码复用平台,符合代开闭原则。
缺点1.类数目的增加,每一个抽象类都需要一个子类来实现,这样导致类数量增加,间接的增加了系统实现的复杂度2.继承关系自身缺点,如果父类添加新的抽象方法,所有子类都要修改一遍代码实现:
public class JavaCourse extends AbstractCoursr {
@Override
public boolean needCheckHomework() {
return true;
}
@Override
protected void checkHomework() {
System.out.println("检查JAVA作业");
}
}
public abstract class AbstractCoursr {
public void createCourse() {
//1.发布预习资料
postPrepareData();
//2.制作PPT
makePPT();
//3.直播上课
videoInClass();
//4.上传课堂笔记
uploadNote();
//5.布置作业
assignHomework();
if (needCheckHomework()) {
//6.检查作业
checkHomework();
}
}
public abstract boolean needCheckHomework();
protected abstract void checkHomework();
protected void assignHomework() {
System.out.println("布置作业");
}
protected void uploadNote() {
System.out.println("上传课堂笔记");
}
protected void videoInClass() {
System.out.println("直播上课");
}
protected void makePPT() {
System.out.println("制作PPT");
}
protected void postPrepareData() {
System.out.println("发布预习资料");
}
}
public class TemplateMain {
public static void main(String[] args) {
JavaCourse javaCourse = new JavaCourse();
javaCourse.createCourse();
}
}
定义:也叫包装模式,是指在不改变原对象的基础之上,将功能附加到对象上,提供了比继承更有弹性的替代方案。属于结构型模式。使用场景:
- 用户扩展一个类的功能,或给一个类添加附加职责。
- 动态的给一个对象添加功能,这些功能可以再动态的撤销。
源码中的实现:
- InputStream BufferedInputStream 就是实际使用到的装饰器模式
- BufferedReader FileReader
- mybatis中的Cache的实现就是装饰器,同时也是委派模式
优点1.装饰器是继承的有力补充,比继承灵活,不改变原有的对象的情况下,动态的给一个对象扩展功能2.通过使用不同装饰器类以及这些装饰类的排列组合,可实现不同效果3.装饰器完全遵守开闭原则
缺点1.会出现更多的代码,更多的类,增加程序复杂性2.动态装饰时,多层装饰会更复杂。
装饰器模式和代理模式对比1.装饰器模式是一种特殊的代理模式2.装饰器模式强调自身的功能扩展3.代理模式强调的是代理过程的控制
代码实现:
public abstract class BatterCake {
public abstract String name();
public abstract Double getPrice();
}
public class BaseBatterCake extends BatterCake {
@Override
public String name() {
return "煎饼";
}
@Override
public Double getPrice() {
return 5D;
}
}
public class Decorator extends BatterCake {
private BatterCake batterCake;
public Decorator(BatterCake batterCake) {
this.batterCake = batterCake;
}
@Override
public String name() {
return this.batterCake.name();
}
@Override
public Double getPrice() {
return this.batterCake.getPrice();
}
}
public class EggDecorator extends Decorator {
public EggDecorator(BatterCake batterCake) {
super(batterCake);
}
@Override
public Double getPrice() {
return super.getPrice() 1;
}
@Override
public String name() {
return super.name() "加1个鸡蛋";
}
}
public class SuguaDecorator extends Decorator {
public SuguaDecorator(BatterCake batterCake) {
super(batterCake);
}
@Override
public String name() {
return super.name() "加1根肠";
}
@Override
public Double getPrice() {
return super.getPrice() 2;
}
}
public class DecoratorMain {
public static void main(String[] args) {
BatterCake batterCake;
batterCake = new BaseBatterCake();
batterCake = new EggDecorator(batterCake);
batterCake = new SuguaDecorator(batterCake);
System.out.println(batterCake.name() " " batterCake.getPrice());
}
}
定义:又叫政策模式,将定义的算法家族分别封装起来,让它们之间可以互相替换,从而让算法的变化不会影响到使用算法的用户。,可以避免多重分支的if。。。else。。。和switch语句。属于行为型模式。
使用场景:
- 假如系统中有很多类,而他们的区别仅仅在于它们的行为不同。
- 一个系统需要动态的在不同的算法中选择一种。
- 需要屏蔽算法规则。
源码中的实现:
- Arrays中public static void parallelSort(T[] a, int fromIndex, int toIndex,Comparator<? super T> cmp)
- TreeMap中的compare
- Spring中的Resource
优点:符合开闭原则避免使用多重条件转移语句 if else switch提供算法的安全性和保密性
缺点:客户端必须知道所有的策略,并且自行决定使用哪一个策略类代码中产生非常多的策略类,增加维护难度
代码实现:
public interface IStradegy {
void algorithm();
}
public class Context {
private IStradegy iStradegy;
public Context(IStradegy iStradegy) {
this.iStradegy = iStradegy;
}
public void algorithm() {
this.iStradegy.algorithm();
}
}
public class ConcreteStrategyA implements IStradegy {
@Override
public void algorithm() {
System.out.println("ConcreteStrategyA");
}
}
public class ConcreteStrategyB implements IStradegy {
@Override
public void algorithm() {
System.out.println("ConcreteStrategyB");
}
}
public class StradegyMain {
public static void main(String[] args) {
Context context = new Context(new ConcreteStrategyA());
context.algorithm();
}
}
定义:又叫委托模式,基本的作用就是负责任务的调度和任务的分配,将任务的分配和执行分离开来,可以看做是一种特殊情况下的静态代理的全权代理。不属于GOF 23种设计模式之一。属于行为型模式。源码中实现:
JVM中的双亲委派 一层一层的往上去找
类 public abstract class ClassLoader
方法 protected Class<?> loadClass(String name, boolean resolve)
反射中的
类public final class Method
方法public Object invoke(Object obj, Object... args)
spring中
BeanDefinition
BeanDefinitionParserDelegate
DispatcherServlet中的
doDispatch();
委派模式和代理模式区别委派模式是行为型模式,代理模式是结构型模式委派模式注重的是任务派遣,注重结果,代理模式注重的是代码增强,注重过程。委派模式是一种特殊的静态代理,相当于全权代理。
代码实现:
public interface IEmployee {
void doing(String task);
}
public class Boss {
public void commond(String task, Leader leader) {
leader.doing(task);
}
}
public class EmployeeA implements IEmployee {
@Override
public void doing(String task) {
System.out.println("员工A开始做事");
}
}
public class EmployeeB implements IEmployee {
@Override
public void doing(String task) {
System.out.println("员工B开始做事");
}
}
public class Leader implements IEmployee {
private Map<String, IEmployee> map = new ConcurrentHashMap();
public Leader() {
map.put("爬虫", new EmployeeA());
map.put("管理", new EmployeeB());
}
@Override
public void doing(String task) {
if (!map.containsKey(task)) {
System.out.println("超出能力范围");
} else {
map.get(task).doing(task);
}
// if ("爬虫".equals(task)){
// new EmployeeA().doing(task);
// } else if ("管理".equals(task)){
// new EmployeeB().doing(task);
// } else {
// System.out.println("不符合用人规范");
// }
}
}
public class DelegateMain {
public static void main(String[] args) {
Boss boss = new Boss();
boss.commond("爬虫", new Leader());
}
}