我读过,在Java中使用枚举实现单例是可能的,例如:

public enum MySingleton {
     INSTANCE;   
}

但是,上面是如何工作的呢?具体来说,Object必须被实例化。这里,MySingleton是如何被实例化的?谁正在执行new MySingleton()?


当前回答

枚举类型是类的一种特殊类型。

你的枚举实际上会被编译成

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

当您的代码第一次访问INSTANCE时,MySingleton类将由JVM加载并初始化。这个过程会初始化上面的静态字段一次(惰性地)。

其他回答

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

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

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

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

尽管这种方法尚未得到广泛应用 采用单元素枚举类型是实现单元素的最佳方式。

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

我不知道JVM或一些容器是如何控制我们的枚举实例的,但它似乎已经使用了隐式的单例模式,不同的是我们不调用getInstance,我们只是调用Enum。

这一点,

public enum MySingleton {
  INSTANCE;   
}

具有隐式空构造函数。让它显式,

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

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

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

你会看到

Here
INSTANCE

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

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

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

值得注意的是,Joshua Bloch在Effective Java第二版的第3条中详细描述了这种模式。

枚举类型是类的一种特殊类型。

你的枚举实际上会被编译成

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

当您的代码第一次访问INSTANCE时,MySingleton类将由JVM加载并初始化。这个过程会初始化上面的静态字段一次(惰性地)。