秋招八股--设计模式

Java后端各科最全八股自用整理,获取方式见

问你知道哪些设计模式?尝试手写一个?

发散问题:如果一个接口很慢,你需要排查问题,你觉得可能会有哪些方面的原因?

还知道哪些设计模式?

常用的设计模式

java的设计模式分为三大类: — 23种经典的设计模式 GOF

  1. 创建型模式,共5种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
  2. 结构型模式,共7种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
  3. 行为型模式,共11种:策略模式、模板方法模式、观察者模式、迭代器模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
  • 创建型模式与对象的创建有关;
  • 结构型模式处理类或对象的组合;
  • 行为型模式对类或对象怎样交互和怎样分配职责进行描述。

设计模式遵循6大原则

http://www.uml.org.cn/sjms/201211023.asp#2

1、单一职责原则

  • 即一个类只负责一项职责。

2、开闭原则(Open Close Principle)

  • 对扩展开放,对修改关闭。

3、里氏代换原则(Liskov Substitution Principle)

  • 只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。
  • 类B继承类A时,除添加新的方法完成新增功能P2外,尽量不要重写父类A的方法,也尽量不要重载父类A的方法。

4、依赖倒转原则(Dependence Inversion Principle)

  • 这个是开闭原则的基础,对接口编程,依赖于抽象而不依赖于具体。

5、接口隔离原则(Interface Segregation Principle)

  • 客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。
  • 建立单一接口,不要建立庞大臃肿的接口,尽量细化接口,接口中的方法尽量少。

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

  • 一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。
  • 尽量降低类与类之间的耦合。

代理模式

观察者模式

又名发布-订阅(Publish/Subscribe)模式。GOF给观察者模式如下定义:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

观察者模式很好理解,类似于邮件订阅和 RSS订阅,当我们浏览一些博客或 wiki 时,经常会看到 RSS 图标,就这的意思是,当你订阅了该文章,如果后续有更新,会及时通知你。其实,简单来讲就一句话:当一个对象变化时,其它依赖该对象的对象都会收到通知,并且随着变化!对象之间是一种一对多的关系。

以下是观察者模式的基本实现步骤:

[1] 定义主题接口(Subject Interface):主题是被观察的对象,需要定义一个接口,其中包含注册、注销和通知观察者的方法。通常,这个接口包括registerObserver、removeObserver和notifyObservers等方法。

[2] 定义观察者接口(Observer Interface):观察者是接收主题通知并进行相应操作的对象,需要定义一个接口,其中包含更新方法。

[3] 实现具体主题类(Concrete Subject):实现主题接口的具体类,负责维护观察者列表并在状态变化时通知观察者。

[4] 实现具体观察者类(Concrete Observer):实现观察者接口的具体类,负责在接收到通知时进行相应的更新操作。

适配器模式

想使用一个已经存在的类,而它的接口不符合要求描述的是适配器模式; 适配器(Adapter)模式将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

适配器模式包括三个角色:目标抽象类(Target)、适配器类(Adapter)和被适配者类(Adaptee)。

  • 目标抽象类(Target):定义了客户端使用的与特定领域相关的接口,也就是客户端需要的方法。
  • 适配器类(Adapter):通过继承或者组合方式,将被适配者类的接口与目标抽象类的接口转换起来,使得客户端可以按照目标抽象类的接口进行操作。
  • 被适配者类(Adaptee):已经存在的、功能稳定的类,在这里指的是需要适配的类。

https://zhuanlan.zhihu.com/p/371225977

适配器模式怎么实现的
适配器模式主要分为类适配器模式和对象适配器模式两种实现方式:

  • 类适配器模式:通过继承来实现适配器功能;
  • 对象适配器模式:通过组合来实现适配器功能。

https://open.alipay.com/portal/forum/post/125101070

策略模式

策略模式(Strategy)属于对象行为型设计模式,主要是定义一系列的算法,把这些算法一个个封装成拥有共同接口的单独的类,并且使它们之间可以互换。策略模式使这些算法在客户端调用它们的时候能够互不影响地变化。

策略模式的决定权在用户,系统本身提供不同算法的实现,新增或者删除算法,对各种算法做封装。外部用户只需要决定用哪个算法即可。

工厂模式

https://www.cnblogs.com/yssjun/p/11102162.html
工厂模式主要是为创建对象提供过渡接口,实现了创建者与调用者的分离,以便将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的。

工厂模式在《Java与模式》中分为三类:

1)简单工厂模式((静态工厂模式))

把实例化的操作单独放到简单工厂类类中,让简单工厂类决定应该实例化哪个具体子类,这样做能把客户类和具体子类的实现解耦,客户类不再需要知道有哪些子类以及应当实例化哪个子类。

优点:
封装了创建对象的过程,可以通过参数直接获取对象。把对象的创建和业务逻辑层分开,避免了修改客户代码,实现新产品直接修改工厂类,而不需要在原代码中修改,这样就降低了客户代码修改的可能性,更加容易扩展。

缺点:
增加新产品时还是需要修改工厂类的代码,违背了“开闭原则”。 【对扩展开放;对修改关闭 】

2)工厂方法模式(Factory Method)

有四个角色,抽象工厂,具体工厂,抽象产品,具体产品。

定义一个用于创建对象的接口(抽象工厂),让子类决定实例化哪个产品类对象。工厂方法使一个产品类的实例化延迟到其工厂的子类。

优点:
• 用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程;
• 在系统增加新的产品时只需要添加具体产品类和对应的具体工厂类,无须对原工厂(抽象工厂)进行任何修改,满足开闭原则;

缺点:
每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加了系统的复杂度。

3)抽象工厂模式(Abstract Factory)

是工厂方法模式的升级版本,工厂方法模式只生产一个等级的产品,而抽象工厂模式可生产多个等级的产品。
在这里插入图片描述

优点:
当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。 【满足开闭原则!】

缺点:
当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。 【抽象工厂+具体工厂!】

使用场景 【重要】

  • 当需要创建的对象是一系列相互关联或相互依赖的产品族时,如电器工厂中的电视机、洗衣机、空调等。
  • 系统中有多个产品族,但每次只使用其中的某一族产品。如有人只喜欢穿某一个品牌的衣服和鞋。
  • 系统中提供了产品的类库,且所有产品的接口相同,客户端不依赖产品实例的创建细节和内部结构。
  • 如:输入法换皮肤,一整套一起换。生成不同操作系统的程序。
工厂方法模式抽象工厂模式
针对的是单个产品等级结构针对的是面向多个产品等级结构
一个抽象产品类多个抽象产品类
可以派生出多个具体产品类每个抽象产品类可以派生出多个具体产品类
一个抽象工厂类,可以派生出多个具体工厂类一个抽象工厂类,可以派生出多个具体工厂类
每个具体工厂类只能创建一个具体产品类的实例每个具体工厂类可以创建多个具体产品类的实例

简单介绍集中设计模式

单例模式,写了双重校验锁和静态内部类的实现

单例模式

手写单例模式(双重判空+同步)

简单点说,就是一个应用程序中,某个类的实例对象只有一个,没有办法去new,因为构造器是被private修饰的,一般通过getInstance()的方法来获取它们的实例。

1、饿汉式

饿汉式1:

class Singleton {   
	
	//1.私化类的构造器
	private Singleton (){
		
	}
	
	//2.内部创建类的对象
	//4.要求此对象也必须声明为静态的
	private static Singleton instance = new Singleton ();
	
	//3.提供公共的静态的方法,返回类的对象
	public static Singleton getInstance(){
		return instance;
	}
}

饿汉式2:使用静态代码块


class Singleton {
	
	//1.私化类的构造器
	private Singleton (){
		
	}
	
	//2.声明当前类对象,没初始化
	//4.此对象也必须声明为static的
	private static Singleton instance = null;

	static{
		instance = new Singleton ();
	}
	
	//3.声明public、static的返回当前类对象的方法
	public static Singleton getInstance(){
		return instance;
	}
}

2、懒汉式:

class Singleton {
	//1.私化类的构造器
	private Singleton (){
		
	}
	
	//2.声明当前类对象,没初始化
	//4.此对象也必须声明为static的
	private static Singleton instance = null;
	
	//3.声明public、static的返回当前类对象的方法
	public static Singleton getInstance(){
		
		if(instance == null){
			
			instance = new Singleton ();
	
		}
		return instance;
	}
}

两种方式的对比:

饿汉式:

  • 坏处:对象加载时间过长。
  • 好处:饿汉式是线程安全的

懒汉式:好处:延迟对象的创建。

  • 目前的写法坏处:线程不安全。—>到多线程内容时,再修改

使用同步机制将单例模式中的懒汉式改写为线程安全的。

class Singleton {

    private Singleton (){   }

    private static Singleton instance = null;

    public static Singleton getInstance(){
        //方式一:效率稍差
//        synchronized (Singleton .class) {
//            if(instance == null){
//
//                instance = new Singleton ();
//            }
//            return instance;
//        }

        //方式二:效率更高
        if(instance == null){

            synchronized (Singleton .class) {
                if(instance == null){
                    instance = new Singleton ();
                }
            }
        						}
        return instance;
    }
}

面试题:写一个线程安全的单例模式?

  • 饿汉式。
  • 懒汉式:上面提供的。

3、双重校验锁

public class Singleton {

    // 私有构造方法,防止外部实例化
    private Singleton() {   }

    // 使用volatile关键字确保多线程环境下的可见性
    private static volatile Singleton instance;

    // 双重锁检测,确保只有一个实例被创建
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
          // 再次检查,防止多个线程同时通过第一层检查,导致创建多个实例
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

volatile关键字: 在Java中,volatile关键字用于确保多线程环境下的可见性。当一个变量被声明为volatile时,它保证了所有线程都能够看到该变量的最新值。

双重锁检测: 在getInstance()方法中,首先检查instance是否为null,如果为null,则进入同步块。在同步块内部,再次检查 instance 是否为null,这是因为在多线程环境下,可能有多个线程同时通过了第一个检查,然后其中一个线程获取了锁并创建了实例,而其他线程在等待锁释放。第二次检查确保只有一个线程创建实例。

4、静态内部类

public class Singleton {  

   private static class SingletonHolder {  
      private static final Singleton INSTANCE = new Singleton();  
   }  
   
   private Singleton (){}  
   
   public static final Singleton getInstance() {  
      return SingletonHolder.INSTANCE;  
   }  
}

5、枚举

public enum Singleton {  
   INSTANCE;  
   public void whateverMethod() {  
   }  
}

这种方式是Effective Java作者Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象,可谓是很坚强的壁垒啊,不过,个人认为由于1.5中才加入enum特性,用这种方式写不免让人感觉生疏。

手撕单例模式必须掌握!

写一下双重检测单例模式,解释一下每一行,volatile的作用,不加会有什么问题,去掉第一层循环会有什么问题,去掉第二层循环会有什么问题。

为什么要双重判空?

为什么要加volatile?volatile原理?

这样的单例模式是否会被破坏?

这个单例模式,我使用反射可不可以打破

https://www.cnblogs.com/wyb628/p/6371827.html

反射如何破坏的?(构造器)怎么防止反射破坏?

除了反射还有其他方法能够破坏单例模式吗?(序列化、克隆)


更多后端全部八股点击👉👉【闲鱼】https://m.tb.cn/h.5yHpgkY?tk=O8bhWpn1NBD CZ8908 「我在闲鱼发布了【京985计算机硕士自用后端八股文出售,不同于市面上的几块钱八】」
点击链接直接打开

Java后端各科最全八股自用整理,获取方式见


整理不易🚀🚀,关注和收藏后拿走📌📌欢迎留言🧐👋📣
欢迎专注我的公众号AdaCoding 和 Github:AdaCoding123
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值