使用静态同步 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/