设计模式(含7大原则)面试题

目录

主要参考文章

设计模式的目的

设计模式的七大原则

设计模式的三大分类及关键点

1、创建型模式(用于解耦对象的实例化过程)

2、结构型模式

3、行为型模式

23种设计模式(乱序--现学现写,不全面--应付面试为主)

单例模式

模板模式

哈哈哈哈哈


声明

此文只针对我个人的目前的理解程度来记录的,有的我知道的或者觉得没有必要写的内容我就会省略掉。并且主要针对面试,介绍的可能不是很全面,甚至有的地方不是很正确,请包涵。

主要参考文章

设计模式23模式介绍-CSDN博客

12-模板方法模式_12(模板方法模式)-CSDN博客

设计模式的目的

  • 可重用性 (即:相同功能的代码,不用多次编写)
  • 可扩展性 (即:当需要增加新的功能时,非常的方便,称为可维护)
  • 可靠性 (即:当我们增加新的功能后,对原来的功能没有影响)
  • 可读性 (即:编程规范性, 便于其他程序员的阅读和理解)
  • 使程序呈现高内聚,低耦合的特性。

设计模式的七大原则

固定记忆:单 开 里 依 接 合 迪。

设计模式的三大分类及关键点

1、创建型模式(用于解耦对象的实例化过程

单例模式某个类只能有一个实例,提供一个全局的访问点
工厂模式一个工厂类根据传入的参量决定创建出哪一种产品类的实例
抽象工厂模式创建相关或依赖对象的家族,而无需明确指定具体类
建造者模式封装一个复杂对象的创建过程,并可以按步骤构造
原型模式通过复制现有的实例来创建新的实例

2、结构型模式

装饰器模式动态的给对象添加新的功能
代理模式不修改原始对象,通过代理对象来间接访问原始对象,并在访问前后执行额外的操作
桥接模式将抽象部分和它的实现部分分离,使它们都可以独立的变化
适配器模式将一个类的方法接口转换成客户希望的另一个接口
组合模式将对象组合成树形结构以表示“部分-整体”的层次结构
外观模式对外提供一个统一的方法,来访问子系统中的一群接口
享元模式通过共享技术来有效的支持大量细粒度的对象

3、行为型模式

策略模式定义一系列算法,把他们封装起来,并且使它们可以相互替换
模板模式抽象父类定义一个算法,子类在不改变该算法结构的情况下重定义该算法的某些步骤
命令模式将命令请求封装为一个对象,使得可以用不同的请求来进行参数化
迭代器模式一种遍历访问聚合对象中各个元素的方法,不暴露该对象的内部结构
观察者模式对象间的一对多的依赖关系
仲裁者模式用一个中介对象来封装一系列的对象交互
备忘录模式在不破坏封装的前提下,保持对象的内部状态
解释器模式给定一个语言,定义它的文法的一种表示,并定义一个解释器
建造者模式允许一个对象在其对象内部状态改变时改变它的行为
责任链模式将请求的发送者和接收者解耦,使的多个对象都有处理这个请求的机会
访问者模式不改变数据结构的前提下,增加作用于一组对象元素的新功能


23种设计模式(乱序--现学现写,不全面--应付面试为主)

单例模式--创建型

某个类只能有一个实例,提供一个全局的访问点

单例模式要素:

  • 私有构造方法;
  • 私有静态引用指向自己实例 ;
  • 以自己实例为返回值的公有静态方法;

单例模式好处:

  • 单例模式只允许创建一个对象,因此节省内存;
  • 避免了频繁的对象创建,可以加快对象访问速度。

单例模式场景:

  • 需要频繁实例化然后销毁的对象;
  • 创建对象时耗时过多或者耗资源过多,但又经常用到的对象;
  • 有状态的工具类对象;

单例模式的实现方式分类:

饿汉式单例:

//线程安全的
public class Singleton {
    //单例模式,构造器要私有化。
    private Singleton() {}

    //私有的静态的常量--私有静态引用指向自己实例。
    //【因为是static修饰的属性,所以是在类加载的时候就被创建,
    //后期不会再改变,所以线程是安全的!】
    private static final Singleton single = new Singleton();

    //以自己实例为返回值的公有静态方法。
    public static Singleton getInstance() {
        return single;
    }
}

懒汉式单例:

//有线程安全的问题,不推荐使用
//就算是使用了[加锁+双重判断]的解救办法,性能还是被损耗了。
public class SingletonTest {
    public static SingletonTest singleton = null;
    public static SingletonTest getInstance(){
        if(singleton == null){
            singleton = new SingletonTest();
        }
        return singleton;
    }
    //单例模式,构造器要私有化
    private SingletonTest {
    }
}

DCL(Double Check双重判断 + Lock加锁)懒汉式单例但线程安全,推荐使用:

//线程安全的
public class Singleton {
    private volatile static Singleton singleton;

    public static Singleton getSingleton(){
        //双重判断之一
        if(singleton==null){
            //加锁
            synchronized (Singleton.class) {
                //双重判断之二
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
    
    //构造函数私有化
    private Singleton(){
    }
}

静态内部类(懒汉式单例但线程安全):

//线程安全的
//外部类加载时并不需要立即加载内部类,内部类不被加载则不去初始化INSTANCE,故而不占内存。
//第一次调用getInstance()方法会导致虚拟机加载SingleTonHoler类且初始化INSTANCE。
//在创建时是否有并发问题? => 没有没有,类加载时jvm会保证线程安全性!
public class Singleton {
    //静态内部类
    private static class SingleTonHoler{
        private static Singleton INSTANCE = new Singleton();
    }
 
    public static Singleton getInstance(){
        return SingleTonHoler.INSTANCE;
    }

    //单例模式,构造器要私有化
    private Singleton {
    }
}

枚举类(饿汉式):

//枚举单例属于懒汉式还是饿汉式:饿汉式,内部枚举类相当于静态成员变量,
//类加载时就会创建,因此也是线程安全的。
public class SingletonObject7 {
 
    private SingletonObject7(){
    }
 
    /**
     * 枚举类型是线程安全的,并且只会装载一次
     */
    private enum Singleton{
        INSTANCE;
 
        private final SingletonObject7 instance;
 
        Singleton(){
            instance = new SingletonObject7();
        }
 
        private SingletonObject7 getInstance(){
            return instance;
        }
    }
 
    public static SingletonObject7 getInstance(){
        //内部枚举类相当于静态成员变量
        return Singleton.INSTANCE.getInstance();
    }
}

破坏单例模式的场景及解决办法:

1、反射是通过调用构造方法生成新对象的,除枚举方式外,其他方式都会被反射破坏单例,所以如果我们想要阻止单例破坏,可以在构造方法中进行判断,若已有实例,则阻止生成新的实例,解决办法如下。

private SingletonObject(){
    if (instance != null) {
        throw new RuntimeException("实例已经存在,请通过 getInstance()方法获取");
    }
}

2、如果单例类实现了序列化接口Serializable, 就可以通过反序列化破坏单例(反序列化不会调用构造函数--反序列化会用到反射但不是通过反射调用构造函数而是ObjectInputStream类的私有的readObject()方法--使用ObjectInputStream.readObject()读取进来之后,如果是多次读取,就会创建多个实例),所以我们可以不实现序列化接口,如果非得实现序列化接口,可以在单例类中定义反序列化方法readResolve(),在反序列化时直接返回单例对象。

import java.io.Serializable;

// Singleton Box:
class Box implements Serializable {
  private static Box instance = new Box("TEST");
  public static Box getInstance() {
    return Box.instance;
  }
  private Box(String name) {
    this.name = name;
  }
  private String name;
  @Override
  public String toString() {
    return "Box " + name;
  }
  
  //【readResolve()方法】,如果不写此方法反序列化时会破坏单例
  private Object readResolve() {
    return Box.getInstance();
  }
}
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class Foo {
  public static void main(String[] args) {
    Box box = Box.getInstance();
    System.out.println(box.toString());
    try {
      ObjectOutputStream o = new ObjectOutputStream(
              new FileOutputStream("e:/box.out"));
      o.writeObject(box);
      o.close();
    } catch(Exception e) {
      e.printStackTrace();
    }

    Box box1 = null, box2 = null;

    try {
      ObjectInputStream in =new ObjectInputStream(
              new FileInputStream("e:/box.out"));
      box1 = (Box)in.readObject();
      in.close();
    } catch(Exception e) {
      e.printStackTrace();
    }

    try {
      ObjectInputStream in =new ObjectInputStream(
              new FileInputStream("e:/box.out"));
      box2 = (Box)in.readObject();
      in.close();
    } catch(Exception e) {
      e.printStackTrace();
    }

    System.out.println("box1.equals(box2) : " + box1.equals(box2));
    System.out.println(box1);
    System.out.println(box2);
  }
}

模板模式--行为型

抽象父类定义一个算法,子类在不改变该算法结构的情况下重定义该算法的某些步骤

算法的整体步骤很固定,但其中个别部分易变时,这时候可以使用模板方法模式,将相同部分的代码放在抽象的父类中,而将不同的代码放入不同的子类中,提高了代码复用性。

自定义lamabda表达式也符合此设计模式。

案例:炒菜的步骤是固定的,分为倒油、热油、倒蔬菜、倒调料品、翻炒等步骤。

模板方法模式

/**
 * @ClassName: AbstractClass
 * @Description: 抽象类(定义模板方法和基本方法)
 * @Author: Sevenyear
 */
public abstract class AbstractClass {

    //模板方法定义
    //【为防止恶意操作,模板方法一般都加上 final 关键词】
    public final void cookProcess() {
        pourOil();
        heatOil();
        pourVegetable();
        pourSauce();
        fry();
    }

    public void pourOil() {
        System.out.println("倒油");
    }

    //第二步:热油是一样的,所以直接实现
    public void heatOil() {
        System.out.println("热油");
    }

    //第三步:倒蔬菜是不一样的(一个下包菜,一个是下菜心)
    public abstract void pourVegetable();

    //第四步:倒调味料是不一样
    public abstract void pourSauce();


    //第五步:翻炒是一样的,所以直接实现
    public void fry(){
        System.out.println("炒啊炒啊炒到熟啊");
    }
}
/**
 * @ClassName: ConcreteClass_BaoCai
 * @Description: 炒包菜类
 * @Author: Sevenyear
 */
public class ConcreteClass_BaoCai extends AbstractClass {
    @Override
    public void pourVegetable() {
        System.out.println("下锅的蔬菜是包菜");
    }

    @Override
    public void pourSauce() {
        System.out.println("下锅的酱料是辣椒");
    }
}
/**
 * @ClassName: ConcreteClass_BaoCai
 * @Description: 炒菜心类
 * @Author: Sevenyear
 */
public class ConcreteClass_CaiXin extends AbstractClass {
    @Override
    public void pourVegetable() {
        System.out.println("下锅的蔬菜是菜心");
    }

    @Override
    public void pourSauce() {
        System.out.println("下锅的酱料是蒜蓉");
    }
}
public class Client {
    public static void main(String[] args) {
        //炒包菜
        //创建对象
        ConcreteClass_BaoCai baoCai = new ConcreteClass_BaoCai();
        //调用炒菜的功能
        baoCai.cookProcess();
    }
}

jdk案例:InputStream抽象父类中已经定义好了读取字节数组数据的方法是每次读取一个字节,并将其存储到数组的第一个空闲索引位置,循环读取len个字节数据。具体如何读取一个字节数据?是由子类实现的

public abstract class InputStream implements Closeable {
    //抽象方法,要求子类必须重写
    public abstract int read() throws IOException;

    public int read(byte b[]) throws IOException {
        return read(b, 0, b.length);
    }

    public int read(byte b[], int off, int len) throws IOException {
        if (b == null) {
            throw new NullPointerException();
        } else if (off < 0 || len < 0 || len > b.length - off) {
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return 0;
        }

        int c = read(); //调用了无参的read方法,该方法是每次读取一个字节数据
        if (c == -1) {
            return -1;
        }
        b[off] = (byte)c;

        int i = 1;
        try {
            for (; i < len ; i++) {
                c = read();
                if (c == -1) {
                    break;
                }
                b[off + i] = (byte)c;
            }
        } catch (IOException ee) {
        }
        return i;
    }
}

在InputStream类中定义了多个 read() 方法,从上面代码可以看到,无参的 read() 方法是抽象方法,要求子类必须实现。而 read(byte b[]) 方法调用了 read(byte b[], int off, int len) 方法,所以在此处重点看的方法是带三个参数的方法。 在该方法中可以看到调用了无参的抽象的 read() 方法。


享元模式--结构型

java常量池(字符串常量池、Integer常量池)的实现其实是基于享元模式(通过共享技术来有效的支持大量细粒度的对象)的思想,可以节省创建的时间,并且节省空间。

字符串常量池在java1.8中是在Java堆中的。在编译期就存在的字符串将会直接存入这个池中,在不同代码地方的字面量形式的相同的字符串将会直接引用同一个字符串,为什么能这样引用是因为字符串的不可变性。


策略模式--行为型

comparable和comparator

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/428140.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

BUUCTF------[HCTF 2018]WarmUp

开局一个表情&#xff0c;源代码发现source.php <?phphighlight_file(__FILE__);class emmm{public static function checkFile(&$page){$whitelist ["source">"source.php","hint">"hint.php"];if (! isset($page) |…

web坦克大战小游戏

H5小游戏源码、JS开发网页小游戏开源源码大合集。无需运行环境&#xff0c;解压后浏览器直接打开。有需要的订阅后&#xff0c;私信本人&#xff0c;发源码&#xff0c;含60小游戏源码。如五子棋、象棋、植物大战僵尸、贪吃蛇、飞机大战、坦克大战、开心消消乐、扑鱼达人、扫雷…

基于小红书评论的文本词语频数挖掘和词云图

import pandas as pd df pd.read_csv(小红书评论.csv) # 读取小红书评论数据 text .join(df[内容].astype(str)).strip() # 将内容列所有数据合成字符串 print(text) 使用jieba库&#xff0c;对文本数据进行分词&#xff0c;并统计出现频数 import jieba from collectio…

JMeter Body Data模拟10000个字符串

方法 **这个表达式使用了JMeter中的Groovy函数&#xff0c;目的是生成一个包含10000个字符 "s" 的字符串。在Groovy语言中&#xff0c;使用 "s" * 10000 可以生成包含10000个 "s" 的字符串。${__groovy("s" * 10000,)} 这个表达式在J…

财报解读:基本盘稳定后,联想如何进一步抢占AI时代?

从2021年下半年开始&#xff0c;受诸多因素影响&#xff0c;消费电子行业始终处在承压状态&#xff0c;“不景气”这一关键词屡次被市场提及。 但寒气没有持续&#xff0c;可以看到&#xff0c;消费电子行业正在逐渐回暖。国金证券在今年1月的研报中就指出&#xff0c;从多方面…

数字人解决方案——阿里EMO音频驱动肖像生成能说话能唱歌的逼真视频

前言 数字可以分为3D数字人和2D数字人。3D数字人以虚幻引擎的MetaHuman为代表&#xff0c;而2D数字人则现有的图像或者视频做为输入&#xff0c;然后生成对口型的数字人&#xff0c;比如有SadTalker和Wav2Lip。 SadTalker&#xff1a;SadTalker是一种2D数字人算法&#xff0c;…

什么是网络安全、信息安全、计算机安全,有何区别?

这三个概念都存在&#xff0c;一般人可能会混为一谈。 究竟它们之间是什么关系&#xff1f;并列&#xff1f;交叉&#xff1f; 可能从广义上来说它们都可以用来表示安全security这样一个笼统的概念。 但如果从狭义上理解&#xff0c;它们应该是有区别的&#xff0c;区别在哪呢&…

基于XTuner微调书生·浦语大模型

1 概述 XTuner 是一个傻瓜式、轻量级的大语言模型微调工具箱&#xff0c;由MMRazor和MMDeploy联合开发。其以配置文件的形式封装了大部分微调场景&#xff0c;0基础的非专业人员也能一键开始微调&#xff1b;对于 7B 参数量的LLM&#xff0c;微调所需的最小显存仅为 8GB。 常…

day11_oop_fianl_satic_多态

今日内容 零、 复习昨日 一、final 二、static 三、多态 四、向上转型&向下转型 五、多态应用 零、 复习昨日 0 类封装步骤 属性私有private提供setget方法 1 继承关键词,继承的好处 extends减少代码重复为多态做准备 2 子类可以使用父类什么 非私有的属性和方法 3 方法重写…

网络机顶盒哪个好?数码小编分享网络机顶盒排名

每次在挑选网络机顶盒的时候&#xff0c;很多朋友会咨询我的意见&#xff0c;最近每天都会收到相关的咨询&#xff0c;不知道网络机顶哪个好&#xff0c;我这次要分享的就是业内公认网络机顶盒排名&#xff0c;入围的几个品牌都是非常出色的&#xff0c;想买网络机顶盒的可以从…

亚信安慧AntDB:数智化转型的可持续动能

AntDB致力于为企业提供可持续发展的数据支持&#xff0c;其使命在于助力企业更好地适应不断变化的数智化时代。作为一款性能出色、可靠稳定的分布式数据库系统&#xff0c;AntDB为企业打造了一个高效、安全、灵活的数据管理平台&#xff0c;不仅拥有强大的数据处理和分析能力&a…

谁才是“内卷”之王?众多洗地机品牌哪家清洁力最强?清洁最干净?

在如今快节奏的生活中&#xff0c;家庭清洁工作愈发显得繁琐而耗时。添可洗地机凭借其高效的一体化清洁功能和智能化操作&#xff0c;为现代家庭生活带来了极大的便利。面对众多款品牌洗地机型号&#xff0c;消费者不禁会问&#xff1a;哪家洗地机清洁力最强&#xff1f;在性能…

IO(Linux)

文件系统 前言1. 回顾关于C文件部分函数2. 一些文件知识的共识3. 相对路径4. fwrite中的\0 一、文件描述符fd1. 概念2. 系统调用① open 和 close② write③ read 和 lseek 3. 缺省打开的fd 二、重定向1. 原理2. 系统调用dup23. stdout和stderr的区别4. 进程替换和原来进程文件…

百度AI,能否“投”出未来?

图片&#xff5c;freeflo.ai ©自象限原创 作者丨程心、罗辑 2月28日&#xff0c;百度发布了2023年四季度财报及全年未经审计的财务报告&#xff0c;AI大模型带来的收入和利润成为最大的亮点。 财报显示&#xff0c;2023年百度集团总营收达1345.98亿元&#xff0c;同比增…

java数据结构与算法刷题-----LeetCode337. 打家劫舍 III

java数据结构与算法刷题目录&#xff08;剑指Offer、LeetCode、ACM&#xff09;-----主目录-----持续更新(进不去说明我没写完)&#xff1a;https://blog.csdn.net/grd_java/article/details/123063846 文章目录 1. 动态规划深度优先1.1 解题思路和细节2.2 代码实现 很多人觉得…

告别信息搜寻烦恼:用fastgpt快速部署国内大模型知识库助手

Docker Compose 快速部署 使用 Docker Compose 快速部署 FastGPT 推荐配置 环境最低配置&#xff08;单节点&#xff09;推荐配置测试2c2g2c4g100w 组向量4c8g 50GB4c16g 50GB500w 组向量8c32g16c64g 200GB 部署架构图 1. 准备好代理环境&#xff08;国外服务器可忽略&…

web游戏-飞机大战

H5小游戏源码、JS开发网页小游戏开源源码大合集。无需运行环境&#xff0c;解压后浏览器直接打开。有需要的订阅后&#xff0c;私信本人&#xff0c;发源码&#xff0c;含60小游戏源码。如五子棋、象棋、植物大战僵尸、贪吃蛇、飞机大战、坦克大战、开心消消乐、扑鱼达人、扫雷…

STM32自学☞I2C

这里只是大体介绍&#xff0c;具体的可参考STM32数据手册

Python算法100例-3.2 水仙花数

完整源代码项目地址&#xff0c;关注博主私信源代码后可获取 1.问题描述2.问题分析3.算法设计4.确定程序框架5.完整的程序6.问题拓展7.巧用字符串技巧 1&#xff0e;问题描述 输出所有的“水仙花数”。所谓的“水仙花数”是指一个三位数&#xff0c;其各位数字的立方和等于该…

[C语言]——C语言常见概念(3)

目录 一.字符和ASCII编码 二.字符串和\0 三.转义字符 四.语句和语句分类 1.空语句 2.表达式语句 3.函数调用语句 4.复合语句 5.控制语句 五.注释 1.注释的2种形式 1.1 /**/ 的形式 1.2 // 的形式 2.注释会被替换 一.字符和ASCII编码 在键盘上可以敲出各种字符&am…