关于设计模式:使用Enum实现Singleton(Java)

Implementing Singleton with an Enum (in Java)

我已经阅读过,可以使用EDCOX1,1,例如,在Java中实现EDCOX1×0Ω。

1
2
3
public enum MySingleton {
     INSTANCE;  
}

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


这个,

1
2
3
public enum MySingleton {
  INSTANCE;  
}

具有隐式空构造函数。把它明确化,

1
2
3
4
5
6
public enum MySingleton {
    INSTANCE;
    private MySingleton() {
        System.out.println("Here");
    }
}

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

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

你会看到

1
2
Here
INSTANCE

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


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

你的enum实际上会被编译成

1
2
3
4
public final class MySingleton {
    public final static MySingleton INSTANCE = new MySingleton();
    private MySingleton(){}
}

当代码第一次访问INSTANCE时,类MySingleton将由JVM加载和初始化。此过程将对上面的static字段进行一次初始化(延迟)。


在Joshua Bloch的Java最佳实践手册中,您可以找到解释为什么要用私有构造函数或枚举类型强制执行SuntLon属性。这一章很长,所以要总结一下:

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

1
2
3
4
5
// Enum singleton - the preferred approach
public enum Elvis {
INSTANCE;
public void leaveTheBuilding() { ... }
}

这种方法在功能上等同于公共领域方法,除了更简洁,免费提供系列化机器,并提供即使在面对复杂的情况下,也能保证不被多次实例化序列化或反射攻击。

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


与所有枚举实例一样,Java在加载类时实例化每个对象,并保证它每JVM只实例化一次。将EDOCX1 7声明作为公共静态最终字段来考虑:Java将在第一次引用类时实例化对象。

实例是在静态初始化过程中创建的,它是在Java语言规范(第12.4节)中定义的。

对于它的价值,Joshua Bloch详细描述了这个模式作为有效的Java第二版的第3项。


由于singleton模式需要一个私有的构造函数,并调用一些方法来控制枚举中的实例化(比如一些getInstance),因此我们已经有了一个隐式私有构造函数。

我不知道JVM或某些容器如何控制我们的Enums的实例,但它似乎已经使用了隐式Singleton Pattern,区别在于我们不称getInstance,我们只称枚举。


在某种程度上,前面提到过,枚举是一个Java类,具有特殊的条件,它的定义必须从至少一个"枚举常量"开始。

它是一个类,类似于任何类,通过在常量定义下面添加方法来使用它:

1
2
3
4
5
6
7
8
9
public enum MySingleton {
    INSTANCE;

    public void doSomething() { ... }

    public synchronized String getSomething() { return something; }

    private String something;
}

您可以沿着以下行访问singleton的方法:

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

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

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

下面是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.

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