今天我浏览了这个网站上的一些问题,我发现一个枚举被用于单例模式,据称线程安全的好处,这种解决方案。

我从来没用过枚举,而且我已经用Java编程好几年了。显然,他们改变了很多。现在他们甚至在自己内部完全支持OOP。

为什么我应该在日常编程中使用enum ?


当前回答

枚举是什么

enum是为枚举定义的关键字,是一种新的数据类型。应该广泛使用类型安全枚举。特别是,它们是较老api中用来表示相关项集的简单String或int常量的健壮替代。

为什么使用enum

enum是java.lang.Enum的隐式final子类 如果枚举是类的成员,则它是隐式静态的 New永远不能用于枚举,即使是在枚举类型本身中 name和valueOf简单地使用枚举常量的文本,而toString可以被覆盖以提供任何内容,如果需要的话 对于枚举常量,equals和==等于同一个东西,并且可以互换使用 枚举常量是隐式的公共静态final

Note

枚举不能扩展任何类。 enum不能是超类。 枚举常量的出现顺序被称为它们的“自然顺序”,并定义了其他项使用的顺序:compareTo,值的迭代顺序,EnumSet, EnumSet.range。 枚举可以有构造函数、静态和实例块、变量和方法,但不能有抽象方法。

其他回答

从Java文档-

您应该在任何时候使用枚举类型 需要表示一个固定的集合 常量。包括自然enum 比如我们太阳系中的行星 你知道的系统和数据集 编译时所有可能的值 时间——例如,a上的选项 菜单、命令行标志等等。

一个常见的例子是用枚举类型替换一个类,用一组私有静态final int常量(常量数量合理)。基本上,如果你认为你在编译时知道“something”的所有可能值,你可以将其表示为枚举类型。比起带有常量的类,枚举提供了可读性和灵活性。

枚举类型几乎没有其他优点。它们总是特定枚举类的一个实例(因此出现了使用枚举作为单例的概念)。另一个优点是可以在switch-case语句中使用枚举作为类型。你也可以在枚举上使用toString()将它们打印为可读的字符串。

Something none of the other answers have covered that make enums particularly powerful are the ability to have template methods. Methods can be part of the base enum and overridden by each type. And, with the behavior attached to the enum, it often eliminates the need for if-else constructs or switch statements as this blog post demonstrates - where enum.method() does what originally would be executed inside the conditional. The same example also shows the use of static imports with enums as well producing much cleaner DSL like code.

其他一些有趣的特性包括枚举提供equals()、toString()和hashCode()的实现,并实现Serializable和Comparable。

我强烈推荐Bruce Eckel的《Thinking in Java》第4版,它用了整整一章来讨论这个主题。特别具有启发性的例子是将石头剪子布(即RoShamBo)游戏作为枚举。

这里有很多答案,只想指出两个具体的:

1)在Switch-case语句中作为常量使用。 Switch case不允许使用String对象作为case。枚举可以派上用场。更多:http://www.javabeat.net/2009/02/how-to-use-enum-in-switch/

2)再次实现单例设计模式- Enum,来拯救。这里的用法:在Java中使用Enum作为单例的最佳方法是什么?

为什么要使用编程语言的特性?我们有语言的原因是

程序员以计算机可以使用的形式有效而正确地表达算法。 维护人员理解他人编写的算法并正确地进行更改。

枚举提高了正确性和可读性的可能性,而无需编写大量的样板文件。如果你愿意写样板文件,那么你可以“模拟”枚举:

public class Color {
    private Color() {} // Prevent others from making colors.
    public static final Color RED = new Color();
    public static final Color AMBER = new Color();
    public static final Color GREEN = new Color();
}

现在你可以这样写:

Color trafficLightColor = Color.RED;

上面的样板具有与

public enum Color { RED, AMBER, GREEN };

两者都提供来自编译器的相同级别的检查帮助。样板文件只是更多的输入。但是节省大量的输入使程序员更有效率(见1),所以这是一个值得的特性。

至少还有一个原因是值得的:

Switch语句

上面的静态最终枚举模拟没有给你的一件事是良好的开关情况。对于枚举类型,Java开关使用其变量的类型来推断枚举情况的范围,因此对于上面的枚举Color,你只需要说:

Color color = ... ;
switch (color) {
    case RED:
        ...
        break;
}

注意它不是颜色。箱子里有红色。如果你不使用enum,使用switch的命名量的唯一方法是:

public Class Color {
    public static final int RED = 0;
    public static final int AMBER = 1;
    public static final int GREEN = 2;
}

但是现在保存颜色的变量必须是int类型。漂亮的编译器检查枚举和静态最终模拟消失了。不快乐。

折衷的方法是在模拟中使用标量值成员:

public class Color {
    public static final int RED_TAG = 1;
    public static final int AMBER_TAG = 2;
    public static final int GREEN_TAG = 3;

    public final int tag;

    private Color(int tag) { this.tag = tag; } 
    public static final Color RED = new Color(RED_TAG);
    public static final Color AMBER = new Color(AMBER_TAG);
    public static final Color GREEN = new Color(GREEN_TAG);
}

Now:

Color color = ... ;
switch (color.tag) {
    case Color.RED_TAG:
        ...
        break;
}

但请注意,更多的是样板文件!

使用枚举作为单例

从上面的样板,你可以看到为什么枚举提供了一种实现单例的方法。而不是写:

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

    // all the methods and instance data for the class here
}

然后访问它

SingletonClass.INSTANCE

我们可以说

public enum SingletonClass {
    INSTANCE;

    // all the methods and instance data for the class here
}

which gives us the same thing. We can get away with this because Java enums are implemented as full classes with only a little syntactic sugar sprinkled over the top. This is again less boilerplate, but it's non-obvious unless the idiom is familiar to you. I also dislike the fact that you get the various enum functions even though they don't make much sense for the singleton: ord and values, etc. (There's actually a trickier simulation where Color extends Integer that will work with switch, but it's so tricky that it even more clearly shows why enum is a better idea.)

线程安全

只有在没有锁定的情况下惰性地创建单例时,线程安全才会成为潜在的问题。

public class SingletonClass {
    private static SingletonClass INSTANCE;
    private SingletonClass() {}
    public SingletonClass getInstance() {
        if (INSTANCE == null) INSTANCE = new SingletonClass();
        return INSTANCE;
    }

    // all the methods and instance data for the class here
}

如果许多线程同时调用getInstance,而INSTANCE仍然为空,则可以创建任意数量的实例。这很糟糕。唯一的解决方案是添加同步访问来保护变量INSTANCE。

然而,上面的静态最终代码没有这个问题。它在类加载时急切地创建实例。类加载是同步的。

enum单例实际上是惰性的,因为它直到第一次使用才初始化。Java初始化也是同步的,因此多个线程不能初始化instance的多个实例。你得到了一个惰性初始化的单例,只有很少的代码。唯一的缺点是语法相当模糊。您需要了解习惯用法或彻底理解类加载和初始化是如何工作的,才能了解发生了什么。

在我的经验中,我看到Enum的使用有时会导致系统很难改变。如果您正在为一组频繁更改的特定于域的值使用Enum,并且它有许多依赖于它的其他类和组件,那么您可能要考虑不使用Enum。

例如,一个交易系统使用Enum进行市场/交易所。市场有很多,几乎可以肯定的是,会有很多子系统需要进入这个市场列表。每当你想要一个新的市场添加到你的系统中,或者如果你想要删除一个市场,有可能太阳下的一切都将不得不重建和释放。

A better example would be something like a product category type. Let's say your software manages inventory for a department store. There are a lot of product categories, and many reasons why this list of categories could change. Managers may want to stock a new product line, get rid of other product lines, and possibly reorganize the categories from time to time. If you have to rebuild and redeploy all of your systems simply because users want to add a product category, then you've taken something that should be simple and fast (adding a category) and made it very difficult and slow.

总之,如果您所表示的数据随着时间的推移是非常静态的,并且有有限数量的依赖项,则枚举是很好的选择。但是如果数据变化很大并且有很多依赖关系,那么就需要一些在编译时不检查的动态的东西(比如数据库表)。