java单例实例化

java单例实例化,java,singleton,instance,Java,Singleton,Instance,我发现了三种实例化单例的方法,但我怀疑其中是否有一种是最好的。我在多线程环境中使用它们,更喜欢惰性实例化。 样本1: private static final ClassName INSTANCE = new ClassName(); public static ClassName getInstance() { return INSTANCE; } 样本2: private static class SingletonHolder { public static fina

我发现了三种实例化单例的方法,但我怀疑其中是否有一种是最好的。我在多线程环境中使用它们,更喜欢惰性实例化。
样本1:

private static final ClassName INSTANCE = new ClassName();

public static ClassName getInstance() {
    return INSTANCE;
}
样本2:

private static class SingletonHolder { 
    public static final ClassName INSTANCE = new ClassName();
}

public static ClassName getInstance() {
    return SingletonHolder.INSTANCE;
}
样本3:

private static ClassName INSTANCE;

public static synchronized ClassName getInstance()
{
    if (INSTANCE == null)
        INSTANCE = new ClassName();

    return INSTANCE;
}
我使用ATM的项目到处都使用样本2,但我更喜欢样本3。还有枚举版本,但我就是不明白


这里的问题是——在哪些情况下我应该/不应该使用这些变体?不过,我不想寻求冗长的解释(关于这一点,还有很多其他的话题,但它们最终都变成了争论的话题),我希望它能够用几句话就理解。

在java中实现单例最安全、最简单的方法是使用枚举(如您所提到的):

枚举语义保证只有一个
实例


如果不使用enum方法,您必须注意很多方面,比如竞争条件和反射。我一直在破坏一些框架的单例,并滥用它们,因为它们没有正确编写。枚举保证没有人会破坏它。

示例1:如果不需要懒惰的单例,请使用。
示例2:永远不要使用-它会与太多的类混淆。一个只包含一个变量的内部类似乎有点不必要。
示例3:这是一个懒惰的单身汉。如果您需要,请使用它。

首先,请绝对确保您需要单例,并且您希望提供对单例的“全局级别”访问。我发现,在许多情况下,singleton的客户不需要知道它是singleton。相反,它们只需要在实例化时获得一个服务

因此,不管你如何获取单体(如果有的话),考虑改变你的类对这个对象的访问方式。虽然这意味着修改构造函数和更改“分布更改”,但我发现依赖注入框架降低了成本。DI框架还可以处理单例实例化(例如,Guice可以这样做)


除此之外。选项3是我熟悉的典型和最常见(线程安全)的版本。选项1主要用于非惰性初始化(不总是可以接受)。我从来没见过有人用过2

示例1不使用延迟初始化

示例2和3都是懒惰的。示例2使用了没有同步开销的。因此,它比样本3快

在有效Java(第3项)中,Joshua Bloch建议单元素枚举类型是实现单元素枚举的最佳方式


但是,如果您不确定枚举类型,请坚持使用IODH。

但是我如何使用它?例如,我有这样一个:公共类MyClass{public enum ClassName{INSTANCE;//我把所有的方法都放在这里而不是像平时那样放在这里吗?}编辑:该死,注释上没有格式??讨厌它!您可以在下面编写您的单例字段和方法。然后通过
ClassName.INSTANCE.doSomething(foo)
@jurchiks:yes访问它们。在其他地方,您可以调用
ClassName.INSTANCE.methodName()
来使用单例方法(或者可能使用静态导入来摆脱
ClassName.INSTANCE.
)部分。另一个注意事项:如果我理解正确,
enum
方法肯定是不懒惰的。@Carl,类加载是懒惰的。只有在加载类但不希望加载实例时才会出现问题。我想实例化的类包含两个可能包含大量数据的Map对象,两个方法分别用于一个Map使用key获取值(如果值为null,则填充该值),以及两个访问数据库的方法(首先填充选定键的值,然后使用现有数据从数据库中插入/更新/删除数据)。这里不包括getter/setter。你认为它不应该是一个单例吗?我不确定你是否理解了我建议的核心内容。假设你将刚才描述的所有内容封装为一个接口和对应的名为DBLookupService和DBLookupService的类。对于使用LookupService的类,使用哪种类型并不重要在它出现之前-重要的是他们获得了LookupService的实例。这也使得测试变得更容易。至于它是否应该是单实例-可能,但您可以考虑使用多个实例的多核优化代码(例如,如果您可以有意义地分割数据)。但我的观点是,您应该尽一切努力避免使用该服务的代码调用该getInstance()。这将使测试和将来的更改更加容易。为什么示例1不使用延迟初始化?我认为它使用的是,因为只有一个实例,并且进行了一次初始化。
public enum ClassName {
    INSTANCE;

    // fields, setters and getters
}