2019-07-23

java语法之-enum

enum大观

jdk1.5版本引入了enum语法。对于我们经常使用到的enum关键字,你是不是也有很多的疑问。

带着如下的这些问题,我们一步一步揭开enum关键字的面纱。

(1) enum和Enum有什么关系?都说enum是语法糖,编译完成之后他最终长什么样子?大量使用Enum对象会有性能问题吗?

(2) 为什么推荐使用EnumMap和EnumSet?

enm真身

众所周知,enum其实是java的一个语法糖。那么她的真身到底长什么样子了?

通过反编译技术,我们一步步揭开enum神秘的面纱。

反编译enum

我们先看一个enum的定义:

/**

* @author samdyli

* @date 2019/07/11 20:32

*/

public enum Color {

RED,

GREEN,

YELLOW;

}

先用javac Color.java命令将Color类编译成字节码,然后使用javap -p -c Color.class反编译字节码。

Compiled from "Color.java"

public final class Color extends java.lang.Enum {

  public static final Color RED;

  public static final Color GREEN;

  public static final Color YELLOW;

  private static final Color[] $VALUES;

  public static Color[] values();

    Code:

      0: getstatic    #1                  // Field $VALUES:[LColor;

      3: invokevirtual #2                  // Method "[LColor;".clone:()Ljava/lang/Object;

      6: checkcast    #3                  // class "[LColor;"

      9: areturn

  public static Color valueOf(java.lang.String);

    Code:

      0: ldc          #4                  // class Color

      2: aload_0

      3: invokestatic  #5                  // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;

      6: checkcast    #4                  // class Color

      9: areturn

  private Color();

    Code:

      0: aload_0

      1: aload_1

      2: iload_2

      3: invokespecial #6                  // Method java/lang/Enum."":(Ljava/lang/String;I)V

      6: return

  static {};

    Code:

      0: new          #4                  // class Color

      3: dup

      4: ldc          #7                  // String RED

      6: iconst_0

      7: invokespecial #8                  // Method "":(Ljava/lang/String;I)V

      10: putstatic    #9                  // Field RED:LColor;

      13: new          #4                  // class Color

      16: dup

      17: ldc          #10                // String GREEN

      19: iconst_1

      20: invokespecial #8                  // Method "":(Ljava/lang/String;I)V

      23: putstatic    #11                // Field GREEN:LColor;

      26: new          #4                  // class Color

      29: dup

      30: ldc          #12                // String YELLOW

      32: iconst_2

      33: invokespecial #8                  // Method "":(Ljava/lang/String;I)V

      36: putstatic    #13                // Field YELLOW:LColor;

      39: iconst_3

      40: anewarray    #4                  // class Color

      43: dup

      44: iconst_0

      45: getstatic    #9                  // Field RED:LColor;

      48: aastore

      49: dup

      50: iconst_1

      51: getstatic    #11                // Field GREEN:LColor;

      54: aastore

      55: dup

      56: iconst_2

      57: getstatic    #13                // Field YELLOW:LColor;

      60: aastore

      61: putstatic    #1                  // Field $VALUES:[LColor;

      64: return

}

从反编译代码段,我们发现enum类是一个继承Enum一个普通final类,每一个enum项被编译成一个常量(static final修饰的属性)。

Enum类

enum继承Enum类,有必要看看Enum都提供了什么功能。

简化Enum源码

public abstract class Enum>

        implements Comparable, Serializable {

        private final String name; // 名称

        private final int ordinal; // 序号


        public final String name() { // 暴露获取名称方法

        return name;

        }


        public final int ordinal() { // 暴露获取序号方法

        return ordinal;

        }


        protected Enum(String name, int ordinal) { // 隐藏构造函数

        this.name = name;

        this.ordinal = ordinal;

        }


        public final boolean equals(Object other) { // equals方法

        return this==other;

        }


        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;

        }

}       

针对Enum源码说明几点:

(1)Enum类提供了equals(==),compareTo,name和ordinal的方法。

(2) 为了阻止通过公开构造函数,序列化的方式构造Enum,Enum类覆盖readObject和readObjectNoData以及构造函数。

values

Enum源码中我们并没有发现values方法的定义和实现,其实values方法时在编译时自动增加的。从反编译源码中,反编译之后编译器新增了public static Color[] values();定义,并且在static代码块中初始化数组。

EnumMap和EnumSet

我们会遇到使用enum对象作为key的场景,这种场景推荐使用高效的EnumMap代替普通的Map。

EnumMap之所以高效是因为它使用Object数组作为存储结构。使用enum的序号(ordinal)作为数组索引进行随机读取。

摘取几段EnumMap实现核心代码,一探究竟。

public class EnumMap, V> extends AbstractMap

    implements java.io.Serializable, Cloneable {

        private transient Object[] vals; // 存储


        private static final Object NULL = new Object() {

            public int hashCode() {

                return 0;

            }

            public String toString() {

                return "java.util.EnumMap.NULL";

            }

        };


        public EnumMap(Class keyType) { // 构造函数

            this.keyType = keyType;

            keyUniverse = getKeyUniverse(keyType);

            vals = new Object[keyUniverse.length];

        }


          public V put(K key, V value) { // put代码

            typeCheck(key);

            int index = key.ordinal();

            Object oldValue = vals[index];

            vals[index] = maskNull(value);

            if (oldValue == null)

                size++;

            return unmaskNull(oldValue);

          }

    }

enum最佳实践

《Effective Java》对于enum有一点最佳实践,分别是:

用enum代替int常量

用实例域代替序数

用EnumSet代替位域

用EnumMap代替序数索引

用接口模拟可伸缩的枚举

参考文献

通过反编译字节码来理解 Java 枚举

Effective Java-枚举和注解

你可能感兴趣的:(2019-07-23)