文章目录
单例模式是一种创建型设计模式,用于确保一个类只有一个实例,并提供一个全局访问点。
1、特点包括:
- 类内部控制实例的创建和访问,对外部隐藏实例的具体实现细节。
- 确保在整个应用程序中只有一个实例存在。
- 提供一个全局访问点,使得其他对象可以方便地访问该实例。
2、生活中的例子
那你来简单实现一下单例模式吧。
好的,面试官。(下面是懒汉创建方式)
假设我们有一个购物车类 ShoppingCart,它用于存放购物车中的商品信息。
public class ShoppingCart {
private static ShoppingCart instance;
private List<String> items;
private ShoppingCart() {
items = new ArrayList<>();
}
public static ShoppingCart getInstance() {
if (instance == null) {
instance = new ShoppingCart();
}
return instance;
}
public void addItem(String item) {
items.add(item);
}
public void removeItem(String item) {
items.remove(item);
}
public void displayItems() {
System.out.println("Shopping Cart Items:");
for (String item : items) {
System.out.println(item);
}
}
}
在上述例子中,ShoppingCart 类的构造函数被声明为私有的,使得外部无法直接实例化该类。通过使用静态变量 instance 和静态方法 getInstance(),我们可以在需要使用 ShoppingCart 对象时获取到全局唯一的实例。
例如,在多个线程中调用 ShoppingCart.getInstance() 方法,都将返回同一个实例,保证了购物车对象的唯一性。可以通过调用 addItem()、removeItem() 和 displayItems() 方法来操作和展示购物车中的商品列表。这个例子中的购物车类就是一个简单的单例模式的实现。
3、单例模式创建方式:
-
饿汉式(Eager Initialization):在类加载时就创建实例对象,保证了线程安全。
-
懒汉式(Lazy Initialization):在第一次使用时创建实例对象,需要注意线程安全问题。
-
双重检查锁定(Double-Checked Locking):在懒汉式的基础上增加了同步锁,实现了线程安全。
-
静态内部类(Static Inner Class):利用类的加载机制保证了线程安全,且在调用getInstance()方法时才会加载内部类,实现了懒加载。
4、讲一下饿汉式和懒汉式的区别,各自的优缺点
饿汉式和懒汉式都是单例模式的创建方式,它们的主要区别在于对象的创建时机。
-
饿汉式:
- 在类加载时就创建了对象实例,无论是否需要使用该实例。
- 在类加载时就初始化,所以线程安全。
- 实现简单,代码可读性好。
- 优点:线程安全,实现简单。
- 缺点:可能会浪费资源,因为可能在程序中并未使用该实例。
-
懒汉式:
- 延迟创建对象实例,只有在需要使用时才进行初始化。
- 在多线程环境下,可能会导致竞态条件(race condition),从而导致创建多个实例。
- 需要使用同步机制(如synchronized关键字)来保证线程安全。
- 优点:延迟加载,节省资源。
- 缺点:线程不安全,需要额外的同步机制。
总结:
- 饿汉式适合在程序启动时就需要创建实例,并且创建实例的过程较为简单的情况下使用。
- 懒汉式适合在需要时才创建实例,并且创建实例的过程较为复杂的情况下使用。
5、那如何解决懒汉式中的线程安全问题
- 使用synchronized关键字同步方法或同步代码块
- 使用双重检查锁定和静态内部类的方式来创建单例。
- 双重检查锁定可以在多线程环境下保证线程安全,并且减少了不必要的同步开销;
- 而静态内部类方式则利用了类加载的特性,保证了线程安全且延迟加载。
6、模式优点:
- 保证只有一个实例存在,节省了系统资源。
- 提供了对唯一实例的全局访问点,方便其他对象对该实例的调用。
- 避免了多个实例之间的冲突和数据不一致。
- 在需要频繁创建和销毁对象的场景中,可以大大提高性能。
7、模式缺点:
- 违反了单一职责原则,因为单例类负责创建对象同时还负责管理全局状态,可能导致代码复杂性增加。
- 单例类的扩展性较差,因为单例类的行为和状态在整个系统中是固定的,难以进行扩展和修改。
- 单例模式在多线程环境下需要考虑线程安全问题,需要额外的处理来保证线程安全。
- 单例模式的使用会增加代码的耦合性,使代码难以进行单元测试。
8、应用场景:
- 当需要确保系统中只有一个实例存在时,可以使用单例模式。例如,日志记录器、数据库连接池等。
- 当某个实例需要被频繁访问时,可以使用单例模式,以避免反复创建和销毁实例,提高性能。
- 当多个实例需要共享一些数据或资源时,可以使用单例模式,以保证数据的一致性。
- 当某个对象需要被全局访问时,可以使用单例模式,以方便其他对象对该对象的调用。
总之,单例模式在某些场景下可以提供一种简单、方便和高效的对象管理方式,但同时也需要注意其缺点和适用场景,避免不必要的使用。