欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

【设计模式】 单例模式:别忘了还可以用枚举

程序员文章站 2023-12-21 17:03:34
...

概念

单例模式就是为了确保一个类当中只有一个实例,并且提供一个全局的公有访问点。

通俗的说,当我们需要用到某个对象的实例的时候,我们无需进行其它多余操作,而是直接通过某个接口获取到它的实例,并且这个实例在整个系统中保证唯一。

要求

这就需要我们满足两点要求:

  1. 生成的类的实例是唯一的,也就是说生成对象实例的代码只能够运行一次
  2. 生成实例的方法必须是静态方法。因为非静态方法的必须通过实例进行调用,如果已经有了实例,我们还需要生成实例的方法干什么呢,就成了先有鸡还是先有蛋的问题了。

实现方式

  • 饿汉式
  • 懒汉式
  • 双重检查
  • 静态内部类
  • 枚举

饿汉式

不管需不需要,我都要创建一个实例。

1. 使用静态常量实现

1) 构造器私有化(防止new)

2) 在类的内部创建对象

3) 向外暴露一个静态的公共方法:getInstance

//饿汉式(静态变量)
class Singleton{

 	//1. 构造器私有化, 防止外部能new
 	private Singleton() {}

    //2.本类内部创建对象实例
    private static Singleton instance = new Singleton();

    //3. 提供一个公有的静态方法,返回实例对象
    public static Singleton getInstance() {
 	    return instance;
 	}
}

优点:这种写法比较简单,在类装载的时候就完成实例化。避免了线程同步问题。

缺点:在类装载的时候就完成实例化,没有达到Lazy Loading的效果。如果从始至终从未使用过这个实例,则会造成内存的浪费。

2. 使用静态代码块实现

1) 构造器私有化(防止new)

2) 在类的内部使用静态代码块创建对象

3) 向外暴露一个静态的公共方法:getInstance

//饿汉式(静态代码块)
class Singleton{

 	//1. 构造器私有化, 防止外部能new
 	private Singleton() {}

    //2.本类内部创建对象实例
    private static Singleton instance;
    static { // 在静态代码块中,创建单例对象
 		instance = new Singleton();
    }

    //3. 提供一个公有的静态方法,返回实例对象
    public static Singleton getInstance() {
 	    return instance;
    }
}

优缺点:这种方式和上面的方式其实类似,只不过将类实例化的过程放在了静态代码块中,也是在类装载的时候,就执行静态代码块中的代码,初始化类的实例。优缺点和上面是一样的。

懒汉式

用的时候才创建,所以叫懒汉式。

1) 构造器私有化(防止new)

2) 在类的内部使用静态代码块创建对象

3) 向外暴露一个静态的公共方法:getInstance

//懒汉式
class Singleton {
    //1.构造方法私有化,防止外部调用
    private Singleton() {}

    //2.声明该静态对象
    private static Singleton instance;

    //3.提供一个静态的公有方法,当使用到该方法时,才去创建instance
    public static Singleton synchronized getInstance() {
        if(instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

优点:起到了Lazy Loading 的效果。

缺点:效率太低,每个线程在想获得类的实例时候,执行getInstance()方法都要进行同步。而其实这个方法只执行一次实例化代码就够了,后面的想获得该类实例,直接return 就行了。方法进行同步效率太低。

结论:在实际开发中,不推荐使用这种方式.

双重检查(较推荐)

//双重检查
class Singleton {
    //1. 私有化构造方法
    private Singleton() {}

    //2. 声明静态对象
 	private static Singleton instance;
 	

 	//提供一个静态的公有方法,加入双重检查代码,同时解决线程安全,懒加载和效率问题
    public static synchronized Singleton getInstance() {
     	if(instance == null) {
            synchronized (Singleton.class) {
                if(instance == null) {
                    instance = new Singleton();
                }
            }
        }
    }
    return instance;
}

1) Double-Check 概念是多线程开发中常使用到的,如代码中所示,我们进行了两次if (singleton == null)检查,这样就可以保证线程安全了。

2) 这样,实例化代码只用执行一次,后面再次访问时,判断if (singleton == null),直接return 实例化对象,也避免的反复进行方法同步.

3) 线程安全;延迟加载;效率较高

4) 结论:在实际开发中,较为推荐使用这种单例设计模式

静态内部类模式(推荐)

//静态内部类(推荐)
public class Singleton {
    //1.将构造方法私有化
    private Singleton(){}

    //写一个静态内部类,该类中有一个私有的静态实例
    private static class SingletonHolder{
        //静态初始化器,由JVM保证线程安全
        private static Singleton ourInstance = new Singleton();
    }

    //留给外界初始化的方法,返回该对象
    public static Singleton getInstance(){
        return SingletonHolder.ourInstance;
    }
}

1) 这种方式采用了类装载的机制来保证初始化实例时只有一个线程。

2) 静态内部类在该类需要实例化,调用getInstance 方法时,才会装载SingletonInstance类,从而完成Singleton的实例化。

3) 类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM 帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。

4) 优点:这种方法基于在懒汉模式中提出的,JVM在类的初始化阶段给出的线程安全性保证,将singleton 的实例化操作放置到一个静态内部类中,在第一次调用getInstance() 方法时,JVM才会去加载InstanceHolder 类,同时初始化singleton 实例,因此,即使我们不采取任何同步策略,getInstance() 方法也是线程安全的。

5) 结论:线程安全,效率高,推荐使用。

枚举(推荐)

这种方式是Effective Java 作者Josh Bloch 提倡的方式,他说最佳的单例实现模式就是枚举模式。利用枚举的特性,让JVM来帮我们保证线程安全和单一实例的问题。除此之外,写法还特别简单。

//使用枚举实现单例, 推荐
public enum Singleton {
	INSTANCE;
	public void sayOK() {
		System.out.println("ok~");
	}
}

 

相关标签: 设计模式

上一篇:

下一篇: