枚举在Java 1.5中作为一种新类型引入,其字段由一组固定的常量组成。例如,我们可以将方向创建为Java枚举,其固定字段为EAST、WEST、NORTH和SOUTH。
Java枚举
在本教程中,我们将学习如何创建枚举。我们还将探讨在Java中使用枚举的好处以及枚举类型的特性。我们还将学习如何使用Java枚举
valueOf
、枚举values
、EnumSet
和EnumMap
,并附有示例。
Java枚举示例
Java enum关键字用于创建枚举类型。让我们来看一个Java枚举示例程序。
package com.journaldev.enums;
public enum ThreadStates {
START,
RUNNING,
WAITING,
DEAD;
}
在上面的示例中,ThreadStates是一个具有固定常量字段START、RUNNING、WAITING和DEAD的枚举。
Java枚举 vs 常量
现在让我们看看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;
}
现在让我们看看在Java程序中如何使用枚举和常量:
/**
* This method shows the benefit of using Enum over Constants
*/
private static void benefitsOfEnumOverConstants() {
// 枚举值是固定的
simpleEnumExample(ThreadStates.START);
simpleEnumExample(ThreadStates.WAITING);
simpleEnumExample(ThreadStates.RUNNING);
simpleEnumExample(ThreadStates.DEAD);
simpleEnumExample(null);
simpleConstantsExample(1);
simpleConstantsExample(2);
simpleConstantsExample(3);
simpleConstantsExample(4);
// 我们可以传递任何 int 常量
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 枚举的更多特性。
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();
// 枚举构造函数应始终是私有的。
private ThreadStatesEnum(int i){
priority = i;
}
// 枚举可以有方法
public int getPriority(){
return this.priority;
}
public void setPriority(int p){
this.priority = p;
}
// 枚举可以重写函数
@Override
public String toString(){
return "Default ThreadStatesConstructors implementation. Priority="+getPriority();
}
@Override
public void close() throws IOException {
System.out.println("Close of Enum");
}
}
Java 枚举重要要点
以下是 Java 中枚举的一些重要要点。
- 所有的Java枚举隐式地扩展了
java.lang.Enum
类,该类扩展了Object类并实现了Serializable和Comparable接口。因此我们不能在枚举中扩展任何类。 - 由于enum是一个关键字,我们不能以它结束包名,例如
com.journaldev.enum
不是一个有效的包名。 - 枚举可以实现接口。就像上面的枚举示例中,它实现了Closeable接口。
- 枚举的构造函数始终是私有的。
- 我们不能使用new运算符创建枚举的实例。
- 我们可以在Java枚举中声明抽象方法,然后所有的枚举字段都必须实现该抽象方法。在上面的示例中,getDetail()是抽象方法,所有的枚举字段都实现了它。
- 我们可以在枚举中定义方法,枚举字段也可以重写它们。例如,toString()方法在枚举中被定义,枚举字段START对其进行了重写。
- Java枚举字段有命名空间,我们只能使用类名加上枚举字段的方式来使用它,例如
ThreadStates.START
- 枚举可以在switch语句中使用,在本教程的后面部分我们将看到它的实际应用。
- 我们可以在不影响任何现有功能的情况下扩展现有的枚举。例如,我们可以在ThreadStates枚举中添加一个新字段NEW,而不影响任何现有功能。
- 由于枚举字段是常量,Java最佳实践是使用大写字母和下划线表示它们的名称。例如EAST、WEST、EAST_DIRECTION等。
- 枚举常量隐式地是静态和final的
- 枚举常量是final的,但它们的变量仍然可以更改。例如,我们可以使用
setPriority()
方法来更改枚举常量的优先级。我们将在下面的示例中看到它的用法。 - 由于枚举常量是final的,我们可以安全地使用“==”和equals()方法进行比较。两者将得到相同的结果。
Java EnumSet、EnumMap、valueOf()
现在我们知道了枚举的大多数特性,让我们看一下Java枚举示例程序。然后我们将学习枚举的一些更多特性。
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();
}
}
在解释枚举的其他重要特性之前,让我们看一下上面程序的输出。
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)
通过字符串创建枚举对象。如果指定的枚举类型没有具有指定名称的常量,或者指定的类对象不代表枚举类型,则会抛出IllegalArgumentException
。如果任何参数为null,也会抛出NullPointerException
。usingEnumValues()
方法展示了values()方法的用法,该方法按照它们声明的顺序返回包含枚举所有值的数组。请注意,这个方法由java编译器为每个枚举自动生成。在java.util.Enum
类中找不到values()的实现。usingEnumInSwitch()
方法展示了如何在switch case中使用枚举常量。usingEnumMap()
方法展示了java.util.EnumMap的用法,它是Java 1.5集合框架中引入的。EnumMap
是用于枚举类型键的Map实现。枚举映射中的所有键必须来自于单个枚举类型,在创建映射时指定,明确或隐含。我们不能将null用作EnumMap的键,并且EnumMap不是同步的。usingEnumSet()
方法展示了如何使用 java.util.EnumSet,它是用于枚举类型的 Set 实现。枚举集中的所有元素必须来自于指定的单个枚举类型,该类型在创建集时明确或隐式地指定。EnumSet 不是同步的,且不允许 null 元素。它还提供了一些有用的方法,如copyOf(Collection<E> c)
、of(E first, E... rest)
和complementOf(EnumSet<E> s)
。
您可以从我们的 GitHub 仓库 中查看所有示例。
参考资料:Oracle 文档
Source:
https://www.digitalocean.com/community/tutorials/java-enum