《Java设计模式》

1、装饰器模式:

可以在不改变原有对象的情况下,通过组合替代继承来扩展原始类的功能,应用在一些继承关系比较复杂的场景,比如IO场景。

例如字节缓冲流对字节流进行装饰,提高了它的性能。

2、适配器模式

主要用于接口互不兼容的类的协调工作,其中被适配的对象或者类称为 适配者(Adaptee) ,作用于适配者的对象或者类称为适配器(Adapter) 。适配器分为类适配器和对象适配器。类适配器使用继承关系来实现,对象适配器使用组合关系来实现。

它也广泛应用于IO中,IO 流中的字符流和字节流的接口不同,我们可以通过适配器模式来协调他们之间的工作。将字节流对象适配成一个字符流对象,这样我们可以直接通过字节流对象来读取或者写入字符数据。

比如InputStream 和 OutputStream 的子类是被适者, InputStreamReader 和 OutputStreamWriter是适配器。InputStreamReader 使用 StreamDecoder (流解码器)对字节进行解码,实现字节流到字符流的转换, OutputStreamWriter 使用StreamEncoder(流编码器)对字符进行编码,实现字符流到字节流的转换。

3、适配器模式和装饰器模式的区别

装饰器模式 更侧重于动态地增强原始类的功能,装饰器类需要跟原始类继承相同的抽象类或者实现相同的接口。并且,装饰器模式支持对原始类嵌套使用多个装饰器。

适配器模式 更侧重于让接口不兼容而不能交互的类可以一起工作,当我们调用适配器对应的方法时,适配器内部会调用适配者类或者和适配类相关的类的方法,这个过程透明的。适配器和适配者两者不需要继承相同的抽象类或者实现相同的接口。

4、工厂模式

工厂模式用工厂类创建对象,不需要通过new的方式创建,从而将创建对象的具体过程屏蔽隔离起来,有着更高的灵活性,比如spring 框架的beanFactory,通过beanFactory来实例化我们的bean对象。

工厂模式可以分为三类:

  • 简单工厂模式(Simple Factory)
  • 工厂方法模式(Factory Method)
  • 抽象工厂模式(Abstract Factory)

4.1、简单工厂模式

简单工厂模式通过工厂类来创建对象,将对象的创建和本身的业务逻辑分离,降低系统的耦合度,当以后需要改变时,只需要修改工厂类即可。

(在没有工厂的时代,如果客户需要一款宝马车,那么就需要客户去创建一款宝马车,然后拿来用。后来出现了工厂,用户不再需要去创建宝马车,由工厂进行创建,想要什么车,直接通过工厂创建就可以了。比如想要320i系列车,工厂就创建这个系列的车。)

  • 工厂类角色: 该模式的核心,用来创建产品,含有一定的商业逻辑和判断逻辑
  • 抽象产品角色:它一般是具体产品继承的父类或者实现的接口。   
  • 具体产品角色:工厂类所创建的对象就是此角色的实例。在java中由一个具体类实现。

产品类:

abstract class BMW {
	public BMW(){}
}
 
public class BMW320 extends BMW {
	public BMW320() {
		System.out.println("制造-->BMW320");
	}
}
public class BMW523 extends BMW{
	public BMW523(){
		System.out.println("制造-->BMW523");
	}
}

工厂类:

public class Factory {
	public BMW createBMW(int type) {
		switch (type) {
		
		case 320:
			return new BMW320();
 
		case 523:
			return new BMW523();
 
		default:
			break;
		}
		return null;
	}
}

用户类:

public class Customer {
	public static void main(String[] args) {
		Factory factory = new Factory();
		BMW bmw320 = factory.createBMW(320);
		BMW bmw523 = factory.createBMW(523);
	}
}

简单工厂模式的优缺点

优点:简单工厂模式提供专门的工厂类用于创建对象,客户端不需知道所创建的具体产品类的类名以及创建过程,只需知道具体产品类所对应的参数即可,将对象的创建和本身的业务逻辑分离,在一定程度上提高了系统的灵活性。

缺点: 不符合“开闭原则”,每次添加新产品就需要修改工厂类。

 为了解决简单工厂模式的问题,出现了工厂方法模式。

补充知识:静态工厂

静态工厂模式:在开发中也有一部分人将工厂类中的创建对象的功能定义为静态的

4.2 工厂方法模式:

工厂方法模式将工厂抽象化,定义抽象工厂,并提供具体实现。每增加新产品,只需增加该产品以及对应的具体实现工厂类,符合“开闭原则”,扩展时不必去修改原来的代码。当我们选择哪个具体工厂时,就已经决定了实际创建的产品是哪个了。
 

  • 抽象工厂 AbstractFactory: 工厂方法模式的核心,是具体工厂角色必须实现的接口或者必须继承的父类,在 Java 中它由抽象类或者接口来实现。 
  • 具体工厂 Factory:被应用程序调用以创建具体产品的对象,含有和具体业务逻辑有关的代码
  • 抽象产品 AbstractProduct:是具体产品继承的父类或实现的接口,在 Java 中一般有抽象类或者接口来实现。 
  • 具体产品 Product:具体工厂角色所创建的对象就是此角色的实例。
     

产品类:

abstract class BMW {
	public BMW(){}
}
public class BMW320 extends BMW {
	public BMW320() {
		System.out.println("制造-->BMW320");
	}
}
public class BMW523 extends BMW{
	public BMW523(){
		System.out.println("制造-->BMW523");
	}
}

工厂接口:

interface FactoryBMW {
	BMW createBMW();
}
 

工厂实现类:

public class FactoryBMW320 implements FactoryBMW{
 
	@Override
	public BMW320 createBMW() {
		return new BMW320();
	}
 
}
public class FactoryBMW523 implements FactoryBMW {
	@Override
	public BMW523 createBMW() {
		return new BMW523();
	}
}

用户类:

public class Customer {
	public static void main(String[] args) {
		FactoryBMW320 factoryBMW320 = new FactoryBMW320();
		BMW320 bmw320 = factoryBMW320.createBMW();
 
		FactoryBMW523 factoryBMW523 = new FactoryBMW523();
		BMW523 bmw523 = factoryBMW523.createBMW();
	}
}

工厂模式优缺点:

优点:工厂方法模式将工厂抽象化,定义抽象工厂,并提供具体实现。每增加新产品,只需增加该产品以及对应的具体实现工厂类,符合“开闭原则”,扩展时不必去修改原来的代码。当我们选择哪个具体工厂时,就已经决定了实际创建的产品是哪个了。

 缺点:每增加一个产品都需要增加一个具体产品类和实现工厂类,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。

4.3抽象工厂模式:

前沿:

在工厂方法模式中,我们使用一个工厂创建一个产品,一个具体工厂对应一个具体产品,但有时候我们需要一个工厂能够提供多个产品对象,而不是单一的对象,这个时候我们就需要使用抽象工厂模式。

在介绍抽象工厂模式前,我们先厘清两个概念:

  • 产品等级结构:产品等级结构指的是产品的继承结构,例如一个空调抽象类,它有海尔空调、格力空调、美的空调等一系列的子类,那么这个空调抽象类和他的子类就构成了一个产品等级结构。
  • 产品族:产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品。比如,海尔工厂生产海尔空调、海尔冰箱,那么海尔空调则位于空调产品族中。

产品等级结构和产品族结构示意图如下:

抽象工厂模式主要用于创建相关对象的家族。当需要用到一个产品族中的物品时,通过抽象工厂模式,使客户端始终只使用同一个产品族中的对象;并且客户端不需要明确制定实现类,交给对应的工厂进行。

缺点:添加新的行为时比较麻烦,如果需要添加一个新产品族对象时,需要更改接口及其下所有子类。

  • 抽象工厂 AbstractFactory:定义了一个接口,这个接口包含了一组方法用来生产产品,所有的具体工厂都必须实现此接口。
  • 具体工厂 ConcreteFactory:用于生产不同产品族,要创建一个产品,用户只需使用其中一个工厂进行获取,完全不需要实例化任何产品对象。
  • 抽象产品 AbstractProduct:这是一个产品家族,每一个具体工厂都能够生产一整组产品。
  • 具体产品 Product:实现抽象类

通过抽象工厂模式,我们可以实现以下的效果:比如宝马320系列使用空调型号A和发动机型号A,而宝马230系列使用空调型号B和发动机型号B,在为320系列生产相关配件时,就无需制定配件的型号,它会自动根据车型生产对应的配件型号A。

        也就是说,当每个抽象产品都有多于一个的具体子类的时候(空调有型号A和B两种,发动机也有型号A和B两种),工厂角色怎么知道实例化哪一个子类呢?抽象工厂模式提供两个具体工厂角色(宝马320系列工厂和宝马230系列工厂),分别对应于这两个具体产品角色,每一个具体工厂角色只负责某一个产品角色的实例化,每一个具体工厂类只负责创建抽象产品的某一个具体子类的实例。

产品类:

//发动机以及型号  
public interface Engine {}  
 
public class EngineA implements Engine{  
    public EngineA(){  
        System.out.println("制造-->EngineA");  
    }  
}  
public class EngineB implements Engine{  
    public EngineB(){  
        System.out.println("制造-->EngineB");  
    }  
}  
 
 
//空调以及型号  
public interface Aircondition {} 
 
public class AirconditionA implements Aircondition{  
    public AirconditionA(){  
        System.out.println("制造-->AirconditionA");  
    }  
}  
public class AirconditionB implements Aircondition{  
    public AirconditionB(){  
        System.out.println("制造-->AirconditionB");  
    }  
} 

抽象工厂接口:

//创建工厂的接口  
public interface AbstractFactory {  
    //制造发动机
    public Engine createEngine();
    //制造空调 
    public Aircondition createAircondition(); 
}  

创建工厂类:


 
//为宝马320系列生产配件  
public class FactoryBMW320 implements AbstractFactory{     
    @Override  
    public Engine createEngine() {    
        return new EngineA();  
    }  
    @Override  
    public Aircondition createAircondition() {  
        return new AirconditionA();  
    }  
}  
//宝马523系列
public class FactoryBMW523 implements AbstractFactory {  
     @Override  
    public Engine createEngine() {    
        return new EngineB();  
    }  
    @Override  
    public Aircondition createAircondition() {  
        return new AirconditionB();  
    }  
} 

客户:

public class Customer {  
    public static void main(String[] args){  
        //生产宝马320系列配件
        FactoryBMW320 factoryBMW320 = new FactoryBMW320();  
        factoryBMW320.createEngine();
        factoryBMW320.createAircondition();
          
        //生产宝马523系列配件  
        FactoryBMW523 factoryBMW523 = new FactoryBMW523();  
        factoryBMW523.createEngine();
        factoryBMW523.createAircondition();
    }  
}


(参考链接:https://blog.csdn.net/a745233700/article/details/120253639)

5、观察者模式

观察者模式又称为 发布-订阅模式,定义了对象之间一对多依赖关系,当被观察者的状态发生改变时,它的所有观察者都会收到通知。这些观察者之间没有相互联系,所以能够根据需要增加和删除观察者,使得系统更易于扩展,符合开闭原则。并且彼此不需要清楚对方的细节,也可以交互。

缺点:如果存在很多个被观察者的话,那么将需要花费一定时间通知所有的观察者,如果观察者与被观察者之间存在循环依赖的话,那么可能导致系统崩溃,并且观察者仅仅只是知道观察目标发生了变化,不知道它是怎么发生变化的。

情景:在气象观测站中,它能够追踪目前的天气状况,包括温度、适度、气压。需要实现一个布告板,能够分别显示目前的状态,气象统计和简单的预报。当气象站中获取最新的测量数据时,三种布告板必须实时更新。

  • Subject:抽象主题(被观察者),每一个主题可以有多个观察者,并将所有观察者对象的引用保存在一个集合里,被观察者提供一个接口,可以增加和删除观察者角色
  • ConcreteSubject:具体主题,将有关状态存入具体观察者对象,在主题发生改变时,给所有的观察者发出通知
  • Observer:抽象观察者,为所有的具体观察者定义一个更新接口,该接口的作用是在收到主题的通知时能够及时的更新自己
  • ConcreteObserver:具体观察者,实现抽象观察者角色定义的更新接口,以便使本身的状态与主题状态相协调。如果需要,具体观察者角色可以保存一个指向具体主题角色的引用。

主题接口:

public interface Subject {
	/**
	 * 注册观察者
	 * @param observer
	 */
	public void registerObserver(Observer observer);
	
	/**
	 * 删除观察者
	 * @param observer
	 */
	public void removeOberver(Observer observer);
	
	/**
	 * 当主题状态发生改变时,这个方法需要被调用,以通知所有观察者
	 */
	public void notifyObserver();
}

观察者接口:

public interface Observer {
	public void update(float temp,float humidity,float pressure);
}

布告板显示接口

public interface DisplayElement {
	public void display();
}

WeatherData实现主题接口:

public class WeatherData implements Subject{
	private List<Observer> observers;
	private float tempterature;
	private float pressure;
	private float humidity;
	
	public WeatherData(){
		observers = new ArrayList<Observer>();
	}
	
	@Override
	public void notifyObserver() {
		for(int i = 0; i < observers.size();i++){
			Observer observer = observers.get(i);
			observer.update(tempterature, humidity, pressure);
		}
	}
 
	@Override
	public void registerObserver(Observer observer) {
		observers.add(observer);
	}
 
	@Override
	public void removeOberver(Observer observer) {
		int i = observers.indexOf(observer);
		if(i >= 0){
			observers.remove(i);
		}
	}
 
	/**
	 * 气象站得到更新的观测数据时,通知观察者
	 */
	public void measurementChanged(){
		notifyObserver();
	}
	
	public void setMeasurements(float temperature,float humidity,float pressure){
		this.tempterature = temperature;
		this.humidity = humidity;
		this.pressure = pressure;
		measurementChanged();
	}
}

布告板 

public class CurrentConditionsDisplay implements Observer,DisplayElement{
	private float temperature;
	private float humidity;
	private	Subject weatherData;
	
	public CurrentConditionsDisplay(Subject weatherData){
		this.weatherData = weatherData;
		weatherData.registerObserver(this);      //注册观察者
	}
	
	public void update(float temp, float humidity, float pressure) {
		this.temperature = temp;
		this.humidity = humidity;
		display();
	}
 
	@Override
	public void display() {
		System.out.println("Current conditions:"+temperature+"F degrees and "+humidity+"% humidity");
	}
 
}

测试程序:

public class WeatherStation {
 
	public static void main(String[] args) {
		WeatherData weatherData = new WeatherData();
		
		CurrentConditionsDisplay conditionsDisplay = new CurrentConditionsDisplay(weatherData);
	
		weatherData.setMeasurements(80, 65, 30.4f);
		weatherData.setMeasurements(82, 70, 29.2f);
		weatherData.setMeasurements(78, 78, 40.4f);
	}
}

观察者模式的两种模式:

(1)拉取模式:目标角色在发生变化后,仅仅告诉观察者角色“我变化了”,观察者角色如果想要知道具体的变化细节,则就要自己从目标角色的接口中得到,这种模式称为拉取模式,就是说变化的信息是观察者角色主动从目标角中“拉”出来的。

(2)推送模式:目标角色发生变化时,通知观察者的同时,通过参数将变化的细节传递到观察者角色中去。

        这两种模式的使用取决于系统设计,如果目标角色比较复杂,并且观察者角色进行更新时必须得到一些具体变化的信息,则“推模式”比较合适,如果目标角色比较简单,则“拉模式”就很合适啦。

代理模式:

我们使用代理对象来代替对真实对象(real object)的访问,这样就可以提供额外的功能操作,扩展目标对象的功能,某个方法执行前后增加一些自定义的操作。

静态代理:

我们对目标对象的每个方法的增强都是手动完成的, 静态代理在编译时就将接口、实现类、代理类这些都变成了一个个实际的 class 文件。如果需要新增功能的话,就必须重新修改,非常麻烦。

动态代理:

代理类并不是在Java代码中定义,而是在运行时根据我们在动态生成的。这样就可以很方便的对代理类的函数进行统一处理,而不用修改每个代理类中的方法。

动态代理的实现方式有很多种,比如 JDK 动态代理CGLIB 动态代理

JDK 动态代理

在 Java 动态代理机制中 InvocationHandler 接口和 Proxy 类是核心。

3.1.2. JDK 动态代理类使用步骤

  • 定义一个接口及其实现类;
  • 自定义类实现 InvocationHandler 并重写invoke方法,在 invoke 方法中我们会调用原生方法(被代理类的方法)并自定义一些处理逻辑;
  • 通过 Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 方法创建代理对象

invoke() 方法有下面三个参数:

  1. proxy :动态生成的代理类
  2. method : 与代理类对象调用的方法相对应
  3. args : 当前 method 方法的参数
public interface InvocationHandler {

    /**
     * 当你使用代理对象调用方法的时候实际会调用到这个方法
     */
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}
public class ProxyTest {
    public static void main(String[] args) {
        
        //创建一个实例对象,这个对象是被代理的对象
        Person zhangsan = new Student("张三");
        
        //创建一个与代理对象相关联的InvocationHandler
        InvocationHandler stuHandler = new StuInvocationHandler<Person>(zhangsan);
        
        //创建一个代理对象stuProxy来代理zhangsan,代理对象的每个执行方法都会替换执行Invocation中的invoke方法
        Person stuProxy = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, stuHandler);
 
       //代理执行上交班费的方法
        stuProxy.giveMoney();
    }
}

Proxy.newProxyInstance这个方法一共有 3 个参数:

  1. loader :类加载器,用于加载代理对象。
  2. interfaces : 被代理类实现的一些接口;
  3. h : 实现了 InvocationHandler 接口的对象;
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        ......
    }

这样,所有执行代理对象的方法都会被替换成执行invoke方法,也就是说,最后执行的是StuInvocationHandler中的invoke方法。

动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。是因为所有被代理执行的方法,都是通过在InvocationHandler中的invoke方法调用的,所以我们只要在invoke方法中统一处理,就可以对所有被代理的方法进行相同的操作了
 

CGLIB 动态代理

JDK 动态代理只能代理实现了接口的类,为了解决这个问题,我们可以用 CGLIB 动态代理机制来避免。

在 CGLIB 动态代理机制中 MethodInterceptor 接口和 Enhancer 类是核心。

CGLIB 动态代理类使用步骤

  • 定义一个类;
  • 自定义 MethodInterceptor 并重写 intercept 方法,intercept 用于拦截增强被代理类的方法,和 JDK 动态代理中的 invoke 方法类似;
  • 通过 Enhancer 类的 create()创建代理类;

不同于 JDK 动态代理不需要额外的依赖。CGLIBopen in new window(Code Generation Library) 实际是属于一个开源项目,如果你要使用它的话,需要手动添加相关依赖。

<dependency>
  <groupId>cglib</groupId>
  <artifactId>cglib</artifactId>
  <version>3.3.0</version>
</dependency>

需要自定义类实现 MethodInterceptor 并重写 intercept 方法,intercept 用于拦截增强被代理类的方法。

  1. obj : 被代理的对象(需要增强的对象)
  2. method : 被拦截的方法(需要增强的方法)
  3. args : 方法入参
  4. proxy : 用于调用原始方法

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * 自定义MethodInterceptor
 */
public class DebugMethodInterceptor implements MethodInterceptor {


    /**
     * @param o           被代理的对象(需要增强的对象)
     * @param method      被拦截的方法(需要增强的方法)
     * @param args        方法入参
     * @param methodProxy 用于调用原始方法
     */
    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        //调用方法之前,我们可以添加自己的操作
        System.out.println("before method " + method.getName());
        Object object = methodProxy.invokeSuper(o, args);
        //调用方法之后,我们同样可以添加自己的操作
        System.out.println("after method " + method.getName());
        return object;
    }

}

你可以通过 Enhancer类来动态获取被代理类,当代理类调用方法的时候,实际调用的是 MethodInterceptor 中的 intercept 方法。

import net.sf.cglib.proxy.Enhancer;

public class CglibProxyFactory {

    public static Object getProxy(Class<?> clazz) {
        // 创建动态代理增强类
        Enhancer enhancer = new Enhancer();
        // 设置类加载器
        enhancer.setClassLoader(clazz.getClassLoader());
        // 设置被代理类
        enhancer.setSuperclass(clazz);
        // 设置方法拦截器
        enhancer.setCallback(new DebugMethodInterceptor());
        // 创建代理类
        return enhancer.create();
    }
}

JDK 动态代理和 CGLIB 动态代理对比

  • JDK 动态代理只能代理实现了接口的类或者直接代理接口,而 CGLIB 可以代理未实现任何接口的类。 另外, CGLIB 动态代理是通过生成一个被代理类的子类,来拦截被代理类的方法调用,因此不能代理声明为 final 类型的类和方法。
  • 就二者的效率来说,大部分情况都是 JDK 动态代理更优秀

Spring AOP 就是基于动态代理的,如果要代理的对象,实现了某个接口,那么 Spring AOP 会使用 JDK Proxy 去创建代理对象,而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候 Spring AOP 会使用 Cglib 生成一个被代理对象的子类来作为代理。

模板方法:

它定义一个操作的模板结构,而将具体内容延迟到子类中实现。 在不改变模板结构的前提下在子类中重新定义模板中的内容

  • 提高代码复用性
    将相同部分的代码放在抽象的父类中,而将不同的代码放入不同的子类中
  • 实现了反向控制
    通过一个父类调用其子类的操作,通过对子类的具体实现扩展不同的行为,实现了反向控制 & 符合“开闭原则”

Spring 中 JdbcTemplatetransactionTemplate 等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式。一般情况下,我们都是使用继承的方式来实现模板模式,但是 Spring 并没有使用这种方式,而是使用 Callback 模式与模板方法模式配合,既达到了代码复用的效果,同时增加了灵活性。

单例模式:

是指在内存中只会创建且仅创建一次对象的设计模式。在程序中多次使用同一个对象且作用相同时,为了防止频繁地创建对象使得内存飙升,单例模式可以让程序仅在内存中创建一个对象,让所有需要调用的地方都共享这一单例对象。

  • 懒汉式:在真正需要使用对象时才去创建该单例类对象
  • 饿汉式:在类加载时已经创建好该单例对象,等待被程序使用
	public class LazySingleton{
		//单例对象声明
		private static LazySingleton lazySingleton = null;
		//私有化构造器
		private LazySingleton(){
		}
		//获取单例对象,当调用该方法时才去判断是否存在对象,不存在实例化一个对象
		public static synchronized LazySingleton getSingletonInstance(){ //需要加锁才能保证线程安全
			if(lazySingleton == null){
				return new LazySingleton();
			}
			return lazySingleton;
		}
	}
	public class HungrySingleton{
		//私有化构造器
		private HungrySingleton(){
		}
		//单例对象实例化,在类初始化的时候就实例化该对象
		private static HungrySingleton hungrySingleton = getSingletonInstance();
		
		//获取单例对象
		public static HungrySingleton getSingletonInstance(){
			if(hungrySingleton == null){
				return new HungrySingleton();
			}
			return hungrySingleton;
		}
	}

对比懒汉式和饿汉式,懒汉式需要加锁才能保证线程安全,当多个线程调用此方法时会造成低效率;饿汉式类初始化的时候便进行对象创建,当此对象还没需要被用到时,则会造成资源浪费。因此引申出来第三种单例模式的实现,即双重校验锁。​​​​​​​

双重检查锁定(Double-Checked Locking):

在懒汉式的基础上添加双重检查,确保只在第一次实例化时加锁,提高了并发性能。

public class DoubleCheckedLockingSingleton {
    private static volatile DoubleCheckedLockingSingleton instance;

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

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

应用场景:

Spring 默认情况下将所有的 Bean 配置为单例(Singleton)作用域,这意味着在整个应用程序中只会创建一个 Bean 实例。这种单例模式的应用有助于减少资源消耗、提高性能,并确保对 Bean 的共享和一致性。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值