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

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

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


当前回答

Java允许将变量限制为只有几个预定义值之一——换句话说,枚举列表中的一个值。 使用枚举可以帮助减少代码中的bug。 下面是一个类外部枚举的例子:

enums coffeesize{BIG , HUGE , OVERWHELMING }; 
//This semicolon is optional.

这限制了咖啡的大小:大的,巨大的,或压倒性的变量。

其他回答

当一个变量(特别是一个方法参数)只能从一组可能的值中选取一个时,您应该始终使用枚举。例如类型常量(合约状态:“永久”,“临时”,“学徒”)或标志(“立即执行”,“延迟执行”)。

如果使用枚举而不是整数(或字符串代码),则增加了编译时检查,避免了传入无效常量的错误,并且记录了哪些值是可以合法使用的。

顺便说一句,过度使用枚举可能意味着你的方法做了太多的事情(通常有几个单独的方法会更好,而不是一个方法有几个标记来修改它所做的事情),但如果你必须使用标记或类型代码,枚举是最好的选择。

举个例子,哪个更好?

/** Counts number of foobangs.
 * @param type Type of foobangs to count. Can be 1=green foobangs,
 * 2=wrinkled foobangs, 3=sweet foobangs, 0=all types.
 * @return number of foobangs of type
 */
public int countFoobangs(int type)

/** Types of foobangs. */
public enum FB_TYPE {
 GREEN, WRINKLED, SWEET, 
 /** special type for all types combined */
 ALL;
}

/** Counts number of foobangs.
 * @param type Type of foobangs to count
 * @return number of foobangs of type
 */
public int countFoobangs(FB_TYPE type)

方法调用如下:

int sweetFoobangCount = countFoobangs(3);

然后就变成:

int sweetFoobangCount = countFoobangs(FB_TYPE.SWEET);

在第二个例子中,很明显哪些类型是允许的,文档和实现不能不同步,编译器可以强制执行这一点。 另外,无效调用

int sweetFoobangCount = countFoobangs(99);

已经不可能了。

枚举是什么

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

为什么使用enum

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

Note

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

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

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

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

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时,可以加强一定的刚性。它们提供了一种有趣的可能性,可以将信息传递给一个构造函数,就像在Oracle网站上的Planets示例中那样,而且,正如您所发现的,还允许以一种简单的方式创建一个单例模式。

例:Locale.setDefault(Locale.US)读起来比Locale.setDefault(1)更好,并强制使用IDE中显示的固定值集。分隔符,而不是所有整数。

Enum表示枚举,即逐个提到(一些事情)。

enum是一种包含固定常数集的数据类型。

OR

枚举就像一个类,在编译时有一组固定的实例。

例如:

public class EnumExample {
    interface SeasonInt {
        String seasonDuration();
    }

    private enum Season implements SeasonInt {
        // except the enum constants remaining code looks same as class
        // enum constants are implicitly public static final we have used all caps to specify them like Constants in Java
        WINTER(88, "DEC - FEB"), SPRING(92, "MAR - JUN"), SUMMER(91, "JUN - AUG"), FALL(90, "SEP - NOV");

        private int days;
        private String months;

        Season(int days, String months) { // note: constructor is by default private 
            this.days = days;
            this.months = months;
        }

        @Override
        public String seasonDuration() {
            return this+" -> "+this.days + "days,   " + this.months+" months";
        }

    }
    public static void main(String[] args) {
        System.out.println(Season.SPRING.seasonDuration());
        for (Season season : Season.values()){
            System.out.println(season.seasonDuration());
        }

    }
}

枚举的优点:

Enum提高了编译时检查的类型安全性,以避免在运行时出现错误。 枚举可以很容易地用于交换机 枚举可以遍历 枚举可以有字段、构造函数和方法 enum可以实现许多接口,但不能扩展任何类,因为它在内部扩展了enum类

更多的