枚举是在 Java 1.5 中作为一种新类型引入的,它的字段由一组固定的常量组成。例如,我们可以将方向创建为 Java 枚举,其中固定字段为 EAST、WEST、NORTH 和 SOUTH。
Java 枚举
在本教程中,我们将学习如何创建 Enum。我们还将研究在 java 中使用枚举的好处和枚举类型的特性。我们还将学习使用Java枚举valueOf
,枚举values
,EnumSet
并EnumMap
结合实例。
Java 枚举示例
Java enum关键字用于创建枚举类型。我们来看看java enum 示例程序。
package com.journaldev.enums;
public enum ThreadStates {
START,
RUNNING,
WAITING,
DEAD;
}
在上面的示例中,ThreadStates 是具有固定常量字段 START、RUNNING、WAITING 和 DEAD 的枚举。
Java 枚举与常量
现在让我们看看 java 枚举如何比 Java 类中的普通常量字段更好。
让我们在 java 中创建一个类似的常量类。
package com.journaldev.enums;
public class ThreadStatesConstant {
public static final int START = 1;
public static final int WAITING = 2;
public static final int RUNNING = 3;
public static final int DEAD = 4;
}
现在让我们看看 enum 和常量是如何在 java 程序中使用的:
/**
* This method shows the benefit of using Enum over Constants
*/
private static void benefitsOfEnumOverConstants() {
//Enum values are fixed
simpleEnumExample(ThreadStates.START);
simpleEnumExample(ThreadStates.WAITING);
simpleEnumExample(ThreadStates.RUNNING);
simpleEnumExample(ThreadStates.DEAD);
simpleEnumExample(null);
simpleConstantsExample(1);
simpleConstantsExample(2);
simpleConstantsExample(3);
simpleConstantsExample(4);
//we can pass any int constant
simpleConstantsExample(5);
}
private static void simpleEnumExample(ThreadStates th) {
if(th == ThreadStates.START) System.out.println("Thread started");
else if (th == ThreadStates.WAITING) System.out.println("Thread is waiting");
else if (th == ThreadStates.RUNNING) System.out.println("Thread is running");
else System.out.println("Thread is dead");
}
private static void simpleConstantsExample(int i) {
if(i == ThreadStatesConstant.START) System.out.println("Thread started");
else if (i == ThreadStatesConstant.WAITING) System.out.println("Thread is waiting");
else if (i == ThreadStatesConstant.RUNNING) System.out.println("Thread is running");
else System.out.println("Thread is dead");
}
如果我们看看上面的例子,我们会发现使用枚举解决的常量有两个风险。
- 我们可以将任何 int 常量传递给该
simpleConstantsExample
方法,但我们只能将固定值传递给 simpleEnumExample,因此它提供了类型安全性。 - 我们可以在
ThreadStatesConstant
类中更改 int 常量值,但上面的程序不会抛出任何异常。我们的程序可能无法按预期工作,但如果我们更改枚举常量,我们将收到编译时错误,从而消除了运行时问题的任何可能性。
Java 枚举方法
现在让我们通过一个例子来看看 java enum 的更多特性。
package com.journaldev.enums;
import java.io.Closeable;
import java.io.IOException;
/**
* This Enum example shows all the things we can do with Enum types
*
*/
public enum ThreadStatesEnum implements Closeable{
START(1){
@Override
public String toString(){
return "START implementation. Priority="+getPriority();
}
@Override
public String getDetail() {
return "START";
}
},
RUNNING(2){
@Override
public String getDetail() {
return "RUNNING";
}
},
WAITING(3){
@Override
public String getDetail() {
return "WAITING";
}
},
DEAD(4){
@Override
public String getDetail() {
return "DEAD";
}
};
private int priority;
public abstract String getDetail();
//Enum constructors should always be private.
private ThreadStatesEnum(int i){
priority = i;
}
//Enum can have methods
public int getPriority(){
return this.priority;
}
public void setPriority(int p){
this.priority = p;
}
//Enum can override functions
@Override
public String toString(){
return "Default ThreadStatesConstructors implementation. Priority="+getPriority();
}
@Override
public void close() throws IOException {
System.out.println("Close of Enum");
}
}
Java 枚举要点
下面是 Java 中 Enum 的一些要点。
- 所有 java 枚举都隐式扩展
java.lang.Enum
了扩展 Object 类并实现Serializable和Comparable接口的类。所以我们不能在 enum 中扩展任何类。 - 由于 enum 是一个关键字,我们不能用它来结束包名,例如
com.journaldev.enum
不是一个有效的包名。 - Enum 可以实现接口。如上面的枚举示例,它正在实现
Closeable
接口。 - 枚举构造函数始终是私有的。
- 我们无法使用 new 运算符创建枚举实例。
- 我们可以在java枚举中声明抽象方法,那么所有的枚举字段都必须实现抽象方法。上面的例子
getDetail()
是抽象方法,所有的枚举字段都实现了它。 - 我们可以在枚举中定义一个方法,枚举字段也可以覆盖它们。例如,
toString()
方法是在 enum 中定义的,而 enum 字段 START 已经覆盖了它。 - Java 枚举字段具有命名空间,我们只能使用具有类名的枚举字段,例如
ThreadStates.START
- 枚举可以用在switch 语句中,我们将在本教程的后面部分看到它的实际作用。
- 我们可以在不破坏任何现有功能的情况下扩展现有枚举。例如,我们可以在 ThreadStates 枚举中添加一个新字段 NEW 而不影响任何现有功能。
- 由于枚举字段是常量,java 最佳实践是用大写字母和下划线表示空格。例如 EAST、WEST、EAST_DIRECTION 等。
- 枚举常量是隐式静态和最终的
- 枚举常量是最终的,但它的变量仍然可以改变。例如,我们可以使用
setPriority()
方法来改变枚举常量的优先级。我们将在下面的示例中看到它的使用。 - 由于枚举常量是最终的,我们可以使用“==”和 equals() 方法安全地比较它们。两者都会有相同的结果。
Java EnumSet、EnumMap、valueOf()
现在我们知道了 Enum 的大部分功能,让我们来看看 Java Enum 示例程序。然后我们将学习枚举的更多功能。
package com.journaldev.enums;
import java.io.IOException;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.Set;
public class JavaEnumExamples {
public static void main(String[] args) throws IOException {
usingEnumMethods();
usingEnumValueOf();
usingEnumValues();
usingEnumInSwitch(ThreadStatesEnum.START);
usingEnumInSwitch(ThreadStatesEnum.DEAD);
usingEnumMap();
usingEnumSet();
}
private static void usingEnumSet() {
EnumSet enumSet = EnumSet.allOf(ThreadStatesEnum.class);
for(ThreadStatesEnum tsenum : enumSet){
System.out.println("Using EnumSet, priority = "+tsenum.getPriority());
}
}
private static void usingEnumMap() {
EnumMap<ThreadStatesEnum, String> enumMap = new EnumMap<ThreadStatesEnum,String>(ThreadStatesEnum.class);
enumMap.put(ThreadStatesEnum.START, "Thread is started");
enumMap.put(ThreadStatesEnum.RUNNING, "Thread is running");
enumMap.put(ThreadStatesEnum.WAITING, "Thread is waiting");
enumMap.put(ThreadStatesEnum.DEAD, "Thread is dead");
Set keySet = enumMap.keySet();
for(ThreadStatesEnum key : keySet){
System.out.println("key="+key.toString()+":: value="+enumMap.get(key));
}
}
private static void usingEnumInSwitch(ThreadStatesEnum th) {
switch (th){
case START:
System.out.println("START thread");
break;
case WAITING:
System.out.println("WAITING thread");
break;
case RUNNING:
System.out.println("RUNNING thread");
break;
case DEAD:
System.out.println("DEAD thread");
}
}
private static void usingEnumValues() {
ThreadStatesEnum[] thArray = ThreadStatesEnum.values();
for(ThreadStatesEnum th : thArray){
System.out.println(th.toString() + "::priority="+th.getPriority());
}
}
private static void usingEnumValueOf() {
ThreadStatesEnum th = Enum.valueOf(ThreadStatesEnum.class, "START");
System.out.println("th priority="+th.getPriority());
}
private static void usingEnumMethods() throws IOException {
ThreadStatesEnum thc = ThreadStatesEnum.DEAD;
System.out.println("priority is:"+thc.getPriority());
thc = ThreadStatesEnum.DEAD;
System.out.println("Using overriden method."+thc.toString());
thc = ThreadStatesEnum.START;
System.out.println("Using overriden method."+thc.toString());
thc.setPriority(10);
System.out.println("Enum Constant variable changed priority value="+thc.getPriority());
thc.close();
}
}
在解释 enum 的其他重要特性之前,让我们看看上面程序的输出。
priority is:4
Using overriden method.Default ThreadStatesConstructors implementation. Priority=4
Using overriden method.START implementation. Priority=1
Enum Constant variable changed priority value=10
Close of Enum
th priority=10
START implementation. Priority=10::priority=10
Default ThreadStatesConstructors implementation. Priority=2::priority=2
Default ThreadStatesConstructors implementation. Priority=3::priority=3
Default ThreadStatesConstructors implementation. Priority=4::priority=4
START thread
DEAD thread
key=START:: value=Thread is started
key=RUNNING:: value=Thread is running
key=WAITING:: value=Thread is waiting
key=DEAD:: value=Thread is dead
Using EnumSet, priority = 10
Using EnumSet, priority = 2
Using EnumSet, priority = 3
Using EnumSet, priority = 4
要点
- 这些
usingEnumMethods()
方法展示了如何创建枚举对象以及我们如何使用它的方法。它还显示了使用setPriority(int i)
方法来更改枚举的变量。 usingEnumValueOf()
显示了java.util.Enum
valueOf(enumType, name)
我们可以通过它从 String 创建枚举对象的用法。IllegalArgumentException
如果指定的枚举类型没有指定名称的常量,或者指定的类对象不代表枚举类型,则抛出。NullPointerException
如果任何参数为空,它也会抛出。usingEnumValues()
方法显示了values()方法的用法,该方法返回一个数组,该数组包含按声明顺序排列的枚举的所有值。请注意,此方法是由 java 编译器为每个枚举自动生成的。你不会在java.util.Enum
课堂上找到 values() 的实现。- 该
usingEnumInSwitch()
方法展示了如何在 switch case 中使用枚举常量。 usingEnumMap()
方法展示了java.util.EnumMap 的使用,它是在 Java 1.5 Collections Framework 中引入的。EnumMap
是用于枚举类型键的 Map 实现。枚举映射中的所有键都必须来自创建映射时显式或隐式指定的单个枚举类型。我们不能使用 null 作为 EnumMap 的键,并且 EnumMap 不同步。usingEnumSet()
方法显示了java.util.EnumSet 的使用,它是用于枚举类型的 Set 实现。枚举集合中的所有元素都必须来自创建集合时显式或隐式指定的单个枚举类型。EnumSet 不同步,并且不允许空元素。它还提供了一些有用的方法,例如copyOf(Collection<E> c)
、of(E first, E... rest)
和complementOf(EnumSet<E> s)
。