设计模式

注意:括号中为八股在每次面试中出现的概率

单例设计模式(357/1759=20.3%)

单例设计模式是一种创建型设计模式,核心思想是保证一个类在整个应用中只有一个实例,并提供一个全局的访问入口。它常用于管理共享资源,比如配置、缓存或数据库连接等场景。接下来,我将详细介绍单例模式的实现过程,以常用的懒汉式加双重检查锁定为例说明其设计原理:

首先,为了避免外部直接创建对象,我们将该类的构造方法设为私有。这一步确保了实例只能在类内部被创建,从而杜绝了通过 new 关键字产生多个实例的可能性。

其次,在类内部定义一个私有的静态变量,该变量用于存储单例实例。初始状态下,它的值为 null,这意味着实例尚未被创建。

然后,我们提供一个公共的静态方法来获取该实例。该方法首先判断静态变量是否为 null,如果不为 null,则说明实例已存在,直接返回即可;如果为 null,则进入同步代码块。在同步块内,再次检查实例是否为 null,若仍为 null,则创建该实例。通过双重检查锁定的方式,我们既保证了多线程环境下的线程安全,又避免了每次访问实例时都进行同步操作带来的性能开销。

最后,当实例创建完成后,后续调用获取实例的方法时,都会直接返回该唯一的实例。这样不仅保证了全局唯一性,也提高了系统性能,因为同步操作只在第一次创建实例时执行。

如何记忆:

1.联想记忆法

场景:

单身公寓

解释 :

单例模式就像一栋单身公寓,整栋楼只能住一个人(唯一实例)。为了保证这一点:

公寓的门钥匙(构造方法)被房东(类)保管,不允许租客(外部代码)私自复制钥匙。

房东在房间里放了一张床(静态变量),用来标记是否已经有人入住。

如果有人敲门(调用获取实例的方法),房东会先看看房间里是否有人(第一次检查),如果没有,他会锁上门再确认一次(双重检查锁定),然后安排人入住。

拓展:

1.单例模式的经典实现方式对比

(1)饿汉式(静态常量)

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

特点:类加载时立即初始化,线程安全。

优点:实现简单,无并发问题。

缺点:即使未使用也会占用资源,可能造成浪费。

(2)懒汉式(线程安全)

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

特点:首次调用时初始化,通过synchronized保证线程安全。

缺点:每次调用getInstance()都会同步,性能较低。

(3)双重检查锁(Double-Check Locking)

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

关键点:通过两次判空减少同步开销,volatile防止指令重排。

适用场景:高并发环境下的延迟初始化。

(4)静态内部类

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

优势:利用类加载机制保证线程安全,同时实现延迟初始化。

推荐场景:兼顾性能与安全性的通用方案。

(5)枚举单例

public enum Singleton {
    INSTANCE;
    // 添加业务方法
}

特点:天然线程安全,支持序列化,防止反射攻击。

最佳实践:Effective Java推荐的实现方式。

2.Spring 单例 Bean 存在线程安全问题吗?

在多线程环境下,单例 Bean 是否会出现线程安全问题,主要取决于该 Bean 是否维护了可变的共享状态。换句话说,如果一个单例 Bean 中包含可变成员变量,并且多个线程会同时对这些变量进行读写操作,那么就有可能发生线程安全问题。反之,如果该 Bean 不存在可变共享数据,或者所有操作都在内部做好了线程安全的保护,就不会出现线程安全隐患。

常见的处理思路包括:

保持 Bean 的无状态化:尽量只使用不可变成员变量,或者在需要存储状态时,将这些状态放到方法内部,避免多线程同时访问同一份数据。

使用 ThreadLocal:为每个线程提供独立的变量副本,从而隔离线程之间的共享数据,避免竞争。

加锁或其他同步手段:如果必须在 Bean 中操作可变状态,可以使用 synchronized、ReentrantLock 等机制来保证并发操作的安全性。

因此,如果你的单例 Bean 仅用于提供一些纯计算或无状态的业务逻辑(例如常见的 Dao、Service 等),通常无需额外处理多线程问题;但如果其中包含可变共享资源,就需要通过合适的线程安全措施来保证应用的正确性和稳定性。

工厂设计模式(411/1759=23.4%)

工厂设计模式是一种创建型设计模式,它主要用于封装对象的创建过程,从而使客户端不需要直接实例化具体的类,而是通过工厂来获取对象。这种模式提高了代码的灵活性和扩展性。接下来我会详细讲述工厂设计模式的实现过程。

当我们需要创建一个产品对象时,

首先,我们会定义一个抽象的产品接口或者抽象类,明确规定产品的公共行为和属性。这样,无论后续添加多少具体产品,客户端都可以通过同一接口来操作它们。

其次,我们实现具体的产品类,这些类分别实现了抽象产品接口,包含各自独特的业务逻辑和功能。

接着,我们定义一个工厂接口或者抽象工厂类,声明一个创建产品对象的方法。该方法的职责是隐藏具体产品对象的实例化过程,客户端只需要调用这个方法即可获得产品实例。

然后,我们实现具体的工厂类,它们根据传入的参数或内部逻辑,决定创建哪一种具体的产品对象。这样,具体产品的创建细节完全被封装在工厂内部,客户端无需关心对象的创建过程。

最后,当客户端需要一个产品时,它只需调用工厂提供的创建方法,获得对应的产品对象,并直接使用。这种方式不仅降低了客户端与具体产品实现之间的耦合,也方便了系统的扩展和维护。

如何记忆:

1.联想记忆法

场景:

餐厅点餐

解释 :

抽象产品 :菜单上的菜品名称和描述(比如“牛排”)。

具体产品 :厨房实际制作的牛排、意大利面等具体菜品。

抽象工厂 :服务员的职责是接受订单并通知厨房制作菜品。

具体工厂 :厨师根据订单制作具体的菜品。

客户端 :顾客只需要点餐,不需要知道牛排是怎么做出来的。

拓展:

1.分类与实现方式

(1)简单工厂模式(Simple Factory)

简单工厂模式并不是一种正式的设计模式,但它是最基础的形式。它通过一个工厂类根据传入的参数决定创建哪种具体产品。

public class SimpleFactory {
    public static Product createProduct(String type) {
        if ("A".equals(type)) {
            return new ConcreteProductA();
        } else if ("B".equals(type)) {
            return new ConcreteProductB();
        }
        throw new IllegalArgumentException("Unknown type");
    }
}

// 抽象产品
public interface Product {
    void use();
}

// 具体产品A
public class ConcreteProductA implements Product {
    @Override
    public void use() {
        System.out.println("Using Product A");
    }
}

// 具体产品B
public class ConcreteProductB implements Product {
    @Override
    public void use() {
        System.out.println("Using Product B");
    }
}

特点:集中管理对象创建逻辑,易于理解和实现。

缺点:违反开闭原则,新增产品时需修改工厂类。

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

工厂方法模式通过定

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

【神品八股】1759篇面经精华 文章被收录于专栏

神哥引路,稳稳起步!!早鸟特惠,仅剩177名额!晚了就涨到29.9了! 核心亮点: 1.数据驱动,精准高频:基于1759篇面经、24139道八股题,精准提炼真实高频八股。 2.科学记忆,高效掌握:融合科学记忆法和面试表达技巧,记得住,说得出。 3.提升思维,掌握财商:不仅可学习八股,更可教你变现,3个月赚不回购买价,全额退。 适宜人群: 在校生、社招求职者及自学者。

全部评论
刚自己找完设计模式的八股就更新了
1 回复 分享
发布于 03-06 10:08 山西
好耶
1 回复 分享
发布于 03-06 16:26 江苏
生产者消费者可以当作设计模式吗,并不在23种设计模式里面
1 回复 分享
发布于 03-06 16:47 山东
完结撒花
1 回复 分享
发布于 03-07 10:12 江苏

相关推荐

JWT 的工作原理用户通过用户名和密码等凭据进行身份验证。服务器验证用户的凭据,生成 JWT,并将其返回给用户。JWT 通过 Base64Url 编码而成。用户存储 JWT,通常存储在浏览器的 localStorage 或 sessionStorage 中。用户在每次请求时将 JWT 发送到服务器,通常通过 HTTP 请求的 Authorization 头部。服务器接收到 JWT 后验证其签名与内容。如果有效,服务器执行该请求;如果无效,返回相应的错误信息。JWT 的优势无状态:JWT 以自包含的方式存储用户信息,服务器不需要存储用户会话信息,适合分布式系统。跨域支持:由于 JWT 是基于标准的字符串格式,可以轻松支持跨域请求。灵活性:可以在 Token 中存储自定义数据,不仅限于身份验证相关的信息。安全性:通过签名算法保证数据的完整性,避免被篡改。JWT 的缺点不可撤销性:JWT 一旦生成,无法简单地撤销,除非设计了 Token 刷新机制。过期管理:需要合理设置过期时间,过长会带来安全隐患,过短则可能影响用户体验。隐私问题:JWT 中的信息是Base64Url编码的,并未加密,因此敏感信息不应直接放在 Token 中。常见用例用户身份验证:用户登录成功后,会话管理通过发放 JWT 实现。API 访问控制:保护 API 端点,确保只有持有有效 Token 的用户才能访问。单点登录(SSO):多个系统之间共享 JWT,实现单点登录。
点赞 评论 收藏
分享
评论
4
4
分享

创作者周榜

更多
牛客网
牛客企业服务