enum java 原理_java enum实现原理

一、分析自定义枚举类

普通的枚举类和抽象枚举类相似,故直接分析抽象枚举类。

1. 编写一个抽象枚举类

package org.example;

public enum Operator {

ADD ("+"){

@Override

public int calculate(int a, int b) {

return a + b;

}

},

SUB ("-"){

@Override

public int calculate(int a, int b) {

return a - b;

}

},

MUL ("*"){

@Override

public int calculate(int a, int b) {

return a * b;

}

},

DIV ("/"){

@Override

public int calculate(int a, int b) {

return a / b;

}

};

private String identifier;

Operator(String identifier){

this.identifier = identifier;

}

public abstract int calculate(int a, int b);

public String getIdentifier(){

return identifier;

}

}

2. 编译

使用命令javac Operator将.java文件编译成.class文件。

编译生成如下五个文件:

Operator.class

Operator$1.class

Operator$2.class

Operator$3.class

Operator$4.class

Operator.class 是Operator的编译结果,但是其余四个.class文件目前不明确来源。

3. 反编译

普通的枚举类的反编译结果使用final进行修饰。

3.1 查看Operator简单内容

通过命令javap Operator反编译Operator.class。

反编译结果:

public abstract class org.example.Operator extends java.lang.Enum {

public static final org.example.Operator ADD;

public static final org.example.Operator SUB;

public static final org.example.Operator MUL;

public static final org.example.Operator DIV;

public static org.example.Operator[] values();

public static org.example.Operator valueOf(java.lang.String);

public abstract int calculate(int, int);

public java.lang.String getIdentifier();

org.example.Operator(java.lang.String, int, java.lang.String, org.example.Operator$1);

static {};

}

反编译之后的结果多了values、valueOf以及一个static代码块,而且构造函数的参数和我们写的参数也不一致。但是有用信息太少,需要查看详细的编译信息,用来确定这些新增代码的用途。

3.2 查看Operator详细信息

通过命令javap -c -v Operator查看Operator.class反编译的详细信息。

具体的信息由于篇幅原因不展示具体的编译信息,只展示如下分析结果:

public abstract class org.example.Operator extends java.lang.Enum {

public static final org.example.Operator ADD;

public static final org.example.Operator SUB;

public static final org.example.Operator MUL;

public static final org.example.Operator DIV;

private static final org.example.Operator[] $VALUES;

private String identifier;

// 抽象方法,在子类中实例化

public abstract int calculate(int, int);

public java.lang.String getIdentifier(){

return identifier;

}

// 关于编译之后添加的name、ordinal可以在Enum中看出它们的用途

private org.example.Operator(String name, int ordinal, String identifier){

super(name, ordinal);

this.identifier = identifier;

}

// 看不懂该方法的作用

org.example.Operator(String name, int ordinal, String identifier, org.example.Operator$1 ){

this(name, ordinal, identifier);

}

public static org.example.Operator[] values(){

return (Operator[])$VALUES.clone();

}

// 根据名字返回对应的枚举常量,具体看Eunm类的分析

public static org.example.Operator valueOf(String name){

return (Operator)super.valueOf(Operator.class, name);

}

// 实例化枚举类型中定义的常量

static {

ADD = new Operator$1("ADD", 0, "+");

SUB = new Operator$2("SUB", 1, "-");

MUL = new Operator$3("MUL", 2, "*");

DIV = new Operator$4("DIV", 3, "/");

$VALUES = new Operator[4];

$VALUES[0] = ADD;

$VALUES[1] = SUB;

$VALUES[2] = MUL;

$VALUES[3] = DIV;

};

}

InnerClasses:

static #24; //class org/example/Operator$4

static #19; //class org/example/Operator$3

static #14; //class org/example/Operator$2

static #9; //class org/example/Operator$1

最后的InnerClasses部分解答了之前的疑惑:每一个枚举对象就是一个匿名内部类的实例。该实例在Operator类的static代码块中初始化。并且每个枚举类型内部都会维护一个$VALUES数组,用来保存内部所有的枚举实例。

3.3 查看Operator$1.class详细信息

使用javap -c -v Operator$1查看Operator$1.class反编译的详细信息。

其余的几个和Operator$1.class类似,不做多余解释。

同样只展示分析后的结果:

final class org.example.Operator$1 extends org.example.Operator{

// 构造函数传入了一个匿名子类的实例(实际上是`null`),搞不懂

org.example.Operator$1(String name, int ordinal, String identifier){

super(name, ordinal, identifier, null);

}

public int calculate(int a, int b){

return a + b;

}

}

很简单的匿名内部类的实现,单纯的实现了父类的抽象方法。

自定义枚举分析完毕,下来看一下Enum类。

二、分析Enum类源码

public abstract class Enum> implements Comparable, Serializable {

/**

* 枚举常量的名字,在本案例中是“ADD”、“SUB”、“MUL”、“DIV”。

* 大多数的程序应该在{@link #toString}中引用该字段,而不是直接访问该字段。

* 因此{@link #toString}需要返回对用户友好的字符串。

*/

private final String name;

public final String name() { return name; }

/**

* 枚举常量的定义的顺序(第一个常量的ordinal从0开始)。

* 大多数的程序中不应该使用该字段。该字段被一些基于枚举的数据结构所使用,例如

* {@link java.util.EnumSet} and {@link java.util.EnumMap}.

*/

private final int ordinal;

public final int ordinal() { return ordinal; }

/**

* 唯一的构造方法。程序不能执行该构造方法。它只能被编译器编生成的代码

* (ACC_SYNTHETIC,比如{@link org.example.Operator(String, int, String)})

* 所使用。

*

* 如果要是通过反射调用该构造方法,也会抛出异常。因为在反射API中对Class的类型做了判断。

*/

protected Enum(String name, int ordinal) {

this.name = name;

this.ordinal = ordinal;

}

/**

* 该方法应该被重写,并返回一些对用户友好的字符串。

*/

public String toString() { return name; }

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

public final int hashCode() { return super.hashCode(); }

/** enum classes cannot have finalize methods. */

protected final void finalize() { }

/**

* 该函数是通过ordinal字段来比较的,因此其返回结果和常量定义的顺序有关系。

* 并且枚举常量只能和相同类型的枚举常量进行比较。

*/

public final int compareTo(E o) {

Enum> other = (Enum>)o;

Enum self = this;

if (self.getClass() != other.getClass() && // optimization

self.getDeclaringClass() != other.getDeclaringClass())

throw new ClassCastException();

return self.ordinal - other.ordinal;

}

/**

* 返回该枚举类型正确的Class对象。

* 在当前案例中,每一个枚举的Class对象应该是内部类,比如“ADD”对象实际上是Class,

* 但这些是虚拟机内部的实现,用户想要看到的是Class。

*/

@SuppressWarnings("unchecked")

public final Class getDeclaringClass() {

Class> clazz = getClass();

Class> zuper = clazz.getSuperclass();

return (zuper == Enum.class) ? (Class)clazz : (Class)zuper;

}

/**

* 返回指定枚举类型中指定名字的枚举常量。该name在每个枚举类型中唯一。

*

* 该方法被每个枚举类型中隐式的{@code public static T valueOf(String)}

* 所使用。程序应该使用每个枚举类型中的{@code public static T valueOf(String)}。

*/

public static > T valueOf(Class enumType, String name) {

// 根据name从map中取该常量

T result = enumType.enumConstantDirectory().get(name);

if (result != null)

return result;

if (name == null)

throw new NullPointerException("Name is null");

throw new IllegalArgumentException(

"No enum constant " + enumType.getCanonicalName() + "." + name);

}

/**

* 该方法抛出一个异常,为了保证枚举常量“单例”的特性。

*/

protected final Object clone() throws CloneNotSupportedException {

throw new CloneNotSupportedException();

}

/**

* 防止反序列化攻击,为了保证枚举常量“单例”的特性。

*/

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {

throw new InvalidObjectException("can't deserialize enum");

}

private void readObjectNoData() throws ObjectStreamException {

throw new InvalidObjectException("can't deserialize enum");

}

}

三、收获

枚举如何保证单例性

new关键字:

枚举:直接在编译器层面进行了控制。

替代:可以通过构造方法私有化来实现。

继承:

枚举:在编译器层面做了控制,无法继承一个枚举类。

替代:普通类可以通过final关键字实现。抽象类则无法实现,因为我们没有办法控制一个类只被特定的几个类继承,但是可以通过其他方法间接实现该效果。

反射API:

枚举:在反射API中进行了控制。

替代:可以在单例对象的构造方法中进行判断来实现。

反序列化:

枚举:在反序列化用到的方法中进行了控制。

替代:可以通过重写反序列化方法实现。

关于反射API

反射API一般通过Class.newInstance来获取实例对象:

// java.lang.Class

@CallerSensitive

public T newInstance() throws InstantiationException, IllegalAccessException {

// ... 省略其他代码

try {

return tmpConstructor.newInstance((Object[])null);

} catch (InvocationTargetException e) {

Unsafe.getUnsafe().throwException(e.getTargetException());

return null;

}

}

该方法通过 tmpConstructor.newInstance((Object[])null);新建对象。继续跟踪:

// java.lang.reflect.Constructor

@CallerSensitive

public T newInstance(Object ... initargs) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {

// ... 省略其他代码

if ((clazz.getModifiers() & Modifier.ENUM) != 0)

throw new IllegalArgumentException("Cannot reflectively create enum objects");

// ... 省略其他代码

}

可以看到反射API对Enum类型做了判断,不会实例化Enum类型。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值