你所不知的 java 枚举

java中的枚举在实现上非常简单,以下就是一个枚举的例子:

但是如果反编译FruitEnum.class,会发现编译器在背后默默的做了大量的工作,以下是反编译结果:

下面根据反编译结果说明枚举的几个特性。

  • 枚举的实际类型

说明枚举是类,并且是用 final 修饰的类,意味着枚举不能再被继承扩展。而我们声明枚举类时使用的 enum 只是一个关键字。

其次,所有的枚举都继承了一个基类 Enum ,该基类为枚举提供了一些通用方法。

  • 枚举的构造函数

在 FruitEnum 中,我们并没有定义构造函数,但在反编译的代码中,我们发现编译器自动帮我们添加了以下私有构造函数:

该私有构造函数只是简单调用了父类 Enum 的构造函数:

实际上,编译器只允许私有的枚举类构造函数,并且显示定义的枚举构造函数经编译器后,都会被添加 name 和 ordinal 2个参数,用以调用父类 Enum 的构造函数。

  • 枚举常量

再看反编译后的 FruitEnum 中的成员变量:

其中 FruitEnum APPLE对应我们定义的枚举常量 APPLE,而 FruitEnum $VALUES则是容纳所有枚举常量的数组。

这两个变量初始化操作如下:

可知,每个枚举常量都是定义它的类的对象,各枚举对象都是用 public static final修饰的,这也是枚举对象被称为枚举常量的原因。

其次,FruitEnum APPLE和 FruitEnum $VALUES都是在静态代码块中进行初始化,因此在 JVM 记载完成枚举类后,各枚举常量都已被

创建完毕。由于枚举类中只允许出现私有构造函数,固无法再实例化新的枚举对象。

  • 枚举类实例化

无法通过构造函数这种一般手段实例化枚举类,那么通过反射呢?

先尝试以下形式:

抛出了以下错误:

因为 Class.newInstance 内部是通过目标Class的 无参构造函数创建实例的,而上文已经说了 FruitEnum 的实际构造函数是:

固会找不到对应的构造函数,抛出异常。

换种反射方式,先获取正确的构造函数:

成功获取了 FruitEnum 的构造函数,然而还是抛出了异常:

在 JVM 层面禁止了通过反射构造枚举实例的行为。

然而除了反射外,还可以通过 Object.clone() 方法克隆一个已存在的对象。但是这种方式也是不行的,看 Enum 类中的 clone()方法:

Enum 类重写了 Obejct 的 clone()方法,只要尝试通过 clone()方法构造枚举对象,都会抛出异常,并且该方法被设置为不能再重写。

  • 单例

从编译器、JVM,再到 java 内部设计,层层把关,封死了实例化枚举类的这一企图。因此有一种作法是通过枚举的形式实现单例,下面给个示例:

  • 线程安全

    枚举常量都是 static 类型的,在枚举类加载完成后,会进行枚举常量的初始化,之后枚举类无法再实例化和修改。java 的类加载、初始化过程是线程安全的,因此创建一个 enum 是线程安全的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值