Ref: https://javadoop.com/post/design-pattern

1.饿汉模式

饿汉模式最简单:

  1. public class Singleton {
  2. // 首先,将 new Singleton() 堵死
  3. private Singleton() {};
  4. // 创建私有静态实例,意味着这个类第一次使用的时候就会进行创建
  5. private static Singleton instance = new Singleton();
  6. public static Singleton getInstance() {
  7. return instance;
  8. }
  9. // 瞎写一个静态方法。这里想说的是,如果我们只是要调用 Singleton.getDate(...),
  10. // 本来是不想要生成 Singleton 实例的,不过没办法,已经生成了
  11. public static Date getDate(String mode) {return new Date();}
  12. }

很多人都能说出饿汉模式的缺点,可是我觉得生产过程中,很少碰到这种情况:你定义了一个单例的类,不需要其实例,可是你却把一个或几个你会用到的静态方法塞到这个类中。

2.饱汉模式

饱汉模式最容易出错:

  1. public class Singleton {
  2. // 首先,也是先堵死 new Singleton() 这条路
  3. private Singleton() {}
  4. // 和饿汉模式相比,这边不需要先实例化出来,注意这里的 volatile,它是必须的
  5. private static volatile Singleton instance = null;
  6. public static Singleton getInstance() {
  7. if (instance == null) {
  8. // 加锁
  9. synchronized (Singleton.class) {
  10. // 这一次判断也是必须的,不然会有并发问题
  11. if (instance == null) {
  12. instance = new Singleton();
  13. }
  14. }
  15. }
  16. return instance;
  17. }
  18. }

双重检查,指的是两次检查 instance 是否为 null。 volatile 在这里是需要的,希望能引起读者的关注。 很多人不知道怎么写,直接就在 getInstance () 方法签名上加上 synchronized,这就不多说了,性能太差。

3.嵌套类

静态嵌套类最经典,以后大家就用它吧:

  1. public class Singleton3 {
  2. private Singleton3() {}
  3. // 主要是使用了 嵌套类可以访问外部类的静态属性和静态方法 的特性
  4. private static class Holder {
  5. private static Singleton3 instance = new Singleton3();
  6. }
  7. public static Singleton3 getInstance() {
  8. return Holder.instance;
  9. }
  10. }

注意,很多人都会把这个嵌套类说成是静态内部类,严格地说,内部类和嵌套类是不一样的,它们能访问的外部类权限也是不一样的。

最后,我们说一下枚举,枚举很特殊,它在类加载的时候会初始化里面的所有的实例,而且 JVM 保证了它们不会再被实例化,所以它天生就是单例的。
虽然我们平时很少看到用枚举来实现单例,但是在 RxJava 的源码中,有很多地方都用了枚举来实现单例。

单例模式优缺点

Ref: https://www.cnblogs.com/damsoft/p/6105122.html
缺点:
1. 不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误,不能保存彼此的状态。
2. 由于单利模式中没有抽象层,因此单例类的扩展有很大的困难。
3. 单例类的职责过重,在一定程度上违背了 “单一职责原则”。
4. 滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态的丢失。