前言

在看狂神频道的时候偶然发现下图,感触颇深。特别在当今【程序 = 业务 + 框架】思想盛行的开发者中,夯实基础基础显得格外重要,因此开此专栏总结记录。

设计模式详解

  • 设计模式的考察点,一般有2个:

    • 一个是常用设计模式的实现,
    • 另外一个是设计模式的使用场景。也就是每个设计模式用来解决什么样的问题,在什么场景下该使用什么样的设计模式。
  • 最常见的设计模式有单例模式、工厂模式、代理模式,构造者模式、责任链模式、适配器模式、观察者模式等。

  • 我们看一下详解设计模式的知识点分为三大类型,共23种:

    • 创建型的有5种:

      (抽 工 单 建 原)

      • 象工厂模式、厂方法模式、例模式、造者模式、型模式。
    • 结构型的有7种:

      (有个长得不错桥梁工程代理喜欢拿着组合转接头享受缘分

      • 适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
    • 行为型的有11种:

      访问者写好策略备忘录,观察模板迭代的状态,命令中介解释责任链。)

      解释:这句话讲的就是看房子的经过。

      看房子的人就是访问者,看房前要写看房策略备忘录,不能马马虎虎地去看房子。

      去看房子的时候,要仔细观察楼板(模板)层叠(迭代)的状态

      看完房子,命令中介解释清楚产权的责任链

      • 策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式。忘录模式状态模式、访问者模式、中介者模式和解释器模式。
  • 面试中对于设计模式,你应该明白不同设计用来解决什么样的场景问题。对于常用的设计模式能够灵活运用。

常用模式

常用的设计模式有:单例模式、工厂模式、代理模式、构造者模式、责任链模式、适配器模式、观察者模式等,

接下来逐个模式进行讲解

重点:单例模式

  • 学习博客:厚积薄发打卡Day28 :狂神说Java之彻底玩转单例设计模式

    我引用我自己哈哈

  • 首先是单例模式,这个模式在实际业务中经常会用到,也是设计模式中的主要考察点。这里我只介绍线程安全的单例模式的实现方式。

  • 静态初始化(饿汉式)

    • 实现的思路就是在类初始化时。成单例实例的创建,因此不会产生并发问题。这种方式下,不管是否会使用到这个单例,都会创建这个单例。

    • 创建步骤:

      1. 私有构造器
      2. 类的内部创建对象
      3. 向外暴露一个静态的公共方法
    • 上代码:

      public class HungrySingleton {//1.私有构造器private HungrySingleton() {}//2.类的内部创建对象private final static HungrySingleton HUNGRYSINGLE = new HungrySingleton();//3.向外暴露一个静态的公共方法public static HungrySingleton getInstance() {return HUNGRYSINGLE;}//测试public static void main(String[] args) {//单线程测试:HungrySingleton instance1 = HungrySingleton.getInstance();HungrySingleton instance2 = HungrySingleton.getInstance();System.out.println(instance1 == instance2); //true//多线程测试:new Thread(() -> {HungrySingleton instanceA = HungrySingleton.getInstance();System.out.println(instanceA);//HungrySingleton@626213bf}).start();new Thread(() -> {HungrySingleton instanceB = HungrySingleton.getInstance();System.out.println(instanceB);//HungrySingleton@626213bf}).start();//多线程测试非单例:new Thread(() -> {NoneSingleton noneSingleton1 = new NoneSingleton();System.out.println(noneSingleton1);//NoneSingleton@531ebd8d}).start();new Thread(() -> {NoneSingleton noneSingleton2 = new NoneSingleton();System.out.println(noneSingleton2);//NoneSingleton@6bd95d5d}).start();}
      }
      //非单例测试类
      class NoneSingleton {}
      
  • 双重检查(懒汉式)

    • 第二种实现方式是双重检查,也叫做懒汉方式。只有在真正用到这个单例实例的时候才会创建。如果没有使用,就不会创建这个方式,必然会面对多个线程,同时使用实例时的并发问题。

    • 为了解决并发访问问题,通过synchronize 的或者lock 进行双重检查,保证只有一个线程能够创建实例,这里需要注意内存可见性引起的并发问题,必须使用volatile关键字,修饰单例变量

    • 实现步骤:

      1. 私有化构造函数
      2. 创建对象容器(因为new并不是一个原子性操作,需要加锁实例化)
      3. 对外提供加锁的静态实例化方法
    • 上代码(DCL:Double Check Lock)

      public class LazySingletonWithDCL {//1. 私有化构造函数private LazySingletonWithDCL() {System.out.println(Thread.currentThread().getName() + " ->OK");}//2. 创建对象容器(因为new并不是一个原子性操作,需要加锁实例化)private volatile static LazySingletonWithDCL lazySingleton;//3. 对外提供加锁的静态实例化方法public static LazySingletonWithDCL getInstance() {synchronized (LazySingletonWithDCL.class) {if (lazySingleton == null) {lazySingleton = new LazySingletonWithDCL();}}return lazySingleton;}//开启多条线程实例化LazySingletonWithDCLpublic static void main(String[] args) {for (int i = 0; i < 100; i++) {//            new Thread(()->{LazySingletonWithDCL.getInstance();}).start();new Thread(LazySingletonWithDCL::getInstance).start();}}
      }
      //-------------打印结果-------------------
      //Thread-0 ->OK
      
    • 看个反例(WithoutDcl)

      public class LazySingleton {//1.私有化构造函数private LazySingleton() {System.out.println(Thread.currentThread().getName()+" ->OK");}//2.创建对象(容器)private static LazySingleton lazyMan ;//3.对外提供静态实例化方法,判断在对象为空的时候创建public static LazySingleton getInstance(){//用的时候再加载if (lazyMan == null) {lazyMan = new LazySingleton();}return lazyMan;}//开启多条线程实例化LazySingletonWithDCLpublic static void main(String[] args) {for (int i = 0; i < 100; i++) {//            new Thread(()->{LazySingletonWithDCL.getInstance();}).start();new Thread(LazySingleton::getInstance).start();}}
      }
      //--------------打印结果:--------------
      /*
      Thread-0 ->OK
      Thread-5 ->OK
      Thread-4 ->OK
      Thread-3 ->OK
      Thread-2 ->OK
      Thread-1 ->OK
      */
      
  • JDK例子:Runtime

    public class Runtime {//2.类的内部创建对象private static Runtime currentRuntime = new Runtime();//3.开发对外的实例化方法public static Runtime getRuntime() {return currentRuntime;}//1.私有化构造方法private Runtime() {}....
    }
    
  • 单例注册表

    • spring 中的bean 的单例模式就是通过单例注册表方式实现的

      单例模式 - 单例注册表与 Spring 实现单例剖析

      运用Map的key不能重复与的特性,实现单例。

    • 详见Spring源码:

    • AbstractBeanFactory中doGetBean()

      // Create bean instance.
      if (mbd.isSingleton()) {sharedInstance = getSingleton(beanName, () -> {try {return createBean(beanName, mbd, args);}catch (BeansException ex) {// Explicitly remove instance from singleton cache: It might have been put there// eagerly by the creation process, to allow for circular reference resolution.// Also remove any beans that received a temporary reference to the bean.destroySingleton(beanName);throw ex;}});bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
      }
      
    • DefaultSingletonBeanRegistry中getSingleton() addSingleton()方法

    public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {Assert.notNull(beanName, "Bean name must not be null");synchronized (this.singletonObjects) {Object singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null) {//省略...}finally {if (recordSuppressedExceptions) {this.suppressedExceptions = null;}afterSingletonCreation(beanName);}if (newSingleton) {addSingleton(beanName, singletonObject);}}return singletonObject;
    }/** Cache of singleton objects: bean name to bean instance. */
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);protected void addSingleton(String beanName, Object singletonObject) {synchronized (this.singletonObjects) {this.singletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);this.earlySingletonObjects.remove(beanName);this.registeredSingletons.add(beanName);}
    }
    

记得要将设计模式与实际业务场景进行结合来体现,对设计模式的理解和应用能力。

工厂模式

  • 学习博客:

    1. 厚积薄发打卡Day29 :[kuangStudy] GoF23通俗易懂的设计模式之 <工厂模式>
  • 这个是创建不同类型实例常用的方式,例如spring 中的各种bean是由不同的工厂类进行创建的

    厚积薄发打卡Day47: [itcast] GoF23设计模式之 <自定义Spring IOC> (上)

    BeanFactory解析

    Spring中Bean的创建是典型的工厂模式,通过简单工厂模式+配置文件的方式实现这一系列的Bean工厂,即IoC容器,为开发者管理对象之间的依赖关系提供了很多便利和基础服务,在Spring中有许多IoC容器的实现供用户选择,其相互关系如下图所示。

    其中,BeanFactory作为最顶层的一个接口,定义了IoC容器的基本功能规范,观察其源码:

    public interface BeanFactory {String FACTORY_BEAN_PREFIX = "&";//根据bean的名称获取IOC容器中的的bean对象Object getBean(String name) throws BeansException;//根据bean的名称获取IOC容器中的的bean对象,并指定获取到的bean对象的类型,这样我们使用时就不需要进行类型强转了<T> T getBean(String name, Class<T> requiredType) throws BeansException;Object getBean(String name, Object... args) throws BeansException;<T> T getBean(Class<T> requiredType) throws BeansException;<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);<T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);//判断容器中是否包含指定名称的bean对象boolean containsBean(String name);//根据bean的名称判断是否是单例boolean isSingleton(String name) throws NoSuchBeanDefinitionException;boolean isPrototype(String name) throws NoSuchBeanDefinitionException;boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;@NullableClass<?> getType(String name) throws NoSuchBeanDefinitionException;String[] getAliases(String name);
    }
    

    在BeanFactory里只对IoC容器的基本行为做了定义,根本不关心Bean是如何定义及怎样加载的,以bean的产生结果为导向,正如我们只关心能从工厂里得到什么产品,不关心工厂是怎么生产这些产品的,所以后续会有注册类来为 BeanFactory加载bean。

  • Calendar类:

代理模式

  • 学习博客:厚积薄发打卡Day36 :[itcast] GoF23通俗易懂的设计模式之 <代理模式>

    从火车站卖票的例子对比说明了动态代理和静态代理

  • 主要用在不适合或者不能直接引用另一个对象的场景,可以通过代理模式对被代理的对象进行访问行为的控制,Java的代理模式分为静态代理和动态代理

    • 静态代理:是指在编译时就已经创建好了代理类

      • 例如我们在源代码中编写的类
    • 动态代理:只在JVM 运行过程中动态创建的代理类:使用动态代理的方法

      • 有JDK动态代理,CGLIB

      • 可以看到在newProxyInstance()方法中需要由Class<?>[] interfaces参数,也就是说必须要定义接口,才能对接口进行代理。

        假如没有接口的情况下,这时就应该使用CGLib来实现动态代理:

        CGLIB(Code Generation Library)详解

        CGLIB是一个功能强大,高性能的代码生成包。其被广泛应用于AOP框架(Spring、dynaop)中,用以提供方法拦截操作。Hibernate作为一个比较受欢迎的ORM框架,同样使用CGLIB来代理单端(多对一和一对一)关联(延迟提取集合使用的另一种机制)。CGLIB作为一个开源项目,其代码托管在github,地址为:https://github.com/cglib/cglib

  • 代理和装饰者的区别

    静态代理和装饰者模式的区别:

    • 相同点:

      • 都要实现与目标类相同的业务接口
      • 在两个类中都要声明目标对象
      • 都可以在不修改目标类的前提下增强目标方法
    • 不同点:
      • 目的不同

        • 装饰者是为了增强目标对象
        • 静态代理是为了保护和隐藏目标对象
      • 获取目标对象构建的地方不同
        • 装饰者是由外界传递进来,可以通过构造方法传递
        • 静态代理是在代理类内部创建,以此来隐藏目标对象
  • 面试时遇到这个问题,可以举个动态代理的例子:

    1. 比如dubbo中,是使用jdk的动态代理,通过反射把远程请求进行封装。使服务看上去就像在使用本地的方法。

    2. jdk动态代理在mybatis中的应用,主要通过 MapperProxyFactory实现,代理模式(以及动态代理在mybatis中的应用)

      如果使用过Mybatis,我们就会发现Mybatis的使用非常简单,首先定义一个dao接口,然后编写一个与dao接口的对应的配置文件,java对象与数据库字段的映射关系和dao接口对应的sql语句都是以配置的形式写在配置文件中,非常的简单清晰

      但是笔者在使用的过程中就曾经有过这样的疑问,dao接口是怎么和mapper文件映射起来的呢?只有一个dao接口又是怎么以对象的形式来实现数据库的读写操作呢?相信有疑问的肯定不止我一个人,当然,在看了上面两节之后,应该很容易猜到可以通过代理模式来动态的创建dao接口的代理对象,并通过这个代理对象来实现数据库的操作。

    3. Spring中的AOP实现

      Java动态代理——框架中的应用场景和基本原理

责任链模式

  • 责任链模式有点像工厂的流水线链上每一个节点,完成对对象的某一种处理
  • 例如Netty框架在处理消息时使用的pipeline,就是一种责任链模式

适配器模式

  • 学习博客:厚积薄发打卡Day33 :[kuangStudy] GoF23通俗易懂的设计模式之 <适配器模式>

  • 类似于我们常见的转接头,把两种不匹配的对象来进行适配,也可以起到对两个不同的对象进行解耦的作用

    • 例如我们常用的日志处理框架SLF4J,如果我们使用了SLF4J,就可以跟Log4J或者Logback,这种具体的日志实现框架进行解耦

    • 通过不同适配器将SLF4J与Log4J等实现框架进行适配,完成日志功能的使用

    • 适配器模式在Spring源码中的应用

      适配器模式在 SpringMVC 中的经典使用体现在它的核心方法 doDispatch 方法中,再来看一个 Spring MVC 中的 HandlerAdapter 类,它也有多个子类,类图如下。

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Etw8sTET-1627302709199)(厚积薄发打卡Day74 :【MSUP】Java语言特性与设计模式(上)/5-20092G24T2227.png)]

      设计模式|适配器模式使用案例、适配器模式在源码中的应用

      位于org.springframework.web.servlet包中的DispatcherServlet是servlet接口的实现类,作用是处理请求并返回结果。在servlet容器接收到一个请求时,servlet容器会针对这个请求创建一个servletRequest和servletRespones对象,相应的处理方法会通过servletRequest中携带的参数对应处理请求,再通过servletRespones对象商城请求的响应结果。

      DispatcherServlet类的doDispatch方法是处理请求的核心逻辑,截取部分内容如下:

       try {//对请求做类型转换processedRequest = checkMultipart(request);multipartRequestParsed = (processedRequest != request);// 根据请求信息找到相应的HandlermappedHandler = getHandler(processedRequest);if (mappedHandler == null) {noHandlerFound(processedRequest, response);return;}// 根据handleru想你找相应的HandlerAdapterHandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());}
      

观察者模式

  • 也被称作发布订阅模式,适用于一个对象的某个行为,需要触发一系列的事件场景

    • 例如GRPC 中stream 流式的请求的处理,就是通过观察者模式实现的。
    • 或者举个 redis 例子:Redis 发布订阅
  • 博客学习:厚积薄发打卡Day45: [itcast] GoF23通俗易懂的设计模式之 <观察者模式>

    1. JDK中的应用:

      在 Java 中,通过 java.util.Observable 类和 java.util.Observer 接口定义了观察者模式,只要实现它们的子类就可以编写观察者模式实例。

      1,Observable类

      • 值得注意的时,void notifyObservers(Object arg) 方法:通常越晚加入集合的观察者越先得到通知:深入查看源码得知时倒序通知。

      2,Observer 接口

      Observer 接口是抽象观察者,它监视目标对象的变化,当目标对象发生变化时,观察者得到通知,并调用 update 方法,进行相应的工作。

      public interface Observer {void update(Observable o, Object arg);
      }
      

      3,举例应用:

      【例】警察抓小偷

      警察抓小偷也可以使用观察者模式来实现,警察是观察者,小偷是被观察者。

      • 警察:

        public class Police implements Observer {private String name;public Police(String name) {this.name = name;}public void setName(String name) {this.name = name;}public String getName() {return name;}@Overridepublic void update(Observable o, Object arg) {System.out.println("警察:" + ((Thief) o).getName() + ",我已经盯你很久了,你可以保持沉默,但你所说的将成为呈堂证供!");}
        }
        
      • 小偷:

        public class Thief extends Observable {//小偷名字private String name;public Thief(String name) {this.name = name;}public void setName(String name) {this.name = name;}public String getName() {return name;}public void steal() {System.out.println("小偷:渣渣们,老子来偷东西了!");super.setChanged(); //changed  = truesuper.notifyObservers();}
        }
        
      • 警察抓小偷:

    2. Spring中的应用:

      1. Spring之事件监听(观察者模型)【波波烤鸭.著】

        1. Spring中事件监听的结构

        2. 总结:

          1. Spring中的事件监听使用的是观察者模式
          2. 所有事件需要继承ApplicationEvent父类
        3. 所有的监听器需要实现ApplicationListener接口

          1. 事件发布需要通过ApplicationContext中的publisherEvent方法实现
          2. 监听器的注册是ApplicationEventMulticaster提供的,但我们并不需要实现。
        4. 扩展:

          1. 三种方式实现观察者模式 及 Spring中的事件编程模型
          2. Spring 应用之观察者设计模式
    3. Servlet中的Listener

      实例解析观察者模式及其在Java设计模式开发中的运用

      在说Servlet中的Listener之前,

      先说说观察者模式的另一种形态——事件驱动模型。与上面提到的观察者模式的主题角色一样, 事件驱动模型包括事件源, 具体事件, 监听器, 具体监听器。
      Servlet中的Listener就是典型的事件驱动模型。

构造者模式

  • 适用于一个对象,有很多复杂的属性,需要根据不同情况创建不同的具体对象
  • 例如创建一个SpringBuilder对象时,可以使用builder 方式
  • 建造者模式-各框架应用场景穷举

厚积薄发打卡Day75 :【MSUP】Java语言特性与设计模式(上)相关推荐

  1. java语言概述、java语言特性、java语言发展史、java语言作用

    Java介绍: Java语言概述: Java语言是由美国Sun(Stanford University Network)斯坦福网络公司的java语言之父–詹姆斯·高斯林,在1995年推出的高级的编程语 ...

  2. Java语言特性和技术特点探究

    Java语言特性和技术特点探究 1   引子 Java一直以来是排名前二的编程语言, 这篇文章我们来学习和探讨一下Java的语言特性和技术特点. 2 通用的Java Java是一种通用的编程语言,它是 ...

  3. Java语言特性运用:各种Java语法特性是怎样被Spring各种版本巧妙运用的?

    Java语法变化 Java5(2004): 枚举.泛型.注解.封箱(解箱)- Java6(2006): @Override接口 Java7(2011): Diamond语法.多Catch.Try- J ...

  4. 头歌Educoder——Java高级特性 - JDBC(上)

    第1关:JDBC连接数据库 任务描述 本关任务:使用jdbc连接数据库并完成创建数据库和创建表的操作. 相关知识 JDBC API提供以下接口和类: DriverManager:此类管理数据库驱动程序 ...

  5. 《Algorithms》Java 语言特性

    算法第4版中出现的特性 泛型 自动装箱 迭代 记录一下.......不时更新.. 泛型 集合类的抽象数据类型的一个关键特性是我们应该可以用它存储任意类型的数据. Java拥有独特的泛型机制(许多语言并 ...

  6. 下列关于java语言特性,下列关于Java 语言特点的叙述中,错误的是()。

    鳃是鱼类的呼吸器官,硬骨鱼类共有______对全鳃,而软骨鱼类共有______个半鳃. 男工人数是女工人数的25,男.女工人数的比是______. 颅内手术后,头部翻转过猛可引起A.脑疝B.休克C.脑 ...

  7. 厚积薄发打卡Day55 :[狂神]Redis详细教程(上)<从Nosql 概述到WSL安装Redis踩坑记录>

    视频教程:[狂神说Java]Redis最新超详细版教程通俗易懂 Nosql概述 时代背景: 1.单机数据库时代 90年代:一个基本的网站访问量一般不会太大,单个数据库完全足够! 那个时候,更多的去使用 ...

  8. Educoder Java高级特性 - JDBC(上)

    第1关:JDBC连接数据库 任务描述 本关任务:使用jdbc连接数据库并完成创建数据库和创建表的操作. 相关知识 JDBC API提供以下接口和类: DriverManager:此类管理数据库驱动程序 ...

  9. java语言【 #96. 天花板上的小灯】(已通过)

    题目描述 ​ 天花板上有一些小灯,如下图排列 ​ 小灯总共有 a 行,b 列,请求出小灯的总数量. 输入 ​ 一行两个整数 a 和 b(1≤a,b≤100000) 输出 ​ 一个整数表示小灯的总数量. ...

最新文章

  1. 向iOS越狱彻底说再见!
  2. centos6.7上使用nginx实现负载均衡!
  3. 临时对象与NRV技术
  4. 【UGV】Arduino Mega2560 获取小车角度信息,传感器JY60
  5. 好看的极简网站导航源码自适应静态页
  6. IntelliJ IDEA上手这一篇就够了,从入门到上瘾
  7. PSD分层电商促销模板|换季大促销,不怕老板催你做海报了
  8. 垃圾回收相关算法总结
  9. 无人驾驶全局路径规划之RRT算法
  10. 2021-2027全球与中国3D 动作捕捉解决方案市场现状及未来发展趋势
  11. 如何选择外贸网站服务器?
  12. 英文信件结尾的表达方式
  13. 计算机热启动方法,关于电脑热启动的介绍
  14. 伊诺伊香槟分校计算机世界排名,2020年伊利诺伊大学香槟分校QS世界排名
  15. Tornado 源码分析(一)
  16. c盘垃圾太多怎么清理?c盘垃圾太多需要重装系统嘛?
  17. 银河麒麟服务器操作系统V10搭建内网YUM源服务器
  18. Arcgis制作风速风向
  19. Microsoft Office InfoPath 2003 简介
  20. Redis常用操作命令

热门文章

  1. 2021新年算法小专题—2.股票买卖利润刷题(Java)
  2. 流媒体客户端的流传送原理
  3. MSP430之BR0、BR1计算
  4. matlab 求概率密度,MATLAB如何使用pdf函数计算指定分布的概率密度函数
  5. NTN(四) RRC related
  6. 矩阵向右旋转90° — C语言
  7. 阿里云域名相关操作(购买、解析、备案)
  8. 去中心化和非去中心化的区别?
  9. 码出高效_第一章 | 有意思的二进制表示及运算
  10. 帝国CMS7.5正式版(后台模板深度美化)