您最近深入研究了单例设计模式、如何在 Java 中实现它以及它的用途。但是,如果我告诉您 Java 自带开箱即用的单例呢?感兴趣吗?那么让我们开始吧。

您可能已经了解Enum 类。它有一个你应该知道的特殊功能。具体来说,Enum实现了单例设计模式。此选项几乎与涉及公共字段的单例方法相同。

单例作为枚举:


public enum Device {   
    PRINTER	
} 
    

单例作为公共变量:


public class Printer {   
    public static final Printer PRINTER = new Printer();   
    private Printer() {
    }
//…
}
    

枚举方法比公共字段方法更紧凑,因为我们不需要编写自己的实现最重要的是,枚举在序列化方面没有问题。

枚举的序列化与普通对象不同:只有枚举名称的值被序列化。在反序列化期间,该方法与反序列化名称一起使用以获取实例。此外,枚举可以保护您免受反射攻击

您将在第二个模块的课程中了解有关反射的更多信息,我们将在其中探索反射 API

Java 禁止实例化枚举——这是Constructor类的newInstance方法实现中的一个限制,在通过反射创建对象时通常会调用该方法。

Constructor.newInstance中的代码摘录。用于创建 枚举


if ((clazz.getModifiers() & Modifier.ENUM) != 0)
    throw new IllegalArgumentException("Cannot reflectively create enum objects");
    

使用枚举 创建单例的缺点包括:

  • 缺少惰性初始化,因为对象是立即创建的,初始化不能延迟。

  • 其他类不能扩展。也就是说,在需要继承另一个类的情况下,将枚举用作单例是行不通。在这种情况下,我们需要转向我们已经熟悉的其他实现选项:静态方法或公共变量。

  • 将枚举作为单例使用时,只能使用一个枚举字段。


public enum Device extends Electricity { 
    PRINTER 
}
    

这段代码会给我们一个编译错误:

枚举不允许扩展子句

但是如果我们需要实现一个接口,就没有问题,因为枚举可以实现接口:


public enum Device implements Electricity { 
    PRINTER 
}
    

如果不需要使用继承,最好通过enum实现单例模式。我们并不是唯一推荐这一点的人——Joshua Bloch 本人也这样做

这种实现方法为您提供方便、紧凑、开箱即用的序列化、防止反射攻击和唯一性——优秀的单例所需要的一切!