单例模式(四)

Python011

单例模式(四),第1张

本篇主要介绍几种常用的写法,还是以Java语言为例,其中会涉及到Java语言的特性,如果用其他语言来写单例模式,主要还是抓住单例的全局唯一性原则去考虑即可。如果想看往期单例模式的,可以从这些链接进入:

单例模式(一)

单例模式(二)

单例模式(三)

关于单例模式,其实网上也早有流传一种叫法,分别是懒汉模式和饿汉模式,所谓懒汉模式即懒加载,也就是等到用的时候再去创建单例对象,而饿汉模式即立即创建单例对象,下面来介绍几种常用的几种单例写法:包括2种懒汉模式 + 2种饿汉模式:

第一种:饿汉模式

在 Singleton 类初始化的时候就创建了单例对象singleton,这也是饿汉模式的命名的原因;这种模式在单例模式(一)的时候我们也分析过。

第二种:懒汉模式

这种模式就是在用的时候,才创建单例对象,关于两次的校验 if (singleton == null )和 synchronized 主要是起到了多线程安全和优化性能的作用,在单例模式(二)和 单例模式(三)中也仔细分析过。

第三种:懒汉模式 ,运用java内部类的写法

这种写法其实跟语言特性有关,内部类也是java中一种特性写法,不一定每种语言都有,所以当用其他语言来写的时候,要注意语言特性问题。

这种写法,就是在 Singleton 类中加了一个 SingletonHolder 类,这就是内部类,所谓的内部类,就是一个大类中还有一个小类,只有当调用到 getInstance 方法的时候,才会去加载 SingletonHolder 这个类( Java类加载原理 ,后面会专门有篇章介绍类加载),这样就能满足调用时再创建对象的条件了,另外由于类加载是线程安全的,所以这里也不会有多线程竞争条件存在,也符合了全局唯一性原则。

第四种:饿汉模式 ,运用java 枚举类enum的写法

枚举enum也是java语言的一个特性,跟上面的内部类一样,具体选择语言的时候,还是要根据语言特性去选择。

这种方式也是单例模式的最简写法,也是《Effective Java》作者Josh Bloch推荐的写法,既能保证多线程安全,也能保证单例对象全局唯一性。

其实写到这里似乎意犹未尽,时间不多,今天就先列举单例模式常用的4种写法,下一篇会继续介绍一些遗留的问题,比如:

等等问题,会在下一篇继续讨论,如果有其他没有涵盖到的问题或者疑问,也欢迎读者一起提问讨论。

单例模式实现方式有很多:在第一次使用的时候创建(构造函数中判断是否已经有实例存在),在类加载的时候用静态块儿创建(静态块初始化),在应用启动的时候创建。

在单线程中,基本大同小异,保证类的实例在整个应用中只有一个,都是没问题的。

但是在多线程环境下,什么时候创建这个实例是要考虑线程安全的。

枚举类型最大的特点就是:构造函数是private修饰的,也就是不能对其进行new,对象的实例都是预定义的,也就是在类加载的时候都是定义好了的,不会给其它调用去创建实例的机会。

结论是,可以模拟这个思路去创建单例,不一定非得用枚举,但是推荐用使用定义枚举的方式去实现单例模式

是的,Java枚举型是静态常量,隐式地用static final修饰过。

确切地说,Java枚举型是“静态常量”,这里面包含了两层意思:

枚举型中的实例隐式地用static final修饰过。

枚举型作为某个类中的成员字段也隐式地用static final修饰过。

public class Traffic{

public enum Light{GREEN,YELLOW,RED}

}

还是你上面这个代码,反编译一下,你就能看到--编译器背着你偷偷做了哪些手脚:

/*

* Decompiled with CFR 0_118.

*/

package com.ciaoshen.thinkinjava.chapter19

public class Traffic {

public static final class Light

extends Enum<Light>{

public static final /* enum */ Light GREEN = new Light()

public static final /* enum */ Light YELLOW = new Light()

public static final /* enum */ Light RED = new Light()

private static final /* synthetic */ Light[] $VALUES

public static Light[] values() {

return (Light[])$VALUES.clone()

}

public static Light valueOf(String string) {

return Enum.valueOf(Light.class, string)

}

private Light() {

super(string, n)

}

static {

$VALUES = new Light[]{GREEN, YELLOW, RED}

}

}

}

首先,枚举型Light是个实实在在的类。继承自基类Enum<Light>。然后在你不知情的情况下,偷偷加了static final修饰词。

然后三个枚举实例GREEN, YELLOW, RED也确确实实是Light的实例。然而前面也加上了static final。

然后构造器也被偷偷地阉割成private。这种实例控制手段,是不是在单例器模式里也见过?所以枚举也是实现单例器的一种方法。

然后编译器还偷偷合成了Light[]数组,一个values( )方法,一个valueOf( )方法。这个values( )在Enum的文档里都找不到。

如果在Enum里定义一个相关方法,你还会看到一个匿名内部类:

public enum Light{

GREEN{public void show(){System.out.println("Green")}},

YELLOW{public void show(){System.out.println("Yellow")}},

RED{public void show(){System.out.println("Red")}}

}

反编译的结果如下:

/*

* Decompiled with CFR 0_118.

*/

package com.ciaoshen.thinkinjava.chapter18

import java.io.PrintStream

public class Light

extends Enum<Light>{

public static final /* enum */ Light GREEN = new Light("GREEN", 0){

public void show() {

System.out.println("Green")

}

}

public static final /* enum */ Light YELLOW = new Light("YELLOW", 1){

public void show() {

System.out.println("Yellow")

}

}

public static final /* enum */ Light RED = new Light("RED", 2){

public void show() {

System.out.println("Red")

}

}

private static final /* synthetic */ Light[] $VALUES

public static Light[] values() {

return (Light[])$VALUES.clone()

}

public static Light valueOf(String string) {

return Enum.valueOf(Light.class, string)

}

private Light() {

super(string, n)

}

static {

$VALUES = new Light[]{GREEN, YELLOW, RED}

}

}

总之,Java的Enum枚举型整个就是一个大大的“语法糖”。明明是一个完整的类,但只向用户暴露几个静态常量,隐藏掉大部分实现的细节。