目录
Singleton Pattern
1、定义
当你需要确保一个类只有一个实例,并且希望全局访问这个实例时,可以使用单例模式。在单例模式中,一个类只有一个实例,并且该实例可以被全局访问。
实现单例模式的一种常见方法是使用一个私有构造函数、一个私有静态变量和一个公共静态方法来创建和返回该实例。私有构造函数确保其他代码无法实例化该类,而私有静态变量存储该类的唯一实例。公共静态方法允许其他代码获取该实例,并在第一次调用时创建它。
2、比喻了解
当你去餐厅吃饭时,你点了一份特别美味的菜,但是这道菜只有一份,如果你的朋友也想尝尝,那么服务员可以将这道菜分成两份,并且每个人都能品尝到同样的美味。
换句话说,在这个例子中,这道菜就像一个单例对象,因为它只有一个实例。无论你多少次点这道菜,你都会得到同样的一份。在这种情况下,服务员充当了一个类似于单例模式的角色,负责创建和管理该对象的实例,以确保客人们得到的都是同一份菜。
3、代码示例
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
在上面的示例中,Singleton 类具有一个私有构造函数,这意味着其他类无法使用 new 操作符来创建该类的实例。此外,Singleton 类还具有一个名为 instance 的私有静态变量,它存储了唯一的 Singleton 实例。
getInstance() 方法是一个公共静态方法,它允许其他类获取 Singleton 的唯一实例。如果 instance 变量为 null,则方法将创建一个新的 Singleton 实例并将其分配给 instance 变量。否则,方法将直接返回 instance 变量。
请注意,在上面的示例中,getInstance() 方法使用了一种称为“懒汉式”单例模式的实现方式。这种实现方式延迟了 Singleton 实例的创建时间,而不是在类加载时立即创建该实例。这是因为如果实例从未被使用,则在类加载时创建它可能会浪费系统资源。
let instance = null;
class Singleton {
constructor() {
if (!instance) {
instance = this;
}
return instance;
}
logMessage(message) {
console.log(message);
}
}
const singletonInstance1 = new Singleton();
const singletonInstance2 = new Singleton();
console.log(singletonInstance1 === singletonInstance2); // Output: true
singletonInstance1.logMessage("Hello, world!"); // Output: "Hello, world!"
singletonInstance2.logMessage("Same instance!"); // Output: "Same instance!"
在上面的示例中,我们定义了一个名为Singleton的类来实现单例模式。 在构造函数中我们先检查变量“instance”是否已经被赋值,如果是,则直接返回该实例;否则,将当前实例赋值给“instance”。
最终,我们创建两个名为singletonInstance1和singletonInstance2的实例。由于它们都是Singleton类的实例,因此它们应该是相等的(即,它们指向同一个对象)。输出结果也证明了这一点。
最后,我们在每个实例上调用logMessage方法,以确保它们确实是同一个对象的实例。
4、高频问题
1)单例模式的定义是什么?
答:单例模式是一种创建型设计模式,它保证一个类只有一个实例,并提供了全局访问该实例的方式。
2)为什么要使用单例模式?可以提供一个实际应用场景吗?
答:单例模式可以避免不必要的内存分配和对象创建,提高程序的性能和效率。此外,单例模式还可以确保系统中某些资源(如数据库连接池)只被一个对象占用,从而避免资源竞争和冲突。一个常见的实际应用场景是日志记录器,由于多个线程同时写入日志文件容易出现冲突,因此可以使用单例模式来确保所有线程共享同一个日志记录器实例。
3)单例模式如何实现?可以给出一个基本的实现示例代码吗?
答:单例模式可以通过一个私有构造函数、一个私有静态变量和一个公共静态方法来实现。以下是一个基本的实现
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
4) 如果需要创建线程安全的单例对象,应该怎么做?
答:为了创建线程安全的单例对象,可以使用双重检查锁定或静态内部类的方式。在使用双重检查锁定时,需要将 getInstance() 方法添加同步锁以确保只有一个线程能够访问该方法。在使用静态内部类时,由于 JVM 在加载类时会保证线程安全,因此可以避免同步锁的开销。
// 使用双重检查锁定实现线程安全的单例模式
public class Singleton {
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
// 使用静态内部类实现线程安全的单例模式
public class Singleton {
private Singleton() {}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
5) 单例模式有哪些优缺点?如何权衡这些优缺点?
答:单例模式的优点包括:
- 可以避免不必要的内存分配和对象创建,提高程序的性能和效率。
- 可以确保系统中某些资源(如数据库连接池)只被一个对象占用,从而避免资源竞争和冲突。
单例模式的缺点包括:
- 可能会导致程序结构复杂化。
- 单例模式在多线程环境下可能会引发一些问题,例如:线程安全性、可测试性等。
在权衡这些优缺点时,需要考虑具体的业务场景和应用需求,以及是否真正需要使用单例模式。如果确实需要使用单例模式,可以通过合适的实现方式来解决其缺点,并确保系统的可靠性和稳定性。