单例模式
# 单例模式
在单例模式中,类的构造函数被私有化,使得外部无法直接实例化该类。而是通过类内部的静态方法或者静态属性来获取类的唯一实例。这个实例在首次被创建后会被缓存起来,并在后续的调用中重复使用。
# 定义
保证整个程序一个类只有一个实例,并提供一个全局访问点来访问该实例。
# 优点
全局访问点:通过单例模式,可以提供一个全局的访问点,使得其他对象可以方便地访问该实例,而不需要传递实例的引用。
单一实例:单例模式保证一个类只有一个实例存在,避免了多个实例的创建和资源的重复分配,节省了系统资源。
延迟实例化:单例模式可以延迟实例化,即在首次被使用时才进行实例化操作,提高了系统的性能和资源利用率。
数据共享:由于单例模式只有一个实例存在,因此可以方便地实现数据的共享和通信,简化了不同模块之间的数据传递。
# 缺点
难以扩展:由于单例模式只允许存在一个实例,因此扩展时可能会遇到困难。如果需要拓展功能,可能需要修改原有的单例类,违反了开闭原则。
隐藏依赖关系:单例模式会隐藏类之间的依赖关系,使得代码的耦合性增加。这可能会导致代码的理解和维护变得困难。
可能引入全局变量:单例模式的实例在整个系统中是全局可访问的,可能会导致实例被滥用,引入全局变量的问题。这可能会增加代码的复杂性和难以追踪错误。
# 应用场景
资源共享:当多个对象需要共享同一个资源时,可以使用单例模式确保资源只有一个实例。例如,线程池、数据库连接池等资源管理器可以使用单例模式来实现。
配置信息管理:单例模式可以用于管理全局的配置信息,确保在整个系统中只有一个配置对象,并提供统一的访问接口。这样可以方便地获取和修改配置信息。
日志记录器:在系统中使用单例模式来实现日志记录器可以确保日志信息的一致性。所有的日志操作通过单例对象进行记录,方便集中管理和控制日志输出。
缓存管理:单例模式可以用于实现缓存管理器,确保在系统中只有一个缓存对象。这样可以提高数据读写的效率,并减少对底层资源的访问压力。
计数器、计时器等工具类:一些工具类,如计数器、计时器等,只需要一个实例来提供计数和计时功能。使用单例模式可以方便地实现这些工具类。
提示
单例模式在使用时需要慎重考虑,避免滥用。过度使用单例模式可能会引入全局状态和共享状态,增加程序的复杂性,并对代码的测试和维护产生影响。正确地选择和使用单例模式是需要根据具体情况和需求来决定的。
# 示例代码1
场景1:
单例模式创建实例的方式
/**
* 饿汉式单例模式
* 线程安全,可能会创建不需要的实例,产生垃圾对象,造成资源浪费。
*/
public class HungrySingleton {
private static HungrySingleton instance = new HungrySingleton();
/**
* 私有构造方法
*/
private HungrySingleton() {
}
/**
* 对外提供公开静态获取实例的方法
* @return
*/
public static HungrySingleton getInstance() {
return instance;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 懒汉式单例模式(线程不安全)
* 多个线程同时获取实例时,会实例化出多个实例,导致线程不安全
*/
public class LazySingleton {
private static LazySingleton instance;
/**
* 私有构造方法
*/
private LazySingleton() {
}
/**
* 获取实例方法
*
* @return
*/
public static LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
/**
* 线程安全懒汉式单例模式(线程安全、性能低)
* 在getInstance()方法上添加了同步锁,解决了线程安全问题,但是降低了性能。
*/
public class LazySafeSingleton {
private static LazySafeSingleton instance;
/**
* 私有构造方法
*/
private LazySafeSingleton() {
}
/**
* 获取实例方法
*
* @return
*/
public static synchronized LazySafeSingleton getInstance() {
if (instance == null) {
instance = new LazySafeSingleton();
}
return instance;
}
}
/**
* 双重校验懒汉式单例模式(线程安全、性能高)
* 在方法内部使用锁,解决了线程安全和性能低效问题。
*/
public class LazyDoubleCheckSingleton {
private static LazyDoubleCheckSingleton instance;
/**
* 私有构造方法
*/
private LazyDoubleCheckSingleton() {
}
/**
* 获取实例方法
*
* @return
*/
public static LazyDoubleCheckSingleton getInstance() {
if (instance == null) {
synchronized (LazyDoubleCheckSingleton.class) {
if (instance == null) {
instance = new LazyDoubleCheckSingleton();
}
}
}
return instance;
}
}
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
/**
* 静态内部类单例模式
*
* 内部静态类适用于静态域情况,双重校验锁可在实例域需要延迟初始化时使用。
*/
public class InnerSingleton {
/**
* 静态内部类
*/
private static class Inner {
public static InnerSingleton instance = new InnerSingleton();
}
/**
* 私有构造方法
*/
private InnerSingleton() {
}
/**
* 调用getInstance方法时内部类才会初始化,静态变量instance才被创建
* @return
*/
public static InnerSingleton getInstance() {
return Inner.instance;
}
}
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
/**
* 枚举单例模式
* 简洁、自动支持序列化、非常高效
*/
public enum EnumSingleton {
INSTANCE;
public static EnumSingleton getInstance() {
return INSTANCE;
}
}
2
3
4
5
6
7
8
9
10
11
// Make sure to add code blocks to your code group
# 示例代码2
场景2:
单例模式管理数据库连接对象,双重校验锁和枚举方式分别实现单例模式。
双重校验锁需要考虑线程安全、同步控制等细节,代码相对较复杂,枚举方式代码简洁明了、防止反射和序列化攻击,推荐使用枚举实现单例模式,如需延迟初始化或与旧代码兼容时才考虑使用双重校验锁方式。
/**
* 数据库连接类
* 单例类,构造方法私有化,外部无法实例化该对象,通过getInstance()方法获取类的唯一实例,避免重复创建实例。
*/
@Slf4j
public class DatabaseConnection {
private static DatabaseConnection instance;
/**
* 私有构造方法
*/
private DatabaseConnection() {
}
/**
* 获取数据库连接
* @return
*/
public static DatabaseConnection getInstance() {
if (instance == null) {
synchronized (DatabaseConnection.class) {
if (instance == null) {
instance = new DatabaseConnection();
}
}
}
return instance;
}
public void connect(){
log.info("连接数据库");
}
public void disconnect(){
log.info("断开数据库连接");
}
}
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
/**
* 数据库连接类
*/
@Slf4j
public enum DatabaseConnectionEnum {
INSTANCE;
public void connect(){
log.info("连接数据库");
}
public void disconnect(){
log.info("断开数据库连接");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 测试类
*/
public class Client {
public static void main(String[] args) {
// 双重校验单例模式
DatabaseConnection connection = DatabaseConnection.getInstance();
connection.connect();
// 其它操作...
connection.disconnect();
// 枚举单例模式
DatabaseConnectionEnum connectionEnum = DatabaseConnectionEnum.INSTANCE;
connectionEnum.connect();
// 其它操作...
connectionEnum.disconnect();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Make sure to add code blocks to your code group