什么用
实际上我们经常见到这种设计模式。当你要打印东西时,你有很多个打印任务,但正在工作的却只有一个;当你从数据库获取数据时,看上去你会建立很多次连接,但其实你的连接都是从唯一的数据库连接池中获取的;还有Spring中的bean一般默认是单例的。
对于这种要么没有实例,有就只有一个实例的类,我们可以让类自己保存它的唯一实例,保证没有其他实例被创建,并且向外部提供一个访问该实例的方法。
模式结构
- 单例类
负责保证只能创建一个实例,且外部可访问该实例。
优缺点
优点
- 提供了对唯一实例的受控访问。因为单例类封装了它的唯一实例,确保所有的对象都访问一个实例。
- 节约系统资源。对于一些需要频繁创建和销毁的对象,单例模式无疑可以提高系统的性能。
- 允许可变数目的实例。我们可以基于单例模式进行扩展,使用与单例控制相似的方法来获得指定个数的对象实例。
缺点
- 由于单例模式没有抽象层,因此单例类的扩展有很大的困难。
- 一定程度上违背了单一职责原则。因为单例类不仅需要负责实例的创建,还要对创建的实例是否唯一进行判断,既充当了工厂角色,又充当了产品角色,将产品的创建与产品本身的功能融合到了一起。
适用情况
- 频繁实例化然后销毁的对象
- 创建对象时耗时过多或者耗资源过多,但又经常用到的对象。
- 频繁访问 IO 资源的对象,例如数据库连接池或访问本地文件。
扩展
- 与实用类的区别。实用类没有状态,只是一些方法属性的集合;单例类有状态,且可以有子类来继承。
- 使用懒单例模式时注意线程安全问题。
- 不能用反射模式创建单例,否则会实例化一个新的对象。
实际应用
- 数据库连接池
- 线程池
代码示例
https://github.com/zero4eva/design_pattern
这里只给出最简单常用的饿汉方法,上面的github有其他的实现
- PrinterTest.java
public class PrintTest {
public static void main(String[] args) {
String file = "xxx.doc";
SimpleLazyPrinter printer1 = SimpleLazyPrinter.getInstance();
printer1.print(file);
SimpleLazyPrinter printer2 = SimpleLazyPrinter.getInstance();
System.out.println(printer1 == printer2);
}
}
- SimpleHungryPrinter.java
// 饿汉式,线程安全 public class SimpleHungryPrinter { // 将构造器私有,禁止外界通过构造器创建实例 private SimpleHungryPrinter() { } // 类加载的时候就创建该实例 private static SimpleHungryPrinter instance = new SimpleHungryPrinter(); // 只能通过该方法获取实例 public static SimpleHungryPrinter getInstance() { return instance; } public void print(String file) { System.out.println("print " + file + " now..."); } }