设计模式在面试中的高频考点

这块在面试中一般是以场景的形式提问,比如问你的项目里有用到设计模式吗?或者是结合Spring问你知道Spring中常见的设计模式吗?或者其他的考点中涉及到设计模式相关的,面试官都有可能问的,所以这块在面试中相对是比较灵活的。

1、你了解的设计模式有哪些?

回答:总的设计模式有23种,可以分为三大类。(建议在面试的时候说几个自己熟悉的,比如单例模式、工厂模式、模板模式等)

创建型模式(共五种):工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。

结构型模式(共七种):适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

行为型模式(共十一种):策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

在问Spring的时候可能会问到这样一个问题:在Spring框架中都用到了哪些设计模式,并举例说明?

  • 工厂设计模式 : Spring使用工厂模式通过 BeanFactoryApplicationContext 创建 bean 对象。
  • 代理设计模式 : Spring AOP 功能的实现。
  • 单例设计模式: Spring 中的 Bean 默认都是单例的。
  • 包装器设计模式 : 我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源。
  • 观察者模式: Spring 事件驱动模型就是观察者模式很经典的一个应用。
  • 适配器模式:Spring AOP 的增强或通知(Advice)使用到了适配器模式、spring MVC 中也是用到了适配器模式适配Controller

2、设计模式的原则有哪些?

回答:设计模式共有六大原则,分别是:单一职责原则、开闭原则、里氏代换原则、依赖倒转原则、接口隔离原则、迪米特法则。

单一职责:一个类只负责一个功能领域中相应的职责,或者可以定义为:就一个类而言,应该只有一个引起它变化的原因。

开闭原则:软件实体应该对扩展开放,对修改关闭。其含义是说一个软件实体应该通过扩展来实现变化,而不是通过修改已有的代码来实现变化。

里氏代换原则:通俗点讲,只要父类能出现的地方子类就可以出现,而且替换为子类也不会产生任何错误或异常,使用者可能根本就不需要知道是父类还是子类。但是,反过来就不行了,有子类出现的地方,父类未必就能适应。

依赖倒转原则:这个是开闭原则的基础,具体内容:真对接口编程,依赖于抽象而不依赖于具体。

接口隔离原则:使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的意思,从这儿我们看出,其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便。所以上文中多次出现:降低依赖,降低耦合。

迪米特法则:为什么叫最少知道原则,就是说:一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。

3、你比较了解哪些设计模式?

回答:这里大家一定要去找几个设计模式看看,比如单例、工厂和模板这些,这里就以单例模式给大家分享一下。

追问:写一下单例模式的代码?

懒汉式-线程不安全

public class Singleton {

    private static Singleton Instance;

    private Singleton() {
    }

    public static Singleton GetInstance() {
        if (Instance == null) {
            Instance = new Singleton();
        }
        return Instance;
    }
}

优缺点:
私有静态变量 uniqueInstance 被延迟实例化,这样做的好处是,如果没有用到该类,那么就不会实例化 uniqueInstance,从而节约资源。

线程不安全,多线程情况下会多次创建实例。

饿汉式-线程安全

public class Singleton {

    private static Singleton Instance = new Singleton();

    private Singleton() {
    }

    public static Singleton GetInstance() {
        return Instance;
    }

}
优缺点:
采取直接实例化 uniqueInstance 的方式就不会产生线程不安全问题。

但是直接实例化的方式也丢失了延迟实例化带来的节约资源的好处。

懒汉式-线程安全

private static Singleton Instance;

public static synchronized Singleton getInstance() {
    if (Instance == null) {
        Instance = new Singleton();
    }
    return Instance;
}

双重校验锁-线程安全

public class Singleton {

    private volatile static Singleton Instance;

    private Singleton() {
    }

    public static Singleton getInstance() {
        if (Instance == null) {//先判断实例是否存在,不存在再加锁
            synchronized (Singleton.class) {//由于Instace实例有没有被创建过实例不知道,只能对其clss加锁
                if (Instance == null) {//当多个线程的时候,只有一个进入,避免多次创建对象!
                    Instance = new Singleton();
                }
            }
        }
        return Instance;
    }
}

追问:这里面试官可能会问使用volatile的好处?

Instance 采用 volatile 关键字修饰也是很有必要的, Instance = new Singleton(); 这段代码其实是分为三步执行:

  1. 为 uniqueInstance 分配内存空间
  2. 初始化 uniqueInstance
  3. 将 uniqueInstance 指向分配的内存地址

但是由于 JVM 具有指令重排的特性,执行顺序有可能变成 1>3>2。指令重排在单线程环境下不会出现问题,但是在多线程环境下会导致一个线程获得还没有初始化的实例。例如,线程 T1 执行了 1 和 3,此时 T2 调用 geteInstance() 后发现 Instance 不为空,因此返回 Instance,但此时 Instance 还未被初始化。

使用 volatile 可以禁止 JVM 的指令重排,保证在多线程环境下也能正常运行。

推荐阅读

#高频知识点汇总##学习路径#
全部评论
🎉恭喜牛友成功参与 【创作激励计划】高频知识点汇总专场,并通过审核! ------------------- 创作激励计划5大主题专场等你来写,最高可领取500元京东卡和500元实物奖品! 👉快来参加吧:https://www.nowcoder.com/discuss/804743
点赞 回复
分享
发布于 2021-12-06 11:29

相关推荐

16 107 评论
分享
牛客网
牛客企业服务