java 中的枚举类型本质是默认继承于 java.lang.Enum 的类
常用方法
方法签名 | 描述 |
values() | 以数组形式返回枚举类型的所有成员 |
ordinal() | 获取枚举成员的索引位置(编号、序号) |
valueOf() | 将普通字符串转换为枚举实例 |
compareTo() | 比较两个枚举成员在定义是的顺序 |
枚举示例
颜色枚举类
注意:
枚举的构造方法默认是私有的,其他类无法调用
枚举类会列出所有的可能枚举值,不需要再进行扩展与继承,且枚举值是不可变的。因此枚举类无需被继承/扩展,如果允许继承/拓展,可能会破坏枚举值的不可变性
public enum ColorEnum {
// 带属性枚举成员
RED("红色", 1),
GREEN("绿色", 2),
BLUE("蓝色", 3);
// 属性
private String color;
private int key;
// 枚举构造方法默认私有
private ColorEnum(String color, int key) {
this.color= color;
this.key = key;
}
// 展示枚举成员的属性
public void show() {
System.out.println(key + " " + color);
}
// 输出所有枚举成员名
public static void names() {
// 使用 for each 遍历 values 结果集
for (ColorEnum colorEnum : ColorEnum.values()) {
// 使用 name() 获取枚举成员名
System.out.print(colorEnum.name() + " ");
}
System.out.println();
}
// 输出所有枚举成员的 ordinal
public static void ordinals() {
// 使用 for each 遍历 values 结果集
for (ColorEnum colorEnum : ColorEnum.values()) {
// 使用 ordinal() 获取枚举成员的索引位置
System.out.print(colorEnum.ordinal() + " ");
}
System.out.println();
}
}
测试枚举类
调用我们自定义的静态方法
ColorEnum.ordinals();
ColorEnum.names();
使用 valueOf(枚举成员名) 获取枚举对象
ColorEnum red = ColorEnum.valueOf("RED");
ColorEnum green = ColorEnum.valueOf("GREEN");
ColorEnum blue = ColorEnum.valueOf("BLUE");
// 调用自定义的属性展示方法
red.show();
green.show();
blue.show();
使用 .枚举成员 的方式获取枚举对象
red = ColorEnum.RED;
green = ColorEnum.GREEN;
blue = ColorEnum.BLUE;
// 调用自定义的属性展示方法
red.show();
green.show();
blue.show();
使用 compareTo() 比较索引位置关系
System.out.println(
red.compareTo(red) + " " +
red.compareTo(green) + " " +
red.compareTo(blue)
);
枚举与反射
枚举类型的构造方法是私有的,那我们能不能通过反射去获取进而构造枚举实例呢?毕竟反射可以无视访问修饰符嘛,现在我们可以来尝试一下
Class colorEnumClass = Class.forName("ColorEnum");
Constructor colorEnumDeclaredConstructor =
colorEnumClass.getDeclaredConstructor(
String.class, int.class
);
ColorEnum colorEnum =
(ColorEnum) colorEnumDeclaredConstructor.newInstance(
"红色", 1
);
System.out.println(colorEnum);
结果显示为找不到 ColorEnum(java.lang.String, int) 的构造方法,但我们明明写了这样的构造方法
ColorEnum(String color, int key) {
this.color = color;
this.key = key;
}
这是应为枚举类型的构造方法会有带有两个默认传给父类的构造参数 name 和 ordinal
/**
* Sole constructor. Programmers cannot invoke this constructor.
* It is for use by code emitted by the compiler in response to
* enum type declarations.
*
* @param name - The name of this enum constant, which is the identifier
* used to declare it.
* @param ordinal - The ordinal of this enumeration constant (its position
* in the enum declaration, where the initial constant is assigned
* an ordinal of zero).
*/
protected Enum(String name, int ordinal) {
this.name = name;
this.ordinal = ordinal;
}
因此我们的构造方法应该是 ColorEnum(java.lang.String, int, java.lang.String, int)
Constructor colorEnumDeclaredConstructor =
colorEnumClass.getDeclaredConstructor(
String.class, int.class, String.class, int.class
);
ColorEnum colorEnum =
(ColorEnum) colorEnumDeclaredConstructor.newInstance(
"红色", 1, "RED", 0
);
这次 NoSuchMethodException 没有出现了,只是告诉我们 IllegalAccessException 非法访问异常,我们可以修改一下访问权限
Constructor colorEnumDeclaredConstructor =
colorEnumClass.getDeclaredConstructor(
String.class, int.class, String.class, int.class
);
// 修改访问权限为允许
colorEnumDeclaredConstructor.setAccessible(true);
ColorEnum colorEnum =
(ColorEnum) colorEnumDeclaredConstructor.newInstance(
"红色", 1, "RED", 0
);
结果还是不行,告诉我们不能用反射创建枚举对象。这是因为枚举的定义就不允许她新建实例,枚举类型的实例的有限的、固定的,不能随意创建新的实例。如果允许反射创建新的实例,这就会破坏枚举类型的不可变性和安全性。于是,Java 直接在 newInstance() 源码中,将枚举类型给过滤掉了
if ((clazz.getModifiers() & Modifier.ENUM) != 0) {
throw new IllegalArgumentException(
"Cannot reflectively create enum objects"
);
}
/**
* Uses the constructor represented by this {@code Constructor} object to
* create and initialize a new instance of the constructor's
* declaring class, with the specified initialization parameters.
* Individual parameters are automatically unwrapped to match
* primitive formal parameters, and both primitive and reference
* parameters are subject to method invocation conversions as necessary.
*
* <p>If the number of formal parameters required by the underlying constructor
* is 0, the supplied {@code initargs} array may be of length 0 or null.
*
* <p>If the constructor's declaring class is an inner class in a
* non-static context, the first argument to the constructor needs
* to be the enclosing instance; see section 15.9.3 of
* <cite>The Java™ Language Specification</cite>.
*
* <p>If the required access and argument checks succeed and the
* instantiation will proceed, the constructor's declaring class
* is initialized if it has not already been initialized.
*
* <p>If the constructor completes normally, returns the newly
* created and initialized instance.
*
* @param initargs array of objects to be passed as arguments to
* the constructor call; values of primitive types are wrapped in
* a wrapper object of the appropriate type (e.g. a {@code float}
* in a {@link java.lang.Float Float})
*
* @return a new object created by calling the constructor
* this object represents
*
* @exception IllegalAccessException if this {@code Constructor} object
* is enforcing Java language access control and the underlying
* constructor is inaccessible.
* @exception IllegalArgumentException if the number of actual
* and formal parameters differ; if an unwrapping
* conversion for primitive arguments fails; or if,
* after possible unwrapping, a parameter value
* cannot be converted to the corresponding formal
* parameter type by a method invocation conversion; if
* this constructor pertains to an enum type.
* @exception InstantiationException if the class that declares the
* underlying constructor represents an abstract class.
* @exception InvocationTargetException if the underlying constructor
* throws an exception.
* @exception ExceptionInInitializerError if the initialization provoked
* by this method fails.
*/
@CallerSensitive
public T newInstance(Object ... initargs)
throws InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException
{
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, null, modifiers);
}
}
if ((clazz.getModifiers() & Modifier.ENUM) != 0)
throw new IllegalArgumentException("Cannot reflectively create enum objects");
ConstructorAccessor ca = constructorAccessor; // read volatile
if (ca == null) {
ca = acquireConstructorAccessor();
}
@SuppressWarnings("unchecked")
T inst = (T) ca.newInstance(initargs);
return inst;
}