返回

饿汉式单例

见解分享

单例模式大全,反射拆解!你要的 8 种单例都在这!

写在前面: 单例模式是软件设计中常用的设计模式,它确保一个类只有一个实例,并且提供一个全局访问点来访问该实例。单例模式在许多场景中都有应用,如数据库连接池、缓存系统和配置管理。

反射和单例模式: 反射是一种编程技术,它允许程序在运行时检查和修改类的属性和方法。这使得程序可以动态地创建和修改对象,从而带来极大的灵活性。然而,反射也给单例模式带来了挑战,因为它允许程序绕过单例的限制,创建多个实例。

本篇文章将深入探讨单例模式,介绍 8 种不同的单例实现方式,并分析它们的优点和缺点。同时,我们将探讨反射如何破解单例模式,以及如何应对这种破解。

8 种单例模式实现方式

1. 饿汉式单例(线程安全):

这种方式在类加载时就创建实例,保证线程安全,但缺点是类加载时就占用资源。

public class Singleton {
  private static final Singleton instance = new Singleton();

  private Singleton() {}

  public static Singleton getInstance() {
    return instance;
  }
}

2. 懒汉式单例(非线程安全):

这种方式在第一次调用时才创建实例,不占用资源,但缺点是非线程安全。

public class Singleton {
  private static Singleton instance;

  private Singleton() {}

  public static Singleton getInstance() {
    if (instance == null) {
      instance = new Singleton();
    }
    return instance;
  }
}

3. 双重检查锁(线程安全):

这种方式结合了饿汉式和懒汉式的优点,在第一次调用时创建实例,同时保证线程安全。

public class Singleton {
  private volatile static Singleton instance;

  private Singleton() {}

  public static Singleton getInstance() {
    if (instance == null) {
      synchronized (Singleton.class) {
        if (instance == null) {
          instance = new Singleton();
        }
      }
    }
    return instance;
  }
}

4. 静态内部类(线程安全):

这种方式使用静态内部类来创建单例,线程安全且延迟加载。

public class Singleton {
  private Singleton() {}

  private static class SingletonHolder {
    private static final Singleton instance = new Singleton();
  }

  public static Singleton getInstance() {
    return SingletonHolder.instance;
  }
}

5. 枚举单例(线程安全):

这种方式使用枚举来创建单例,线程安全且优雅。

public enum Singleton {
  INSTANCE;

  public void doSomething() {
    // ...
  }
}

6. 代理模式(动态单例):

这种方式使用代理模式来创建单例,可以动态地创建和销毁实例。

public class SingletonProxy {
  private Singleton instance;

  public Singleton getInstance() {
    if (instance == null) {
      instance = new Singleton();
    }
    return instance;
  }
}

7. 容器管理单例(依赖注入):

这种方式使用依赖注入框架来创建和管理单例,松耦合且可扩展。

@Singleton
public class Singleton {
  // ...
}

8. ThreadLocal 单例(线程隔离):

这种方式使用 ThreadLocal 来创建单例,每个线程都有自己的独立实例。

public class Singleton {
  private static ThreadLocal<Singleton> instance = new ThreadLocal<>();

  private Singleton() {}

  public static Singleton getInstance() {
    Singleton singleton = instance.get();
    if (singleton == null) {
      singleton = new Singleton();
      instance.set(singleton);
    }
    return singleton;
  }
}

反射破解单例模式

反射可以通过调用私有构造函数或修改私有字段来破解单例模式。

Class<?> singletonClass = Class.forName("Singleton");
Constructor<?> constructor = singletonClass.getDeclaredConstructor();
constructor.setAccessible(true);
Singleton instance1 = (Singleton) constructor.newInstance();
Singleton instance2 = (Singleton) constructor.newInstance();

应对反射破解

为了应对反射破解,可以采用以下策略:

1. 通过枚举来创建单例: 枚举是天生线程安全的,并且无法通过反射破解。

2. 使用 AOP 拦截: 可以使用 AOP 框架在反射调用时拦截私有构造函数或字段的修改,从而阻止破解。

3. 使用自定义类加载器: 可以通过自定义类加载器来控制类的加载和实例化过程,防止反射绕过单例限制。

总结

单例模式是软件设计中一种重要的设计模式,本文介绍了 8 种不同的单例实现方式,并分析了它们的优缺点。同时,我们探讨了反射如何破解单例模式,以及如何应对这种破解。通过理解这些技术,开发者可以根据具体场景选择合适的单例实现方式,并确保其安全性。

附: