单例模式-看这一篇就够了

单例模式

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

注意:

  • 1、单例类只能有一个实例。

  • 2、单例类必须自己创建自己的唯一实例。

  • 3、单例类必须给所有其他对象提供这一实例。

    介绍

  • 意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。

  • 主要解决:一个全局使用的类频繁地创建与销毁。

  • 何时使用:当您想控制实例数目,节省系统资源的时候。

  • 如何解决:判断系统是否已经有这个单例,如果有则返回,如果没有则创建。

  • 关键代码:构造函数是私有的。

具体实现

懒汉式,线程不安全

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class SingletonLazyNotSafe {

private static SingletonLazyNotSafe instance;

private SingletonLazyNotSafe() {

}

public static SingletonLazyNotSafe getInstance() {
if (instance == null) { // 线程不安全,产生多个对象
instance = new SingletonLazyNotSafe();
}
return instance;
}
}

懒汉式,线程安全

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class SingletonLazySafe {

private static SingletonLazySafe instance;

private SingletonLazySafe() {

}

public static synchronized SingletonLazySafe getInstance() { //线程安全,效率低
if (instance == null) {
instance = new SingletonLazySafe();
}
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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
public class SingletonHungrySafe {

private static SingletonHungrySafe instance = new SingletonHungrySafe();// 类加载初始化,安全,占内存,效率高

private SingletonHungrySafe() {

}

public static SingletonHungrySafe getInstance() {
return instance;
}
}

````
#### 双检锁/双重校验锁(DCL,即 double-checked locking)

````java
public class SingletonDCL {
private volatile static SingletonDCL singleton; // volatile 指定jvm不做指令重拍

private SingletonDCL() {
}

public static SingletonDCL getSingleton() {
if (singleton == null) {
synchronized (SingletonDCL.class) { // double check lock 提升效率
if (singleton == null) {
singleton = new SingletonDCL();
}
}
}
return singleton;
}
}

````
#### 登记式/静态内部类

````java
public class SingletonInnerClass {
private static class SingletonHolder { // 饿汉式的变种
private static final SingletonInnerClass INSTANCE = new SingletonInnerClass();
}

private SingletonInnerClass() {
}

public static final SingletonInnerClass getInstance() {
return SingletonHolder.INSTANCE;
}
}
````
#### 枚举
````java
public enum SingletonEnum { // 强烈推荐

INSTANCE;

private Resource instance;

SingletonEnum() {
instance = new Resource();
}

public Resource getInstance() {
return instance;
}
}
````
#### 反射攻击
- 反射攻击的本质是修改构造函数的访问权限,然后调用构造方法,防御的关键在构造方法中进行
- 反射相关代码
````java
Class objectClass = HungrySingleton.class;
//Class.forName(HungrySingleton.class.getName()); 这种方式也能拿到对象

// 反射获取构造器
Constructor constructor = objectClass.getDeclaredConstructor();
// 修改构造方法的访问权限
constructor.setAccessible(true);
// 反射获取的实例, newInstance调用的是构造方法创建实例
HungrySingleton newInstance = (HungrySingleton) constructor.newInstance();

// 正常获取实例
HungrySingleton instance = HungrySingleton.getInstance();
  • [饿汉式]单例模式防御反射攻击代码
    1
    2
    3
    4
    5
    6
    7
    private final static HungrySingleton hungrySingleton = new HungrySingleton();
    private HungrySingleton(){
    // 【饿汉式】防止反射攻击
    if(hungrySingleton != null){
    throw new RuntimeException("单例构造器禁止反射调用");
    }
    }

    框架应用

    Spring contextBean的getBean()等