[Java] Java的单例和多例模式

单例模式和多例模式都是对象模式的一种。即对某些对象的全局实例化数量。

单例模式:一个类的对象在整个系统中只有一份实例,其他地方使用时需要通过类的静态get方法调用。例如后台中需要保存全局数据的某些类,亦或是controller这样负责处理相同事务的类。

多例模式:一个类的对象在整个系统中可以有多个实例,每个实例专门为一段时间的一段任务服务。

 

对于单例模式,通常是以private static的形式将类的实例化保存在类成员中,然后通过一些方式限制实例的获取。当然这个获取方法本身也是static的。

private static MyObject myObject;

public static MyObject getInstance() { ..., return myObject; }

单例模式有几种形式:

1)饿汉式

 加载类代码时立即实例化其他类。

private static MyObject myObject = new MyObject();

2)懒汉式

get获取实例时检查是否已经存在实例。若无则初始化,否则创建新的实例。

这也可以避免在启动时创建大量单例类的实例。

if (myObject != null) {
} else {
    ...,
    myObject = new MyObject();
}

这样的缺点在于会造成线程不安全。为了避免线程竞争造成多次创建对象,可以给getInstance方法加sychronized锁。

给整个方法加上synchronized锁会导致性能严重下降,因此可以使用双重校验锁

即只在创建类实例的时候对类类型加锁,这样之后的获取不会收到影响。

3)静态内置类实现单例模式

即在类内部使用一个静态类负责创建这个类的实例。看起来同样是饿汉模式。

private static class MyObjectHandler {private static MyObject myObject = new MyObject();}

public static MyObject getInstance() { return MyObjectHandler.myObject; }

这样做的原因是:

a. 加载类时,不会立即加载内部类。可以避免像饿汉模式那样有极高的启动时开销。

b. 仅当类的静态成员被调用,内部类才会被加载。这样可以确保线程安全(类加载一定是原子操作)

后续的改进还有,防止反射机制影响单例,以及防止序列化接口影响。

方法是使用一个静态的bool变量来表示类是否有实例被创建。

 

4)枚举类实现单例模式

Java中的枚举类类似于静态代码块,会在加载时自动调用构造方法。

一些常见的枚举数据类型(例如错误码等)常被枚举类型实现。

 5)Spring中的单例和多例

spring中的bean默认为单例。且创建时为懒汉模式。

bean的作用域默认是"singleton", 即单例。

若为prototype,则总是会创建新的实例。

若为request,则bean的作用范围是request范围。

若为session,则bean的作用范围是session范围。

 

posted @ 2022-03-27 19:41  Cheung-10  阅读(53)  评论(0)    收藏  举报