[关闭]
@natsumi 2017-04-22T15:49:30.000000Z 字数 2431 阅读 5925

Java枚举实现单例模式

Java


整理自:
Java 利用枚举实现单例模式
http://blog.csdn.net/yy254117440/article/details/52305175

Java枚举

基本用法

枚举的用法比较多,本文主要旨在介绍利用枚举实现单例模式的原理,所以这里也主要介绍一些相关的基础内容。
首先,枚举类似类,一个枚举可以拥有成员变量,成员方法,构造方法。先来看枚举最基本的用法:

  1. enum Type{
  2. A,B,C,D;
  3. }

创建enum时,编译器会自动为我们生成一个继承自Java.lang.Enum的类,我们上面的enum可以简单看作:

  1. class Type extends Enum{
  2. public static final Type A;
  3. public static final Type B;
  4. ...
  5. }

对于上面的例子,我们可以把Type看作一个类,而把A,B,C,D看作类的Type的实例。
当然,这个构建实例的过程不是我们做的,一个enum的构造方法限制是private的,也就是不允许我们调用。

“类”方法

上面说到,我们可以把Type看作一个类,而把A,B。。。看作Type的一个实例。同样,在enum中,我们可以定义类和实例的变量以及方法。看下面的代码:

  1. enum Type{
  2. A,B,C,D;
  3. static int value;
  4. public static int getValue() {
  5. return value;
  6. }
  7. String type;
  8. public String getType() {
  9. return type;
  10. }
  11. }

在原有的基础上,添加了类方法和实例方法。我们把Type看做一个类,那么enum中静态的域和方法,都可以视作类方法。和我们调用普通的静态方法一样,这里调用类方法也是通过 Type.getValue()即可调用,访问类属性也是通过Type.value即可访问。

“实例”方法

下面的是实例方法,也就是每个实例才能调用的方法。那么实例是什么呢?没错,就是A,B,C,D。所以我们调用实例方法,也就通过 Type.A.getType()来调用就可以了。
最后,对于某个实例而言,还可以实现自己的实例方法。再看下下面的代码:

  1. enum Type{
  2. A{
  3. public String getType() {
  4. return "I will not tell you";
  5. }
  6. },
  7. B,C,D;
  8. static int value;
  9. public static int getValue() {
  10. return value;
  11. }
  12. String type;
  13. public String getType() {
  14. return type;
  15. }
  16. }

这里,A实例后面的{…}就是属于A的实例方法,可以通过覆盖原本的方法,实现属于自己的定制。
除此之外,我们还可以添加抽象方法在enum中,强制ABCD都实现各自的处理逻辑:

  1. enum Type{
  2. A{
  3. public String getType() {
  4. return "A";
  5. }
  6. },B {
  7. @Override
  8. public String getType() {
  9. return "B";
  10. }
  11. },C {
  12. @Override
  13. public String getType() {
  14. return "C";
  15. }
  16. },D {
  17. @Override
  18. public String getType() {
  19. return "D";
  20. }
  21. };
  22. public abstract String getType();
  23. }

【鄙人觉得原作写的不对】枚举单例

  1. class Resource{
  2. }
  3. public enum SomeThing {
  4. INSTANCE;
  5. private Resource instance;
  6. SomeThing() {
  7. instance = new Resource();
  8. }
  9. public Resource getInstance() {
  10. return instance;
  11. }
  12. }

上面的类Resource是我们要应用单例模式的资源,具体可以表现为网络连接,数据库连接,线程池等等。获取资源的方式很简单,只要SomeThing.INSTANCE.getInstance() 即可获得所要实例。

下面我们来看看单例是如何被保证的:

首先,在枚举中我们明确了构造方法限制为私有,在我们访问枚举实例时会执行构造方法,同时每个枚举实例都是static final类型的,也就表明只能被实例化一次。在调用构造方法时,我们的单例被实例化。
也就是说,因为enum中的实例被保证只会被实例化一次,所以我们的INSTANCE也被保证实例化一次。

【鄙人的想法】枚举单例

原文中作者用SomeThing枚举类为Resource类实现单例,但是其实只保证了SomeThing类的构造方法只被调用一次,并没有保证Resource类的。

所以鄙人觉得应该将想要设计为单例的类自身声明为一个枚举类。枚举类的实例INSTANCE作为每次获取的实例。即通过Singleton.INSTANCE来获取。

  1. public enum Singleton {
  2. INSTANCE;//理解为public static final Singleton INSTANCE;
  3. Singleton() {//这个方法默认就是private的
  4. //do something
  5. }
  6. }

枚举单例的优点

传统的两私有一公开(私有构造方法、私有静态实例(懒实例化/直接实例化)、公开的静态获取方法)涉及线程安全问题(即使有多重检查锁也可以通过反射破坏单例)。

目前最为安全的实现单例的方法是通过内部静态enum的方法来实现,因为JVM会保证enum不能被反射并且构造器方法只执行一次

枚举实现单例还是比较简单的,除此之外我们再来看一下Enum这个类的声明:

  1. public abstract class Enum<E extends Enum<E>>
  2. implements Comparable<E>, Serializable

可以看到,枚举也提供了序列化机制。某些情况,比如我们要通过网络传输一个数据库连接的句柄,会提供很多帮助。

最后借用 《Effective Java》一书中的话,
单元素的枚举类型已经成为实现Singleton的最佳方法。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注