Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/395.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/design-patterns/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用枚举实现单例(在Java中)_Java_Design Patterns_Enums_Singleton - Fatal编程技术网

使用枚举实现单例(在Java中)

使用枚举实现单例(在Java中),java,design-patterns,enums,singleton,Java,Design Patterns,Enums,Singleton,我已经了解到,可以使用Enum在Java中实现Singleton,例如: public enum MySingleton { INSTANCE; } 但是,上面的工作原理是什么?具体来说,必须实例化对象。在这里,MySingleton是如何被实例化的?谁在做新的MySingleton()?这件事 public enum MySingleton { INSTANCE; } 具有隐式空构造函数。而是要明确, public enum MySingleton { I

我已经了解到,可以使用
Enum
在Java中实现
Singleton
,例如:

public enum MySingleton {
     INSTANCE;   
}
但是,上面的工作原理是什么?具体来说,必须实例化
对象。在这里,
MySingleton
是如何被实例化的?谁在做新的MySingleton()

这件事

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
字段是编译时常量,但它们是其
enum
类型的实例。而且,它们是在枚举类型第一次被引用时构造的,因为单例模式是关于拥有一个私有构造函数并调用一些方法来控制实例化(比如一些
getInstance
),在枚举中我们已经有了一个隐式私有构造函数


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

与所有枚举实例一样,Java在加载类时实例化每个对象,在某种程度上保证了它每一次只实例化一次。将
实例
声明看作是一个公共静态final字段:Java将在第一次引用类时实例化该对象

实例是在静态初始化期间创建的,静态初始化在中定义


至于它的价值,请将此模式详细描述为的第3项。

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

您的
enum
实际上将编译为

public final class MySingleton {
    public final static MySingleton INSTANCE = new MySingleton();
    private MySingleton(){} 
}
当您的代码第一次访问
实例
时,JVM将加载并初始化类
MySingleton
。此过程将上方的
静态
字段初始化一次(惰性)

在Joshua Bloch的这篇文章中,您可以找到为什么应该使用私有构造函数或枚举类型强制执行Singleton属性的解释。这一章相当长,因此要对其进行总结:

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

// Enum singleton - the preferred approach
public enum Elvis {
INSTANCE;
public void leaveTheBuilding() { ... }
}
该方法在功能上等同于公共领域方法,只是 更简洁,免费提供序列化机制,并提供 铁一般的保证,即使面对复杂的 序列化或反射攻击

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


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

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

public enum MySingleton {
    INSTANCE;

    public void doSomething() { ... }

    public synchronized String getSomething() { return something; }

    private String something;
}
您可以通过以下方式访问singleton的方法:

MySingleton.INSTANCE.doSomething();
String something = MySingleton.INSTANCE.getSomething();
正如在其他答案中提到的,使用枚举而不是类,主要是关于单例的线程安全实例化,以及保证它始终只有一个副本

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

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

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


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

谁在执行new MySingleton(),JVM是。
实例
公共静态最终MySingleton实例=new MySingleton()相同
.ENUM-一个保证的singleton.Read,为什么要使用ENUM而不是其他方法。简短:使用序列化和反射时会出现问题。您应该添加默认枚举具有隐式私有构造函数,并且不需要显式添加私有构造函数,除非您实际拥有需要在该构造函数中运行的代码每个枚举字段只创建一次实例,因此,无需创建private constructor.public enum MySingleton{INSTANCE,INSTANCE1;}和System.out.println(MySingleton.INSTANCE.hashCode());System.out.println(MySingleton.INSTANCE1.hashCode());它打印不同的哈希代码。这是否意味着创建了两个MySingleton对象?@scottmiles是的。因为你有两个例子。根据定义,单例有一个。
private
修饰符对于
enum
构造函数没有任何意义,并且是完全冗余的。我认为,要使单例可测试,可以使用策略模式,并让单例的所有操作都通过策略。所以你可以有一个“生产”策略和一个“测试”策略。。。