引入

在之前的文章中我们介绍了常见的设计模式,今天我们来使用 Java 语言对单例模式进行具体实现举例。

实例

在下面的例子中,我们使用单例模式来保证每次生成的对象都是唯一的。

统一接口

1
2
3
4
5
6
7
/**
* 单例对象
*
* @author marx
* @date 2022/03/10
*/
interface SingletonObject{}

饿汉式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
* 饿汉
*
* @author marx
* @date 2022/03/10
*/
class Hungry implements SingletonObject{
/**
* 实例
*/
private static Hungry instance = new Hungry();

/**
* 私有构造,避免外部利用 new 创建实例
*/
private Hungry() {}

/**
* 得到实例通过饿汉式
*
* @return {@link Singleton}
*/
public static Hungry getInstanceByHungry() {
return instance;
}
}

懒汉式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/**
* 懒汉
*
* @author marx
* @date 2022/03/10
*/
class LazyBones implements SingletonObject{
/**
* 实例
*/
private static LazyBones instance;

/**
* 私有构造,避免外部利用 new 创建实例
*/
private LazyBones() {}

/**
* 得到实例通过懒汉式
*
* @return {@link Singleton}
*/
public synchronized static LazyBones getInstanceByLazyBones() {
if (instance == null) {
instance = new LazyBones();
}

return instance;
}
}

双重锁检测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/**
* Double-checked locking
* 双重锁检测
*
* @author marx
* @date 2022/03/10
*/
class DCL implements SingletonObject {
/**
* 实例
*/
private static DCL instance;

/**
* 私有构造,避免外部利用 new 创建实例
*/
private DCL() {}

/**
* 得到实例通过双重锁检测
*
* @return {@link Singleton}
*/
public synchronized static DCL getInstanceByDCL() {
// 第一重检测,检测实例是否为空
if (instance == null) {
// 此处锁住DCL类以保证对象唯一性
synchronized (DCL.class) {
// 第二重检测,避免在锁争夺时重复生成实例
// 例如:A、B都在争抢锁
// A率先拿到锁,生成实例并获取后释放锁
// B拿到锁,假如没有第二重检测,B会再度创建一个实例破坏单例性
if (instance == null) {
instance = new DCL();
}
}
}

return instance;
}
}

静态内部类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/**
* 静态内部类
*
* @author marx
* @date 2022/03/10
*/
class StaticInnerClass implements SingletonObject {
/**
* 私有构造,避免外部利用 new 创建实例
*/
private StaticInnerClass() {
}

/**
* 得到实例通过静态内部类
*
* @return {@link StaticInnerClass}
*/
public static StaticInnerClass getInstanceByStaticInnerClass() {
return InnerClass.INSTANCE;
}

/**
* 内部类
*
* @author marx
* @date 2022/03/10
*/
static class InnerClass{
/**
* 实例
*/
private static final StaticInnerClass INSTANCE = new StaticInnerClass();
}
}

枚举

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 枚举
*
* @author marx
* @date 2022/03/10
*/
enum Enum implements SingletonObject{
/**
* 实例
*/
INSTANCE;

public static Enum getInstanceByEnum() {
return INSTANCE;
}
}

测试结果

笔者在进行测试时将上面的所有接口及类以及下面的测试类写在一个java文件中,测试结果在不同电脑不同时间上可能有所不同。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public static void main(String[] args) {
Object o1 = new Object();
Object o2 = new Object();
printInfo("普通创建", o1, o2);

SingletonObject s1 = Hungry.getInstanceByHungry();
SingletonObject s2 = Hungry.getInstanceByHungry();
printInfo("饿汉式", s1, s2);

s1 = LazyBones.getInstanceByLazyBones();
s2 = LazyBones.getInstanceByLazyBones();
printInfo("懒汉式", s1, s2);

s1 = DCL.getInstanceByDCL();
s2 = DCL.getInstanceByDCL();
printInfo("双重锁检测", s1, s2);

s1 = StaticInnerClass.getInstanceByStaticInnerClass();
s2 = StaticInnerClass.getInstanceByStaticInnerClass();
printInfo("静态内部类", s1, s2);

s1 = Enum.getInstanceByEnum();
s2 = Enum.getInstanceByEnum();
printInfo("枚举类", s1, s2);
}

public static void printInfo(String way, Object s1, Object s2) {
System.out.println(way + ": ");
System.out.println("s1 = " + s1);
System.out.println("s2 = " + s2);
System.out.println();
}

结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
普通创建: 
s1 = java.lang.Object@404b9385
s2 = java.lang.Object@6d311334

饿汉式:
s1 = designPattern.Hungry@682a0b20
s2 = designPattern.Hungry@682a0b20

懒汉式:
s1 = designPattern.LazyBones@3d075dc0
s2 = designPattern.LazyBones@3d075dc0

双重锁检测:
s1 = designPattern.DCL@214c265e
s2 = designPattern.DCL@214c265e

静态内部类:
s1 = designPattern.StaticInnerClass@448139f0
s2 = designPattern.StaticInnerClass@448139f0

枚举类:
s1 = INSTANCE
s2 = INSTANCE