其他分享
首页 > 其他分享> > 设计模式面试突击

设计模式面试突击

作者:互联网

Java面试中设计模式的常见面试题总结。

文章目录

一、设计模式

1.1 什么是设计模式?

设计模式,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性

1.2 设计模式分类

设计模式总共有 23 种,总体来说可以分为三大类:创建型模式( Creational Patterns )结构型模式( Structural Patterns )行为型模式( Behavioral Patterns )
在这里插入图片描述创建型模式,共五种:工厂方法模式抽象工厂模式单例模式、建造者模式、原型模式

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

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

1.3 设计模式的六大原则

在这里插入图片描述
开放封闭原则(Open Close Principle)

里氏代换原则(Liskov Substitution Principle)

依赖倒转原则(Dependence Inversion Principle)

接口隔离原则(Interface Segregation Principle)

迪米特法则(最少知道原则)(Demeter Principle)

单一职责原则(Principle of single responsibility)

1.4 设计模式的优点

二、单例模式

2.1 什么是单例模式?

单例模式是一种常用的软件设计模式,在应用这个模式时,单例对象的类必须保证只有一个实例存在,整个系统只能使用一个对象实例。

2.2 单例模式优缺点及使用场景

优点:不会频繁地创建和销毁对象,浪费系统资源。

缺点:单例类的扩展困难,不适用于变化的对象

使用场景:IO 、数据库连接、Redis 连接、网站的计数器、Windows的(任务管理器)、windows的(回收站)、多线程的线程池的设计等。

2.3 如何创建单例模式

创建方式:(主要使用懒汉和懒汉式)

  1. 饿汉式:类初始化时,会立即加载该对象,线程天生安全,调用效率高。
  2. 懒汉式: 类初始化时,不会初始化该对象,真正需要使用的时候才会创建该对象,具备懒加载功能。
  3. 静态内部方式:结合了懒汉式和饿汉式各自的优点,真正需要对象的时候才会加载,加载类是线程安全的。
  4. 枚举单例: 使用枚举实现单例模式 优点:实现简单、调用效率高,枚举本身就是单例,由jvm从根本上提供保障!避免通过反射和反序列化的漏洞,缺点没有延迟加载。
  5. 双重检测锁方式 (因为JVM本质重排序的原因,可能会初始化多次,不推荐使用)

饿汉式:类初始化时,会立即加载该对象,线程天生安全,调用效率高。

单例模式代码实现:

class Singleton {
    private static Singleton instance = new Singleton();
    public static Singleton getInstance() {
        return instance;
    }
}

单例模式调用代码:

public class Lesson {
    public static void main(String[] args) {
        Singleton singleton1 = Singleton.getInstance();
        Singleton singleton2 = Singleton.getInstance();
        System.out.println(singleton1 == singleton2); 
    }
}

程序的输出结果:true

可以看出以上单例模式是在类加载的时候就创建了,这样会影响程序的启动速度,那如何实现单例模式的延迟加载?在使用时再创建?

单例延迟加载代码:

// 单例模式-延迟加载版
class SingletonLazy {
    private static SingletonLazy instance;
    public static SingletonLazy getInstance() {
        if (instance == null) {
            instance = new SingletonLazy();
        }
        return instance;
    }
}

以上为非线程安全的,单例模式如何支持多线程?

使用 synchronized 来保证,单例模式的线程安全代码:

class SingletonLazy {
    private static SingletonLazy instance;
    public static synchronized SingletonLazy getInstance() {
        if (instance == null) {
            instance = new SingletonLazy();
        }
        return instance;
    }
}

三、简单工厂模式

3.1 什么是简单工厂模式?

简单工厂模式又叫静态工厂方法模式,就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建。比如,一台咖啡机就可以理解为一个工厂模式,你只需要按下想喝的咖啡品类的按钮(摩卡或拿铁),它就会给你生产一杯相应的咖啡,你不需要管它内部的具体实现,只要告诉它你的需求即可。

3.2 简单工厂模式优缺点及使用场景

优点:

缺点:

3.3 如何创建简单工厂模式

简单工厂代码实现:

class Factory {
    public static String createProduct(String product) {
        String result = null;
        switch (product) {
            case "Mocca":
                result = "摩卡";
                break;
            case "Latte":
                result = "拿铁";
                break;
            default:
                result = "其他";
                break;
        }
        return result;
    }
}

四、抽象工厂模式

4.1 什么是抽象工厂模式?

抽象工厂模式是在简单工厂的基础上将未来可能需要修改的代码抽象出来,通过继承的方式让子类去做决定。

比如,以上面的咖啡工厂为例,某天我的口味突然变了,不想喝咖啡了想喝啤酒,这个时候如果直接修改简单工厂里面的代码,这种做法不但不够优雅,也不符合软件设计的“开闭原则”,因为每次新增品类都要修改原来的代码。这个时候就可以使用抽象工厂类了,抽象工厂里只声明方法,具体的实现交给子类(子工厂)去实现,这个时候再有新增品类的需求,只需要新创建代码即可。

4.2 抽象工厂模式优缺点及使用场景

4.3 如何创建抽象工厂模式

抽象工厂实现代码如下:

public class AbstractFactoryTest {
   public static void main(String[] args) {
       // 抽象工厂
       String result = (new CoffeeFactory()).createProduct("Latte");
       System.out.println(result); // output:拿铁
   }
}
// 抽象工厂
abstract class AbstractFactory{
   public abstract String createProduct(String product);
}
// 啤酒工厂
class BeerFactory extends AbstractFactory{
   @Override
   public String createProduct(String product) {
       String result = null;
       switch (product) {
           case "Hans":
               result = "汉斯";
               break;
           case "Yanjing":
               result = "燕京";
               break;
           default:
               result = "其他啤酒";
               break;
       }
       return result;
   }
}
// 咖啡工厂
class CoffeeFactory extends AbstractFactory{
   @Override
   public String createProduct(String product) {
       String result = null;
       switch (product) {
           case "Mocca":
               result = "摩卡";
               break;
           case "Latte":
               result = "拿铁";
               break;
           default:
               result = "其他咖啡";
               break;
       }
       return result;
   }
}

五、观察者模式

5.1 什么是观察者模式?

观察者模式是定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。观察者模式又叫做发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。

5.2 观察者模式优缺点及使用场景

优点:

缺点:

5.3 如何创建观察者模式

在观察者模式中有如下角色:

观察者模式实现代码如下。

1)定义观察者(消息接收方)

// 观察者(消息接收方)
interface Observer {
    public void update(String message);
}
// 具体的观察者(消息接收方)
class ConcrereObserver implements Observer {
    private String name;

    public ConcrereObserver(String name) {
        this.name = name;
    }

    @Override
    public void update(String message) {
        System.out.println(name + ":" + message);
    }
}

2)定义被观察者(消息发送方)

// 被观察者(消息发布方)
interface Subject {
    // 增加订阅者
    public void attach(Observer observer);
    // 删除订阅者
    public void detach(Observer observer);
    // 通知订阅者更新消息
    public void notify(String message);
}
// 具体被观察者(消息发布方)
class ConcreteSubject implements Subject {
    // 订阅者列表(存储信息)
    private List<Observer> list = new ArrayList<Observer>();
    @Override
    public void attach(Observer observer) {
        list.add(observer);
    }
    @Override
    public void detach(Observer observer) {
        list.remove(observer);
    }
    @Override
    public void notify(String message) {
        for (Observer observer : list) {
            observer.update(message);
        }
    }
}

3)代码调用

public class ObserverTest {
    public static void main(String[] args) {
        // 定义发布者
        ConcreteSubject concreteSubject = new ConcreteSubject();
        // 定义订阅者
        ConcrereObserver concrereObserver = new ConcrereObserver("老王");
        ConcrereObserver concrereObserver2 = new ConcrereObserver("Java");
        // 添加订阅
        concreteSubject.attach(concrereObserver);
        concreteSubject.attach(concrereObserver2);
        // 发布信息
        concreteSubject.notify("更新了");
    }
}

程序执行结果如下:

老王:更新了

Java:更新了

六、装饰器模式

6.1 什么是装饰器模式?

装饰器模式是指动态地给一个对象增加一些额外的功能,同时又不改变其结构。

6.2 装饰器模式优缺点及使用场景

优点:装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。

装饰器模式的关键:装饰器中使用了被装饰的对象。

比如,创建一个对象“laowang”,给对象添加不同的装饰,穿上夹克、戴上帽子…,这个执行过程就是装饰者模式,

6.3 如何创建装饰器模式

实现代码如下。

1)定义顶层对象,定义行为

interface IPerson {
    void show();
}

2)定义装饰器超类

class DecoratorBase implements IPerson{
    IPerson iPerson;
    public DecoratorBase(IPerson iPerson){
        this.iPerson = iPerson;
    }
    @Override
    public void show() {
        iPerson.show();
    }
}

3)定义具体装饰器

class Jacket extends DecoratorBase {
    public Jacket(IPerson iPerson) {
        super(iPerson);
    }
    @Override
    public void show() {
        // 执行已有功能
        iPerson.show();
        // 定义新行为
        System.out.println("穿上夹克");
    }
}
class Hat extends DecoratorBase {
    public Hat(IPerson iPerson) {
        super(iPerson);
    }
    @Override
    public void show() {
        // 执行已有功能
        iPerson.show();
        // 定义新行为
        System.out.println("戴上帽子");
    }
}

4)定义具体对象

class LaoWang implements IPerson{
    @Override
    public void show() {
        System.out.println("什么都没穿");
    }
}

5)装饰器模式调用

public class DecoratorTest {
    public static void main(String[] args) {
        LaoWang laoWang = new LaoWang();
        Jacket jacket = new Jacket(laoWang);
        Hat hat = new Hat(jacket);
        hat.show();
    }
}

七、模板方法模式

7.1 什么是模板方法模式?

模板方法模式是指定义一个模板结构,将具体内容延迟到子类去实现。

7.2 模板方法模式优缺点及使用场景

优点

以给冰箱中放水果为例,比如,我要放一个香蕉:开冰箱门 → 放香蕉 → 关冰箱门;如果我再要放一个苹果:开冰箱门 → 放苹果 → 关冰箱门。可以看出它们之间的行为模式都是一样的,只是存放的水果品类不同而已,这个时候就非常适用模板方法模式来解决这个问题

7.3 如何创建模板方法模式

实现代码如下:

// 添加模板方法 
abstract class Refrigerator {
    public void open() {
        System.out.println("开冰箱门");
    }
    public abstract void put();

    public void close() {
        System.out.println("关冰箱门");
    }
}
class Banana extends Refrigerator {
    @Override
    public void put() {
        System.out.println("放香蕉");
    }
}
class Apple extends Refrigerator {
    @Override
    public void put() {
        System.out.println("放苹果");
    }
}
// 调用模板方法 
public class TemplateTest {
    public static void main(String[] args) {
        Refrigerator refrigerator = new Banana();
        refrigerator.open();
        refrigerator.put();
        refrigerator.close();
    }
}

程序执行结果:

开冰箱门

放香蕉

关冰箱门

八、代理模式

8.1 什么是代理模式?

代理模式是给某一个对象提供一个代理,并由代理对象控制对原对象的引用。

8.2 代理模式优缺点及使用场景

优点:

缺点:

举一个生活中的例子:比如买飞机票,由于离飞机场太远,直接去飞机场买票不太现实,这个时候我们就可以上携程 App 上购买飞机票,这个时候携程 App 就相当于是飞机票的代理商。

8.3 如何创建代理模式

代理模式实现代码如下:

// 定义售票接口 
interface IAirTicket {
    void buy();
}
// 定义飞机场售票 
class AirTicket implements IAirTicket {
    @Override
    public void buy() {
        System.out.println("买票");
    }
}
// 代理售票平台
class ProxyAirTicket implements IAirTicket {
    private AirTicket airTicket;
    public ProxyAirTicket() {
        airTicket = new AirTicket();
    }
    @Override
    public void buy() {
        airTicket.buy();
    }
}
// 代理模式调用 
public class ProxyTest {
    public static void main(String[] args) {
        IAirTicket airTicket = new ProxyAirTicket();
        airTicket.buy();
    }
}

九、策略模式

9.1 什么是策略模式?

策略模式是指定义一系列算法,将每个算法都封装起来,并且使他们之间可以相互替换。

9.2 策略模式优缺点及使用场景

优点:遵循了开闭原则,扩展性良好。

缺点:随着策略的增加,对外暴露越来越多。

以生活中的例子来说,比如我们要出去旅游,选择性很多,可以选择骑车、开车、坐飞机、坐火车等,就可以使用策略模式,把每种出行作为一种策略封装起来,后面增加了新的交通方式了,如超级高铁、火箭等,就可以不需要改动原有的类,新增交通方式即可,这样也符合软件开发的开闭原则。

9.3 如何创建策略模式

策略模式实现代码如下:

// 声明旅行 
interface ITrip {
    void going();
}
class Bike implements ITrip {
    @Override
    public void going() {
        System.out.println("骑自行车");
    }
}
class Drive implements ITrip {
    @Override
    public void going() {
        System.out.println("开车");
    }
}
// 定义出行类 
class Trip {
    private ITrip trip;

    public Trip(ITrip trip) {
        this.trip = trip;
    }

    public void doTrip() {
        this.trip.going();
    }
}
// 执行方法 
public class StrategyTest {
    public static void main(String[] args) {
        Trip trip = new Trip(new Bike());
        trip.doTrip();
    }
}

程序执行的结果:

骑自行车

十、适配器模式

10.1 什么是适配器模式?

适配器模式是将一个类的接口变成客户端所期望的另一种接口,从而使原本因接口不匹配而无法一起工作的两个类能够在一起工作。

10.2 适配器模式优缺点及使用场景

优点:

**缺点:**过多地使用适配器,容易使代码结构混乱,如明明看到调用的是 A 接口,内部调用的却是 B 接口的实现。

以生活中的例子来说,比如有一个充电器是 MicroUSB 接口,而手机充电口却是 TypeC 的,这个时候就需要一个把 MicroUSB 转换成 TypeC 的适配器

10.3 如何创建适配器模式

适配器实现代码如下:

// 传统的充电线 MicroUSB 
interface MicroUSB {
    void charger();
}
// TypeC 充电口 
interface ITypeC {
    void charger();
}
class TypeC implements ITypeC {
    @Override
    public void charger() {
        System.out.println("TypeC 充电");
    }
}
// 适配器 
class AdapterMicroUSB implements MicroUSB {
    private TypeC typeC;

    public AdapterMicroUSB(TypeC typeC) {
        this.typeC = typeC;
    }

    @Override
    public void charger() {
        typeC.charger();
    }
}
// 测试调用 
public class AdapterTest {
    public static void main(String[] args) {
        TypeC typeC = new TypeC();
        MicroUSB microUSB = new AdapterMicroUSB(typeC);
        microUSB.charger();

    }
}

程序执行结果:

TypeC 充电

十一、常见设计模式面试问题

11.1 JDK 类库常用的设计模式有哪些?

1)工厂模式

java.text.DateFormat 工具类,它用于格式化一个本地日期或者时间。

public final static DateFormat getDateInstance();
public final static DateFormat getDateInstance(int style);
public final static DateFormat getDateInstance(int style,Locale locale);

加密类

KeyGenerator keyGenerator = KeyGenerator.getInstance("DESede");
Cipher cipher = Cipher.getInstance("DESede");
2)适配器模式

把其他类适配为集合类

List<Integer> arrayList = java.util.Arrays.asList(new Integer[]{1,2,3});
List<Integer> arrayList = java.util.Arrays.asList(1,2,3);
3)代理模式

如 JDK 本身的动态代理。

interface Animal {
    void eat();
}
class Dog implements Animal {
    @Override
    public void eat() {
        System.out.println("The dog is eating");
    }
}
class Cat implements Animal {
    @Override
    public void eat() {
        System.out.println("The cat is eating");
    }
}

// JDK 代理类
class AnimalProxy implements InvocationHandler {
    private Object target; // 代理对象
    public Object getInstance(Object target) {
        this.target = target;
        // 取得代理对象
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("调用前");
        Object result = method.invoke(target, args); // 方法调用
        System.out.println("调用后");
        return result;
    }
}

public static void main(String[] args) {
    // JDK 动态代理调用
    AnimalProxy proxy = new AnimalProxy();
    Animal dogProxy = (Animal) proxy.getInstance(new Dog());
    dogProxy.eat();
}
4)单例模式

全局只允许有一个实例,比如:

Runtime.getRuntime();
5)装饰器

为一个对象动态的加上一系列的动作,而不需要因为这些动作的不同而产生大量的继承类。

java.io.BufferedInputStream(InputStream);  
java.io.DataInputStream(InputStream);  
java.io.BufferedOutputStream(OutputStream);  
java.util.zip.ZipOutputStream(OutputStream);  
java.util.Collections.checkedList(List list, Class type) ;
6)模板方法模式

定义一个操作中算法的骨架,将一些步骤的执行延迟到其子类中。

比如,Arrays.sort() 方法,它要求对象实现 Comparable 接口。

class Person implements Comparable{
    private Integer age;
    public Person(Integer age){
        this.age = age;
    }
    @Override
    public int compareTo(Object o) {
        Person person = (Person)o;
        return this.age.compareTo(person.age);
    }
}
public class SortTest(){
    public static void main(String[] args){
        Person p1 = new Person(10);
        Person p2 = new Person(5);
        Person p3 = new Person(15);
        Person[] persons = {p1,p2,p3};
        //排序
        Arrays.sort(persons);
    }
}

11.2 IO 使用了什么设计模式?

IO 使用了适配器模式和装饰器模式

11.3 Spring 中都使用了哪些设计模式?

Spring 框架使用的设计模式如下。

11.4 代理模式有哪些类型?

11.5 mvc模式

MVC 模式代表 Model-View-Controller(模型-视图-控制器) 模式。这种模式用于应用程序的分层开发。

参考

设计模式面试题(总结最全面的面试题!!!)

设计模式常见面试题汇总

设计模式常见面试题

标签:void,观察者,模式,class,面试,突击,设计模式,public
来源: https://blog.csdn.net/weixin_44052055/article/details/123029689