使用枚举实现单例(Java)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/26285520/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 02:09:07  来源:igfitidea点击:

Implementing Singleton with an Enum (in Java)

javadesign-patternsenumssingleton

提问by Anand

I have read that it is possible to implement Singletonin Java using an Enumsuch as:

我已经读到可以Singleton使用以下方法在 Java 中实现Enum

public enum MySingleton {
     INSTANCE;   
}

But, how does the above work? Specifically, an Objecthas to be instantiated. Here, how is MySingletonbeing instantiated? Who is doing new MySingleton()?

但是,以上是如何工作的?具体来说,一个Object必须被实例化。在这里,如何MySingleton被实例化?谁在做什么new MySingleton()

采纳答案by Elliott Frisch

This,

这个,

public enum MySingleton {
  INSTANCE;   
}

has an implicit empty constructor. Make it explicit instead,

有一个隐式的空构造函数。改为明确,

public enum MySingleton {
    INSTANCE;
    private MySingleton() {
        System.out.println("Here");
    }
}

If you then added another class with a main()method like

如果您随后添加了另一个具有类似main()方法的类

public static void main(String[] args) {
    System.out.println(MySingleton.INSTANCE);
}

You would see

你会看到

Here
INSTANCE

enumfields are compile time constants, but they are instances of their enumtype. And, they're constructed when the enum type is referenced for the first time.

enum字段是编译时常量,但它们是其enum类型的实例。而且,它们是在第一次引用枚举类型时构造的。

回答by Bruno Franco

Since Singleton Patternis about having a private constructor and calling some method to control the instantiations (like some getInstance), in Enums we already have an implicit private constructor.

由于单例模式是关于拥有一个私有构造函数并调用一些方法来控制实例化(如 some getInstance),因此在 Enums 中我们已经有了一个隐式私有构造函数。

I don't exactly know how the JVMor some containercontrols the instances of our Enums, but it seems it already use an implicit Singleton Pattern, the difference is we don't call a getInstance, we just call the Enum.

我不完全知道JVM或某些容器如何控制我们的 实例Enums,但它似乎已经使用了隐式Singleton Pattern,不同之处在于我们不调用 a getInstance,我们只调用 Enum 。

回答by Jeff Bowman

Like all enum instances, Java instantiates each object when the class is loaded, with some guarantee that it's instantiated exactly once per JVM. Think of the INSTANCEdeclaration as a public static final field: Java will instantiate the object the first time the class is referred to.

与所有枚举实例一样,Java 在加载类时实例化每个对象,并保证每个JVM 只实例化一次。将INSTANCE声明视为公共静态 final 字段:Java 将在第一次引用该类时实例化该对象。

The instances are created during static initialization, which is defined in the Java Language Specification, section 12.4.

实例是在静态初始化期间创建的,这在Java 语言规范的第 12.4 节中进行了定义。

For what it's worth, Joshua Blochdescribes this pattern in detail as item 3 of Effective Java Second Edition.

就其价值而言,Joshua BlochEffective Java Second Edition3 项中详细描述了这种模式。

回答by Sotirios Delimanolis

An enumtype is a special type of class.

一种enum类型是一种特殊类型的class

Your enumwill actually be compiled to something like

enum实际上会被编译成类似的东西

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

When your code first accesses INSTANCE, the class MySingletonwill be loaded and initialized by the JVM. This process initializes the staticfield above once(lazily).

当您的代码第一次访问 时INSTANCE,该类MySingleton将被 JVM 加载和初始化。这个过程初始化static上面的字段一次(懒惰)。

回答by ekostadinov

In this Java best practices bookby Joshua Bloch, you can find explained why you should enforce the Singleton property with a private constructor or an Enum type. The chapter is quite long, so keeping it summarized:

在Joshua Bloch 的这本Java 最佳实践书中,您可以找到解释为什么应该使用私有构造函数或 Enum 类型强制执行 Singleton 属性。这章很长,所以总结一下:

Making a class a Singleton can make it difficult to test its clients, as it's impossible to substitute a mock implementation for a singleton unless it implements an interface that serves as its type. Recommended approach is implement Singletons by simply make an enum type with one element:

使一个类成为单例会使测试其客户端变得困难,因为除非它实现了作为其类型的接口,否则不可能用模拟实现代替单例。推荐的方法是通过简单地创建一个具有一个元素的枚举类型来实现单例:

// Enum singleton - the preferred approach
public enum Elvis {
INSTANCE;
public void leaveTheBuilding() { ... }
}

This approach is functionally equivalent to the public field approach, except that it is more concise, provides the serialization machinery for free, and provides an ironclad guarantee against multiple instantiation, even in the face of sophisticated serialization or reflection attacks.

这种方法在功能上等同于公共字段方法,只是它更简洁,免费提供序列化机制,并提供了针对多个实例化的铁定保证,即使面对复杂的序列化或反射攻击。

While this approach has yet to be widely adopted, a single-element enum type is the best way to implement a singleton.

虽然这种方法尚未被广泛采用,但单元素枚举类型是实现单例的最佳方式。

回答by Erk

As has, to some extent, been mentioned before, an enum is a java class with the special condition that its definition must start with at least one "enum constant".

正如之前在某种程度上提到的那样,枚举是一个具有特殊条件的 java 类,它的定义必须以至少一个“枚举常量”开头。

Apart from that, and that enums cant can't be extended or used to extend other classes, an enum is a class like any class and you use it by adding methods below the constant definitions:

除此之外,枚举不能扩展或用于扩展其他类,枚举与任何类一样是一个类,您可以通过在常量定义下方添加方法来使用它:

public enum MySingleton {
    INSTANCE;

    public void doSomething() { ... }

    public synchronized String getSomething() {?return something; }

    private String something;
}

You access the singleton's methods along these lines:

您可以按照以下方式访问单身人士的方法:

MySingleton.INSTANCE.doSomething();
String something = MySingleton.INSTANCE.getSomething();

The use of an enum, instead of a class, is, as has been mentioned in other answers, mostly about a thread-safe instantiation of the singleton and a guarantee that it will always only be one copy.

正如其他答案中所提到的,使用枚举而不是类主要是关于单例的线程安全实例化,并保证它始终只有一个副本。

And, perhaps, most importantly, that this behavior is guaranteed by the JVM itself and the Java specification.

而且,也许最重要的是,这种行为是由 JVM 本身和 Java 规范保证的。

Here's a section from the Java specificationon how multiple instances of an enum instance is prevented:

这是Java 规范中关于如何防止枚举实例的多个实例的一节:

An enum type has no instances other than those defined by its enum constants. It is a compile-time error to attempt to explicitly instantiate an enum type. The final clone method in Enum ensures that enum constants can never be cloned, and the special treatment by the serialization mechanism ensures that duplicate instances are never created as a result of deserialization. Reflective instantiation of enum types is prohibited. Together, these four things ensure that no instances of an enum type exist beyond those defined by the enum constants.

除了由其枚举常量定义的实例之外,枚举类型没有其他实例。尝试显式实例化枚举类型是编译时错误。Enum 中的最终 clone 方法确保永远不会克隆枚举常量,并且序列化机制的特殊处理确保永远不会因反序列化而创建重复的实例。禁止枚举类型的反射实例化。这四件事一起确保枚举类型的实例不存在于枚举常量定义的实例之外。

Worth noting is that after the instantiation any thread-safety concerns must be handled like in any other class with the synchronized keyword etc.

值得注意的是,在实例化之后,任何线程安全问题都必须像在使用 synchronized 关键字等的任何其他类中一样处理。