一,什么是枚举
在Java中,枚举(Enumeration)是一种特殊的数据类型,它允许我们定义一个固定数量的常量集合。枚举类型在Java中是通过关键字enum
来定义的。每个枚举常量都是枚举类型的实例,它们在枚举类型中以逗号分隔。例如:
public enum Color {
RED,//0
BLUE,//1
BLACK,//2
WHITE;//3
}
注:枚举类型默认值(ordinal)从0开始,往后依次递增1,如果我们自己赋值,例如 BLUE = 4,前面的RED还是0,那么后面的BLAK就会默认赋值为5,往后依次递增。
但是我们明明可以使用 static final 来定义一个常量,为什么还要再定义enum类型呢?那是因为使用enum更加安全,例如 public static final int RED = 1,我们认为 RED = 1,也可以认为 1 = RED,但是 1也有可能只是一个整数类型,我们使用这种方法无法分辨,而使用枚举enum就可以避免这种情况,因为RED变成枚举类型了。
二,枚举的使用
2.1 switch语句
枚举类型可以使用switch语句:
public enum Color {
RED,
BLUE,
BLACK,
WHITE;
public static void main(String[] args) {
Color color1 = Color.BLUE;
switch (color1){
case RED:
System.out.println("red");
break;
case BLUE:
System.out.println("blue");
break;
case BLACK:
System.out.println("black");
break;
case WHITE:
System.out.println("white");
break;
}
}
}
2.2 enum常用方法
方法 | 说明 |
values() | 以数组形式返回枚举类型的所有成员 |
ordinal() | 获取枚举成员的索引位置,类似于下标,与上面的ordinal不同 |
valueOf() | 将普通字符串转换成枚举实例 |
compareTo() | 比较两个枚举成员在定义时的顺序 |
public enum Color {
RED,
BLUE,
BLACK,
WHITE;
public static void main(String[] args) {
Color[] d = Color.values();
for(Color x : d){
System.out.println(x + " " + x.ordinal());
}
Color a = Color.valueOf("RED");
System.out.println(a + " " + a.ordinal);
//注意ordinal()与ordinal是不一样,前一个是类似于下标,后一个是RED代表的值
System.out.println(RED.compareTo(BLACK));
}
}
2.3 enum的构造方法
enum的构造方法默认是私有的,也就是说 我们要添加enum的构造方法也必须是私有的!!
源码中enum只有一个构造方法:
我们自己提供的构造方法:
public enum Color {
//因为我们自己提供了构造方法,所以就没有默认构造方法,要添加枚举常量必须如下:
RED(0,"RED"),
BLUE(1,"BLUE"),
BLACK(2,"BLACK"),
WHITE(3,"WHITE");
int ordinal;
String color;
private Color(int ordinal, String color){//构造方法必须是私有的!!!
this.color = color;
this.ordinal = ordinal;
}
}
2.4 枚举方法的来源
我们上面提到了许多枚举的方法,但是我们创建的枚举类却没有继承任何类,那么这些方法是从哪里来的呢?
我们的第一想法肯定是Object类,毕竟Object类是所有类的父类并且是默认继承的,但是事实上却并非如此,因为我们上面提到了compareTo方法,但是Object类却没有实现comparable接口,所以肯定不是继承了Object类。
实际上Java中的枚举类型(Enum)是一个特殊的类,它隐式地继承了java.lang.Enum
类,我们上面提到的方法基本来源于它,但是有一个例外,那就是values()方法,那么这个values()究竟是从哪里冒出来的呢?
实际上,values()
方法是由编译器自动生成并添加到每个枚举类型中的。它返回一个包含枚举类型中所有枚举值的数组。编译器会自动为每个枚举类型添加以下静态方法:
public static T[] values();
其中,T
是枚举类型本身。由于这个方法是在编译时自动生成的,因此它不能在Enum
类中定义,而是作为每个枚举类型的一个静态方法存在。
三,枚举与反射
讲了以上内容后,我想问一个问题,我们能不能通过上一篇博客讲的反射来获得枚举的实例呢?理论上可以,接下来我们来试一试:
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public enum Color {
RED(0,"RED"),
BLUE(1,"BLUE"),
BLACK(2,"BLACK"),
WHITE(3,"WHITE");
int ordinal;
String color;
private Color(int ordinal, String color){//构造方法必须是私有的!!!
this.color = color;
this.ordinal = ordinal;
}
}
class Demo{
public static void main(String[] args) {
try {
Class<?> a = Class.forName("Color");
Constructor<?> constructor
= a.getDeclaredConstructor(int.class,String.class);
constructor.setAccessible(true);
Color color = (Color) constructor.newInstance(5,"YELLOW");
System.out.println(color);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
但是运行时却报了这样的错误:
可是我们提供的构造方法确实只有两个参数,为什么会显示没有对应的构造方法呢?还记得上文说我们的枚举类型都是默认继承 java.lang.Enum的,既然继承了父类并且提供了构造方法,我们的子类必须先帮助父类进行构造,那我们在构造方法中添加 super()就可以了吗?不是的,实际上是因为Java在我们提供的枚举构造方法参数的前面自动添加了两个参数:
也就是说实际上我们的提供的构造方法有四个参数,对原方法进行修改:
public static void main(String[] args) {
Class<?> a;
try {
a = Class.forName("Color");
Constructor<?> constructor
= a.getDeclaredConstructor(String.class, int.class, int.class, String.class);//修改1
constructor.setAccessible(true);
Color color = (Color) constructor.newInstance("lili",666,5,"YELLOW");//修改2
System.out.println(color);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
但是它又报了如下错误:
它说我们的 newInstance() 报错,我们来看看newInstance()的源代码:
它说我们的枚举类型不能使用反射!!!!