用Enum实现Singleton(用Java)

Ana*_*and 165 java singleton enums design-patterns

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

public enum MySingleton {
     INSTANCE;   
}
Run Code Online (Sandbox Code Playgroud)

但是,上面的工作如何?具体而言,Object必须实例化.在这里,如何MySingleton被实例化?谁在做什么new MySingleton()

Ell*_*sch 193

这个,

public enum MySingleton {
  INSTANCE;   
}
Run Code Online (Sandbox Code Playgroud)

有一个隐式的空构造函数.让我们明确一点,

public enum MySingleton {
    INSTANCE;
    private MySingleton() {
        System.out.println("Here");
    }
}
Run Code Online (Sandbox Code Playgroud)

如果您随后添加了另一个类,使用main()类似的方法

public static void main(String[] args) {
    System.out.println(MySingleton.INSTANCE);
}
Run Code Online (Sandbox Code Playgroud)

你会看到

Here
INSTANCE
Run Code Online (Sandbox Code Playgroud)

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

  • 您应该添加默认情况下枚举具有隐式私有构造函数,并且除非您实际拥有需要在该构造函数中运行的代码,否则不需要显式添加私有构造函数 (13认同)
  • public enum MySingleton {INSTANCE,INSTANCE1; 然后是System.out.println(MySingleton.INSTANCE.hashCode()); 的System.out.println(MySingleton.INSTANCE1.hashCode()); 它打印不同的哈希码.这是否意味着创建了MySingleton的两个对象? (2认同)

Sot*_*lis 72

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

你的enum声明实际上编译成类似的东西

public final class MySingleton {
    public final static MySingleton INSTANCE = new MySingleton();
    private MySingleton(){} 
}
Run Code Online (Sandbox Code Playgroud)

当您的代码首次访问时INSTANCE,该类MySingleton将由JVM加载并初始化.此过程初始化static上面的字段一次(懒惰).

  • @abg枚举常量的初始化是线程安全的. (3认同)
  • @scottmiles是的,这只是两个不同的`enum`常量. (2认同)
  • @caf,当然.见这里:https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.9.3 (2认同)

eko*_*nov 60

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

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

// Enum singleton - the preferred approach
public enum Elvis {
INSTANCE;
public void leaveTheBuilding() { ... }
}
Run Code Online (Sandbox Code Playgroud)

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

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


Jef*_*ica 9

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

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

为了它的价值,Joshua Bloch详细描述了这种模式,作为Effective Java Second Edition 3项.


Erk*_*Erk 9

正如之前在某种程度上所提到的,enum是一个 Java 类,其特殊条件是其定义必须以至少一个“枚举常量”开头。

\n

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

\n
public enum MySingleton {\n    INSTANCE;\n\n    public void doSomething() { ... }\n\n    public synchronized String getSomething() {\xc2\xa0return something; }\n\n    private String something;\n}\n
Run Code Online (Sandbox Code Playgroud)\n

您可以按照以下方式访问单例的方法:

\n
MySingleton.INSTANCE.doSomething();\nString something = MySingleton.INSTANCE.getSomething();\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n

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

\n

以下是Java 规范中关于如何防止枚举实例出现多个实例的部分:

\n
\n

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

\n
\n

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

\n


Bru*_*nco 6

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

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