【Java面试八股文】设计模式篇

  导航: 

【黑马Java笔记+踩坑汇总】JavaSE+JavaWeb+SSM+SpringBoot+瑞吉外卖+SpringCloud+黑马旅游+谷粒商城+学成在线+设计模式+牛客面试题

目录

谈谈你对设计模式的理解?

谈谈你对单例模式的理解?

手写一下单例模式

谈谈你对工厂模式的理解?

手写一下工厂模式

谈谈你对代理模式的理解?

谈谈你对模板模式的理解?

谈谈你对观察者模式的理解?

谈谈JDK中用到的设计模式?

谈谈Spring中用到的设计模式?


谈谈你对设计模式的理解?

关键字:总结的设计经验、通用解决方案、维护性通用性扩展性、降低复杂度、具体问题具体分析;七大原则、三种类型。

设计模式:

设计模式是程序员在面对同类软件工程设计问题所总结出来的有用的经验。模式不是代码,而是某类问题的通用解决方案,设计模式(Design pattern)代表了最佳实践。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。

设计模式的本质提高软件的维护性、通用性和扩展性,并降低软件的复杂度。

过度地使用设计模式可能会导致代码的过度抽象,增加了代码的维护难度和学习成本。因此,在使用设计模式时需要根据具体的项目需要和实际情况进行选择。

七大原则:

  • 单一职责原则:一个类应该只负责一项职责。提高可读性维护性、降低耦合。
  • 接口隔离原则:类之间的依赖关系应该建立在最小的接口上。例如实现类只能用到接口的部分方法,为了降低冗余提高可维护性,对原接口进行合理的拆分和组合。
  • 依赖倒转原则:高低层模块应该依赖于抽象,细节应该依赖抽象。
  • 里氏代换原则:父类型对象替换成子类型对象后功能未变,子类中尽量不要重写父类的方法。
  • 开闭原则:软件对扩展开放,对修改关闭。提高扩展性和可维护性。
  • 迪米特法则:最少知道原则,一个类对自己依赖的类知道的越少越好,只与直接的朋友(成员变量,方法参数,方法返回值的类)通信。
  • 合成复用原则:尽量使用合成/聚合的方式,而不是使用继承。也就是把需要用到的类作为本类的参数、成员变量、局部变量。

设计模式类型:

  • 创建型模式:用于对象的创建,包括单例、工厂等模式。
  • 结构型模式:用于描述对象之间的组合关系,包括代理等模式
  • 行为型模式:用于描述对象之间的通信和责任分配。包括模版方法、观察者、责任链等模式

谈谈你对单例模式的理解?

关键字:概念(一个实例、一个静态方法)、实现方案、优缺点(节省资源、避免重复创建对象、管理全局变量、注意线程安全)、场景(重量级对象)

保证一个类只有一个实例,并且只提供一个取得对象实例的静态方法。

实现方式: 饿汉懒汉、双重检查、静态内部类、枚举。

优点:

  • 节省资源:单例模式实例只有一个,可以避免重复创建对象,从而节省了资源,提高了系统性能。
  • 管理全局变量:单例模式可以用于管理全局状态和变量,方便在整个系统中共享数据
  • 简化系统架构:使用单例模式可以简化系统架构,减少类的数量和接口的复杂度。

缺点:

  1. 可能引发并发问题:单例模式在多线程中使用时,需要保证线程安全,否则可能会引发并发问题。
  2. 可能增加系统复杂性:过度使用单例模式可能会增加系统复杂性,导致代码难以维护。
  3. 难以调试:由于单例模式全局共享状态,可能会导致调试过程中的问题难以定位和测试。

使用场景:

需要频繁的进行创建和销毁的对象、创建对象时耗时过多或耗费资源过多但又经常用到的对象(即:重量级对象)、工具类对象、频繁访问数据库或文件的对象(比如数据源、session 工厂等)
 

手写一下单例模式

实现方式: 饿汉懒汉、双重检查、静态内部类、枚举。

饿汉(静态常量版):线程安全,没用到会浪费内存。

public class Singleton {
    // 1、构造器私有化
    private Singleton() {
    }

    // 2、类的内部创建对象
    private static final Singleton instance = new Singleton();

    // 3、向外暴露一个静态的公共方法
    public static Singleton getInstance() {
        return instance;
    }
}

懒汉(同步方法版):懒加载,线程安全,但效率低(每次获取实例都要加锁),不推荐。

public class Singleton {
    // 1、构造器私有化
    private Singleton() {
    }

    // 2、类的内部声明对象
    private static Singleton instance;

    // 3、向外暴露一个静态的公共方法,加入同步处理的代码,解决线程安全问题
    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

双重检查:线程安全、懒加载

public class Singleton {
    // 1、构造器私有化
    private Singleton() {
    }

    // 2、类的内部声明对象,同时用`volatile`关键字修饰,为了保证可见性。
//原子性、可见性(修改立即更新到内存)、有序性
    private static volatile Singleton instance;

    // 3、向外暴露一个静态的公共方法,加入同步处理的代码块,并进行双重判断,解决线程安全问题
    public static Singleton getInstance() {
        if (instance == null) {    //第一次检查,可能有多个线程同时通过检查
//类级别的锁对象,锁对象是全局的,对该类的所有实例均有效。回顾锁对象是this时仅限于锁当前实例
            synchronized (Singleton.class) {    
                if (instance == null) {   //第二次检查,只会有1个线程通过检查并创建实例
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

静态内部类:线程安全、延迟加载、效率高。

public class Singleton {
    // 1、构造器私有化
    private Singleton() {
    }

    // 2、定义一个静态内部类,内部定义当前类的静态属性
    private static class SingletonInstance {
        private static final Singleton instance = new Singleton();
    }

    // 3、向外暴露一个静态的公共方法
    public static Singleton getInstance() {
        return SingletonInstance.instance;
    }
}

枚举:线程安全,延迟加载。

public enum Singleton {
    INSTANCE;
}

谈谈你对工厂模式的理解?

关键字:工厂类创建对象、工厂、抽象工厂 、复用性可维护性、具体问题具体分析。

以提高复杂性为代价,提高可维护性和复用性。 

工厂模式是一种创建型设计模式,它主要解决了对象的创建过程中的灵活性和可维护性问题。工厂模式允许在不暴露对象创建逻辑的情况下,统一由工厂类负责创建对象并返回,从而降低了代码的耦合性。

简单工厂模式 

简单工厂模式是通过工厂类(抽象或非抽象)的静态方法来创建对象实例,并将实例作为方法的返回值。在使用时,只需要调用该静态方法来创建对象,而无需创建工厂类的实例。

工厂方法模式

工厂方法模式是在抽象工厂类里定义创建抽象产品对象的抽象方法,由具体工厂类决定要实例化的产品类。抽象工厂类的构造器里调用“创建抽象产品对象”的抽象方法,具体工厂类重写抽象方法,按情景实例化具体产品类。使用时直接创建具体工厂对象,将执行抽象工程类构造器调用重写后的创建方法,创建具体产品对象。

一个抽象工厂类能够派生出多个具体工厂类。每一个具体工厂类只能建立一个具体产品类的实例。一个抽象产品类能够派生出多个具体产品类。 

抽象工厂模式

抽象工厂模式是抽象工厂接口里定义创建抽象产品对象的方法,各具体工厂类根据情景创建具体产品对象。

一个抽象工厂类能够派生出多个具体工厂类。每一个具体工厂类能够建立多个具体产品类的实例。多个抽象产品类,每一个抽象产品类能够派生出多个具体产品类。

优点:
1. 可以避免直接使用new关键字创建对象带来的耦合性,提高了代码的可维护性
2. 可以将对象的创建逻辑封装到一个工厂类中,提高了代码的复用性
3. 可以对对象的创建逻辑进行统一管理,方便代码的维护和升级。

缺点:
1. 增加了代码的复杂度,需要创建工厂类,会增加代码规模
2. 如果产品类发生变化,需要修改工厂类,可能会影响到其他代码的功能

综上所述,工厂模式是一种常用的创建型设计模式,可以提高代码的可维护性、复用性和灵活性。但是,在使用时需要权衡利弊,避免过度使用,增加代码的复杂度。

手写一下工厂模式

简单工厂模式:

//简单静态工厂
public class PizzaFactory {
    public static Pizza createPizza2(String orderType) {
        Pizza pizza = null;
        switch (orderType) {
            case "cheese":
                pizza = new CheesePizza();
                break;
            case "greek":
                pizza = new GreekPizza();
                break;
            case "pepper":
                pizza = new PepperPizza();
                break;
            default:
                break;
        }
        return pizza;
    }
}
//订购披萨类
public class OrderPizza {
    public OrderPizza() {
        Pizza pizza=null;
        do {
//直接通过静态方法创建工厂对象,不用再像之前通过“构造器参数赋值成员变量”方式创建对象。
            pizza = PizzaFactory.createPizza(getType());    
            if (pizza == null) {
                System.out.println("Failed to Order Pizza");
            } else {
                pizza.prepare();
                pizza.bake();
                pizza.cut();
                pizza.box();
            }
        } while (true);
    }
}

谈谈你对代理模式的理解?

关键字:结构性、代理对象、增强、控制远程对象、安全控制;静态代理、相同代理接口;JDK动态代理、反射、内存、代理工厂、Proxy.newProxyInstance()、Spring AOP;Cglib 代理、子类对象、代理工厂、ASM框架、MethodInterceptor接口的intercept()方法、cglib包的Enhancer工具类

代理模式是结构型设计模式(用于描述对象之间的组合关系)。

代理模式:为一个对象提供一个替身,以控制对这个对象的访问。即通过代理对象访问目标对象。

好处:

可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能

被代理的对象可以是远程对象、创建开销大的对象或需要安全控制的对象。(代理时对目标对象进行安全控制)

静态代理:

目标对象与代理对象实现租同的接口或继承相同的父类,在编译时生成代理对象。

目标对象实现代理接口,代理对象实现并聚合代理接口,重写方法编写增强后逻辑。

JDK动态代理:

通过Java反射机制在运行时动态地在内存中生成代理对象。 目标对象需要实现代理接口。

目标对象实现代理接口,代理工厂通过Proxy类的静态方法newProxyInstance(),利用反射机制返回代理对象实例。  

newProxyInstance()三个参数:目标对象的类加载器、目标对象的接口、实现InvocationHandler接口并重写invoke()方法,编写代理对象逻辑。

Spring AOP采用了JDK动态代理的方式,在运行时动态的创建代理对象来实现增强。

Cglib 代理:

内存中构建一个子类对象从而实现对目标对象功能扩展。目标对象不需要实现代理接口。底层是通过使用ASM框架转换字节码并生成新的类。

代理工厂类实现MethodInterceptor接口并重写intercept()方法编写代理逻辑,通过cglib包的Enhancer类设置父类字节码文件和创建子类对象来返回代理对象实例。

ASM框架是一个强大的Java字节码操作框架,可以让程序员通过代码生成和转换现有字节码来操作Java类。ASM可以直接生成字节码,也可以通过访问现有字节码来修改它。

谈谈你对模板模式的理解?

介绍:

模板模式是行为型模式(用于描述对象之间的通信和责任分配)。

它定义了一个框架或算法骨架,将一些步骤延迟到子类中实现。

实现方式:抽象类有一个模板方法和其他行为方法,模板方法按流程调用各行为方法(抽象或非抽象);具体子类重写抽象的行为方法。 

优点:

  1. 由于算法的骨架在父类或者抽象类中实现,因此可以避免在每个子类中重复编写相同的代码,提高了代码的可重用性和可维护性
  2. 由于子类只需要实现特定的部分而不需要修改算法的整体结构,因此可以提高代码的安全性和可扩展性;
  3. 可以在父类或者抽象类中控制算法的结构和执行流程,从而保证算法的一致性和稳定性

谈谈你对观察者模式的理解?

关键字: 行为型、一对多、两个接口、主题、注册、移除、通知观察者更新;观察者、更新;耦合、开闭原则、复杂度;JDK、Observable、Observer

观察者模式是一种行为型设计模式(用于描述对象之间的通信和责任分配,它定义了对象之间一对多的依赖关系,使得当主题对象状态发生改变时,所有依赖于它的观察者对象都能够得到通知并自动更新。 

实现方法:

主题对象实现主题接口的注册、移除、通知方法,并管理资源和观察者列表;

观察者对象实现观察者接口的更新方法,并管理资源;

主题对象通知方法:遍历观察者列表执行更新方法。

优点:

1. 降低了对象之间的耦合度,因为主题对象不需要知道观察者的具体实现,只需要知道观察者实现了一个特定接口即可。

2. 可以动态扩展观察者列表,方便灵活。

3. 实现了对象之间的一对多依赖关系,提高了系统的可维护性和可重用性。遵守了ocp原则(开闭原则:对扩展开放,对修改关闭)。

缺点

1. 当观察者过多时,通知过程需要花费较多的时间,会影响系统的性能。

2. 如果观察者与主题对象之间存在循环依赖,可能会出现死循环

JDK提供观察者模式基础功能的主题抽象类和观察者接口:

Observable抽象类简单实现了主题对象基础功能(注册、移除、通知);

Observer即观察者接口,具有update()方法  。

谈谈JDK中用到的设计模式?

静态简单工厂模式 

Calendar 类中,使用了静态简单工厂模式:由一个工厂对象(可以是抽象类,可以是非抽象类)决定创建出哪一种产品类的实例

Calendar 类是一个抽象的工厂类,用于根据时区和地区创建出具体的日期类对象。 

其中getInstance()静态方法用于返回具体日期类的实例,它调用createCalendar(时区,地区)方法创建具体日期类,例如JapaneseImperialCalendar日本国日期类

观察者模式

JDK提供观察者模式基础功能的主题抽象类和观察者接口:

Observable抽象类简单实现了主题对象基础功能(注册、移除、通知);

Observer即观察者接口,具有update()方法  。

谈谈Spring中用到的设计模式?

Spring AOP采用了JDK动态代理的方式,在运行时动态的创建代理对象来实现增强。

猜你喜欢

转载自blog.csdn.net/qq_40991313/article/details/130435077