java - 正确使用 Singleton getInstance 方法

标签 java singleton

使用静态同步 getInstance 方法的单例的正确用法是什么?为什么?

例如:

MySingleton mySingleton = MySingleton.getInstance();
.....
mySingleton.doSomething();

其中 mySingleton 将是一个字段并在整个类(class)中使用。

对比

MySingleton.getInstance().doSomething();

编辑:我指的是编码风格和线程安全。

最佳答案

虽然我同意安迪·特纳 (Andy Turner) 的回答,即尽可能避免单例,但为了完整起见,我将添加以下内容:静态同步 getInstance 方法是过去的遗物。至少有 3 个单例模式实现不需要这样做。

a) 急切的单例

public class EagerSingleton{
  private EagerSingleton(){}
  private static final EagerSingleton INSTANCE = new EagerSingleton();
  public static EagerSingleton getInstance(){ return INSTANCE; }
}

无论何时以任何方式引用 EagerSingleton 类(例如,通过声明该类型的变量),它都会在类加载时初始化。

b) 懒惰的单例

public class LazySingleton{
    private LazySingleton(){}
    private static class Holder{
        private static final LazySingleton INSTANCE = new LazySingleton();
    }
    public static LazySingleton getInstance(){ return Holder.INSTANCE; }

}

这是在调用时初始化的,即您第一次调用方法 getInstance() 时。类加载器现在加载 holder 类并初始化 INSTANCE 字段,并且保证以同步方式进行。如果您的单例设置成本高昂并且并非总是必要,请使用此版本。

c) 枚举单例

public enum EnumSingleton{
    INSTANCE;

    // and if you want, you can add the method, too, but it's
    // unnecessary:
    public static EnumSingleton getInstance(){ return INSTANCE; }
}

枚举项是编译时常量,即它们的唯一性在编译时得到保证,而不仅仅是在运行时(在运行时,每个类加载器的单例都是唯一的,并且适用于所有版本)。这是最好的方法,除非您的要求是从现有类扩展。

枚举版本免费为您提供许多其他功能:

  • 开箱即用的 equals/hashCode/toString 实现
  • 防御反序列化攻击
  • 多点支持(只需添加另一个枚举项)

所以下次你看到有人写双重检查锁定机制或类似的东西时,告诉他们他们太复杂和技术性了。让类加载器为您完成工作。

对于所有 3 个版本:确保您使用的功能由接口(interface)支持,并针对该接口(interface)而不是实现类型进行编码。这将使您的代码可测试。

关于java - 正确使用 Singleton getInstance 方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40367748/

相关文章:

java - 用随机数填充 double 组,然后打印该数组中的偶数和奇数

c# - 处理单例实例 (C#)

java - hibernate jpa 应用程序每次我想运行它时都会挂起

java - 在数组中查找具有特定字母的单词时出现 ArrayIndexOutOfBoundsException

c++ - 具有政策的 Alexandrescu 单例

java - 在 Firebase 监听器中设置 Singleton 属性值

Objective-C 替代使用 ApplicationDelegate 或单例来传递数据

ios - 单例+初始化代码

java - 如何使用用户输入在网格上随机打印星号

java - 需要更好的 Java 应用程序部署策略