Java枚举全面讲解

a 夏天 提交于 2020-01-29 13:46:08

枚举是什么

枚举是Java1.5引入的新特性。通过关键字enum来定义枚举,枚举是一种特殊的类,它和普通的类一样可以定义构造器,成员变量和方法,也能实现一个或多个接口,但枚举类不能继承其他类。

创建枚举类

创建枚举必须使用enum关键字

public enum Color {
    RED,
    GREEN,
    YELLOW,
    BLACK;
}

使用枚举

public class EnumMethodTest {
    public static void main(String[] args) {
        System.out.println(Color.GREEN);
        System.out.println(Color.YELLOW);
        System.out.println(Color.RED);
        System.out.println(Color.BLACK);
        //运行结果
        //GREEN
        //YELLOW
        //RED
        //BLACK
    }
}

这样只是知道枚举简单的使用方法,不能看出枚举的特点和枚举的具体实现。
下面通过XJad工具反编译Color枚举类

public final class Color extends Enum
{
	
	//在enum中声明的变量都对应一个枚举对象,而且都是不可变的常量
	public static final Color RED;
	public static final Color GREEN;
	public static final Color YELLOW;
	public static final Color BLACK;
	private static final Color $VALUES[];
	
	//为了避免 返回的数组被修改,而引起内部values值的变化,返回的是数组的副本
	public static Color[] values()
	{
		return (Color[])$VALUES.clone();
	}
	
	//按名称获取枚举对象
	public static Color valueOf(String name)
	{
		return (Color)Enum.valueOf(cn/zwq/Color, name);
	}
	
	//私有的构造器
	private Color(String name, int ordinal)
	{
		super(name, ordinal);
	}
	
	//静态代码块初始化声明的枚举对象
	static 
	{
		RED = new Color("RED", 0);
		GREEN = new Color("GREEN", 1);
		YELLOW = new Color("YELLOW", 2);
		BLACK = new Color("BLACK", 3);
		$VALUES = (new Color[] {
			RED, GREEN, YELLOW, BLACK
		});
	}
}

从反编译的类中,我们使用enum关键字编写的枚举类,在编译阶段编译器会自动帮我们生成一个真正在JVM中运行的代码。该类继承java.lang.Enum类。在枚举类中声明的变量都会生成一个Color对象,而且都是不可变的常量对象,它们是在静态域中进行初始化,而静态域是在类加载阶段进行初始化,所以枚举对象是线程安全的,由JVM来保证。

Enum成员变量和方法分析

在这里插入图片描述
Enum类实现了Comparable接口,表明它是支持排序的,可以通过Collections.sort 进行自动排序。
重写了public final int compareTo(E o)方法,方法定义为final,且其排序依赖于ordinal字段(final类型),说明它只能根据ordinal排序,排序规则不可变。

private final int ordinal;

public final int compareTo(E o) {
        Enum<?> other = (Enum<?>)o;
        Enum<E> self = this;
        if (self.getClass() != other.getClass() && // optimization
            self.getDeclaringClass() != other.getDeclaringClass())
            throw new ClassCastException();
        return self.ordinal - other.ordinal;
    }

ordinal:表示枚举的顺序,从Color类中可以看出,它是从0开始按自然数顺序增长,且其值是final类型,外部无法更改。对于ordinal()方法,官方建议尽量不要使用它,它主要是提供给EnumMap,EnumSet使用的。

/**
     * Returns the ordinal of this enumeration constant (its position
     * in its enum declaration, where the initial constant is assigned
     * an ordinal of zero).
     *
     * Most programmers will have no use for this method.  It is
     * designed for use by sophisticated enum-based data structures, such
     * as {@link java.util.EnumSet} and {@link java.util.EnumMap}.
     *
     * @return the ordinal of this enumeration constant
     */
    public final int ordinal() {
        return ordinal;
    }

name:表示枚举类的名字,从Color类的构造函数可以看出,它的值就是我们定义的实例的名称。
我们在例子中之所以能打印出实例名称,是因为它的toString()方法直接返回了name属性。

/**
     * Returns the name of this enum constant, as contained in the
     * declaration.  This method may be overridden, though it typically
     * isn't necessary or desirable.  An enum type should override this
     * method when a more "programmer-friendly" string form exists.
     *
     * @return the name of this enum constant
     */
    public String toString() {
        return name;
    }

equals():从其实现来看,我们程序中使用==或者equals()来判断两个枚举相等都是一样的。

public final boolean equals(Object other) {
	return this==other;
}

getDeclaringClass()::返回枚举声明的Class对象。

每一个枚举类型及其定义的枚举对象在JVM中都是唯一的

这句话的意思就是枚举类型它拥有的实例在定义枚举的时候就已经确定,不能通过其他手段创建,且枚举变量在JVM有且只有一个对应的实例。

通过以下的方法来确保上面所说的效果

1、类加载时创建枚举实例,保证线程安全

从反编译的Color类中看出,Color实例是在静态域中创建,在类加载时初始化,JVM保证线程安全,这样就能确保Color对象不会因为并发同时请求而错误的创建多个实例。

2、对序列化进行特殊处理,防止反序列化时创建新的对象

我们知道一旦实现了Serializable接口之后,反序列化时每次调用readObject()方法返回的都是一个新创建出来的对象。
而枚举则不同,在序列化的时候Java仅仅是将枚举对象的name属性输出到结果中,反序列化的时候则是通过Enum的valueOf()方法来根据名字查找枚举对象。

3、私有构造函数,无法正常的new出对象

//私有的构造器
private Color(String name, int ordinal)
{
	super(name, ordinal);
}

4、无法通过clone()方法克隆对象

/**
 * Throws CloneNotSupportedException.  This guarantees that enums
 * are never cloned, which is necessary to preserve their "singleton"
 * status.
 *
 * @return (never returns)
 */
protected final Object clone() throws CloneNotSupportedException {
    throw new CloneNotSupportedException();
}

5、无法通过反射的方式创建枚举对象

枚举类型,在JVM层面禁止了通过反射构造枚举实例的行为,如果尝试通过反射创建,将会报
Cannot reflectively create enum objects

public static void main(String[] args) throws Exception {
    Class color = Class.forName("cn.zwq.Color");
    Constructor constructor = color.getDeclaredConstructor(String.class, int.class);
    constructor.setAccessible(true);
    Enum blue = (Enum) constructor.newInstance("BLUE", 0);
}

异常信息

Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects

枚举的使用

枚举常量
上面的Color枚举类,就是典型的枚举常量

枚举总结

在这里插入图片描述

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!