Enum类型详解

来源:互联网 发布:手机百度云网络异常 编辑:程序博客网 时间:2024/05/21 16:27
咱们先来看看简单的枚举类
package com.xiaotang;public enum AA{read, black, green}
这是最简单的一个枚举应用,然后调用的时候我们都知道通过 AA.read来使用,那么这么简单操作背后又蕴藏着什么样的惊人的秘密呢?接下来让我们一步步去揭秘!
首先咱先 javap命令来一波(此处配置的参数是 -private ):

得到如下结果:

通过以上分析我们可以是不是就可以得到枚举类型对应的类中的属性了吧!但是final属性要初始化啊,通过修改命令参数(在此处,我使用 -v 显示操作属性的指令集)得到指令集代码:
  static {};  Code:   Stack=4, Locals=0, Args_size=0   0:new#1; //class com/xiaotang/AA   3:dup   4:ldc#14; //String read   6:iconst_0   7:invokespecial#15; //Method "<init>":(Ljava/lang/String;I)V   10:putstatic#19; //Field read:Lcom/xiaotang/AA;   13:new#1; //class com/xiaotang/AA   16:dup   17:ldc#21; //String black   19:iconst_1   20:invokespecial#15; //Method "<init>":(Ljava/lang/String;I)V   23:putstatic#22; //Field black:Lcom/xiaotang/AA;   26:new#1; //class com/xiaotang/AA   29:dup   30:ldc#24; //String green   32:iconst_2   33:invokespecial#15; //Method "<init>":(Ljava/lang/String;I)V   36:putstatic#25; //Field green:Lcom/xiaotang/AA;   39:iconst_3   40:anewarray#1; //class com/xiaotang/AA   43:dup   44:iconst_0   45:getstatic#19; //Field read:Lcom/xiaotang/AA;   48:aastore   49:dup   50:iconst_1   51:getstatic#22; //Field black:Lcom/xiaotang/AA;   54:aastore   55:dup   56:iconst_2   57:getstatic#25; //Field green:Lcom/xiaotang/AA;   60:aastore   61:putstatic#27; //Field ENUM$VALUES:[Lcom/xiaotang/AA;   64:return

通过分析指令集可以得到枚举类的属性赋值如下:
public static final AA read = new AA("read", 0);
public static final AA black = new AA("black", 1);
public static final AA green = new AA("green", 2);
private static final AA ENUM$VALUES[] = new AA[] { read, black, green };

可以看到 枚举类AA中有个私有构造方法 private AA(String name,int i) 但是javap命令是查看不了私有方法的指令集的(我目前没有找到,找到的了可以告诉我下,谢谢),那么怎么去看构造方法里的内容呢,这里只能是猜测!构造方法无非就是去操作属性,而枚举类的属性都是AA类型,这里我们需要去查看它的父类Enum了。
可以看到Enum类中有如下属性和方法:
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; }

而我们通过 测试: AA aa=AA.black; System.out.printlen(aa.name()); //输出 black
所以我们猜测私有构造方法内容:
private AA(String name,int ordinal)
{
super(name,ordinal);
}

我们再看看 编辑器给枚举类新增其他两个方法:
(1)public static com.xiaotang.AA[] values()
指令集如下:
 Code:   Stack=5, Locals=3, Args_size=0   0:getstatic#27; //Field ENUM$VALUES:[Lcom/xiaotang/AA;   3:dup   4:astore_0   5:iconst_0   6:aload_0   7:arraylength   8:dup   9:istore_1   10:anewarray#1; //class com/xiaotang/AA   13:dup   14:astore_2   15:iconst_0   16:iload_1   17:invokestatic#35; //Method java/lang/System.arraycopy:  (Ljava/lang/Object;ILjava/lang/Object;II)V   20:aload_2   21:areturn  LineNumberTable:    line 1: 0

通过指令集我们可以还原values方法:
public static AA[] values(){
AA[] array=ENUM$VALUES;
int length=array.length;
AA[] newArray=new AA[3];
System.arrayCopy(array,0,newArray,0,3);
return newArray;
}
(2)public static AA valueOf(java.lang.String);
指令集如下:
 Code:   Stack=2, Locals=1, Args_size=1   0:ldc#1; //class com/xiaotang/AA   2:aload_0   3:invokestatic#43; //Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;   6:checkcast#1; //class com/xiaotang/AA   9:areturn<pre name="code" class="java"> public static <T extends Enum<T>> T valueOf(Class<T> enumType,                                                String name) {        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 const " + enumType +"." + name);    }

LineNumberTable: line 1: 0
分析知道,该方法就是调用了 Enum.valueOf(AA.class,string) 并作为返回值返回,我们在看看Enum的valueOf方法:
 public static <T extends Enum<T>> T valueOf(Class<T> enumType,                                                String name) {        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 const " + enumType +"." + name);    }
返回就是T result,我们在看看 enumType.enumConstantDirectory()干了啥,点进去发现它返回的就是一个map集合而已,从中获取 key值name对应的value值,那么这个map集合是什么内容呢?
Map<String, T> enumConstantDirectory() {if (enumConstantDirectory == null) {            T[] universe = getEnumConstantsShared();            if (universe == null)                throw new IllegalArgumentException(                    getName() + " is not an enum type");            Map<String, T> m = new HashMap<String, T>(2 * universe.length);            for (T constant : universe)                m.put(((Enum)constant).name(), constant);            enumConstantDirectory = m;        }        return enumConstantDirectory;    }
打开getEnumConstantsShared方法,就能看到了结果:
T[] getEnumConstantsShared() {if (enumConstants == null) {    if (!isEnum()) return null;    try {final Method values = getMethod("values");                java.security.AccessController.doPrivileged                    (new java.security.PrivilegedAction() {                            public Object run() {                                values.setAccessible(true);                                return null;                            }                        });enumConstants = (T[])values.invoke(null);    }    // These can happen when users concoct enum-like classes    // that don't comply with the enum spec.    catch (InvocationTargetException ex) { return null; }    catch (NoSuchMethodException ex) { return null; }    catch (IllegalAccessException ex) { return null; }}return enumConstants;    }
通过反射获取 枚举类中values方法,并执行该方法将结果集T[]返回过去,然后遍历存进Map集合中,枚举类的values方法获取的不正是AA 的 ENUM$VALUES复制出来的数组属性吗?它的值是new AA[] { read, black, green }; 可以看到 Map集合中存储的key 为 每个数组元素对应的调用name(),存储的value是 数组元素(AA.read,AA.black).

以上就是我们分析的枚举类了,可以看到,我们定义一个基本的枚举类,其实编译器就已经给我们自动赋值了枚举类属性的值,给了它name和ordinal,name就是它的属性名字,ordinal则是按照0,1,2递增顺序赋值,并并新增了一个数组,数组内容则是枚举类的属性集合,新增了两个方法 valueOf(String name)和 T[] values(),有了这些基础,为我们认识和使用枚举类有了很大的帮助!现在就让我们一起来使用一波枚举类吧!

应用一: 枚举类与switch的应用
public class Test {public static void main(String[] args) {switch (AA.black) {case read:System.out.println("read");break;case black:System.out.println("black");break;case green:System.out.println("green");break;default:System.out.println("other");break;}}}

初一看,呀,还有这种写法啊!switch(AA.black),不是说switch后面只能接int,char类型数据吗?接字符串也是jdk1.7以后的事情啊,况且 AA.black是一个对象啊,更不应该啊!还有 case read, read是什么鬼!case 接常量值好不,大家是不是有一肚子的疑惑啊!哈哈,其实我挺疑惑的,我们知道枚举类中还有个ordinal属性没有用到, 下面我这样写,大家是不是就能理解了:
public class Test {public final static int READ = AA.read.ordinal();public final static int BLACK = AA.black.ordinal();public final static int GREEN = AA.green.ordinal();public static void main(String[] args) {switch (AA.read.ordinal()) {case READ:System.out.println("read");break;case BLACK:System.out.println("black");break;case GREEN:System.out.println("green");break;default:System.out.println("other");break;}}}
但是这个代码是不能运行的啊,编译都通不过 case后面接的不是常量值, 它的值会根据 AA.XX.ordinal()的变化而变化,所以不能接在case 后面,这样只是让我们容易理解罢了!现在我们来看看,当枚举类与switch的相遇时,编译器给我们做了哪些事情吧!
还是先javap一波(查看方法和属性):
Compiled from "Test.java"public class com.xiaotang.Test extends java.lang.Object{    private static int[] $SWITCH_TABLE$com$xiaotang$AA;    public com.xiaotang.Test();    public static void main(java.lang.String[]);    static int[] $SWITCH_TABLE$com$xiaotang$AA();}
可以看到 编译器给我们新增了 int型的$SWITCH_TABLE$com$xiaotang$AA数组,不同的编译器数组取名可能不一样,还新增了$SWITCH_TABLE$com$xiaotang$AA方法,返回int型数组值。

下面我们来详细分析一下$SWITCH_TABLE$com$xiaotang$AA方法:
static int[] $SWITCH_TABLE$com$xiaotang$AA();  Code:   Stack=3, Locals=1, Args_size=0   0:getstatic#53; //Field $SWITCH_TABLE$com$xiaotang$AA:[I   3:dup   4:ifnull8   7:areturn   8:pop   9:invokestatic#55; //Method com/xiaotang/AA.values:()[Lcom/xiaotang/AA;   12:arraylength   13:newarray int   15:astore_0   16:aload_0   17:getstatic#59; //Field com/xiaotang/AA.black:Lcom/xiaotang/AA;   20:invokevirtual#27; //Method com/xiaotang/AA.ordinal:()I   23:iconst_2   24:iastore   25:goto29   28:pop   29:aload_0   30:getstatic#61; //Field com/xiaotang/AA.green:Lcom/xiaotang/AA;   33:invokevirtual#27; //Method com/xiaotang/AA.ordinal:()I   36:iconst_3   37:iastore   38:goto42   41:pop   42:aload_0   43:getstatic#21; //Field com/xiaotang/AA.read:Lcom/xiaotang/AA;   46:invokevirtual#27; //Method com/xiaotang/AA.ordinal:()I   49:iconst_1   50:iastore   51:goto55   54:pop   55:aload_0   56:dup   57:putstatic#53; //Field $SWITCH_TABLE$com$xiaotang$AA:[I   60:areturn

通过指令分析,得到执行代码:
  public  int [] $SWITCH_TABLE$com$xiaotang$AA(){               if($SWITCH_TABLE$com$xiaotang$AA==null)               {                 int[] array = new int[AA.values().length]; array[AA.black.ordinal()] = 2;array[AA.green.ordinal()] = 3;array[AA.read.ordinal()] = 1;                $SWITCH_TABLE$com$xiaotang$AA=array;              }               return $SWITCH_TABLE$com$xiaotang$AA;              }
咱们再来看main方法的执行内容及对应的指令集:
public static void main(String[] args) {switch (AA.read) {case read:System.out.println("read");break;case black:System.out.println("black");break;case green:System.out.println("green");break;default:System.out.println("other");break;}
对应的指令集:
Code:   Stack=2, Locals=1, Args_size=1   0:invokestatic#18; //Method $SWITCH_TABLE$com$xiaotang$AA:()[I   3:getstatic#21; //Field com/xiaotang/AA.read:Lcom/xiaotang/AA;   6:invokevirtual#27; //Method com/xiaotang/AA.ordinal:()I   9:iaload   10:tableswitch{ //1 to 31: 36;2: 47;3: 58;default: 69 }   36:getstatic#31; //Field java/lang/System.out:Ljava/io/PrintStream;   39:ldc#37; //String read   41:invokevirtual#38; //Method java/io/PrintStream.println:(Ljava/lang/String;)V   44:goto77   47:getstatic#31; //Field java/lang/System.out:Ljava/io/PrintStream;   50:ldc#44; //String black   52:invokevirtual#38; //Method java/io/PrintStream.println:(Ljava/lang/String;)V   55:goto77   58:getstatic#31; //Field java/lang/System.out:Ljava/io/PrintStream;   61:ldc#46; //String green   63:invokevirtual#38; //Method java/io/PrintStream.println:(Ljava/lang/String;)V   66:goto77   69:getstatic#31; //Field java/lang/System.out:Ljava/io/PrintStream;   72:ldc#48; //String other   74:invokevirtual#38; //Method java/io/PrintStream.println:(Ljava/lang/String;)V   77:return
是这样的,在Test中定义一个int型数组,根据case先后顺序:case read,case black,case green(这个顺序人为可以改变),编译器会默认对应关系,read----->对应1, balck------->2,green------>3
然后在 新增方法中添加数组赋值, int array[]=new int[AA.values().length];
array[AA.black.ordinal()] = 2;
array[AA.green.ordinal()] = 3;
array[AA.read.ordinal()] = 1;

然后解析 switch后面对象的ordinal()值,并作为数组array数组下标获取对应值,在case中需要对应值。
(2)带参数的枚举应用
public enum AA {read(100), black("sdaf"), green;private AA(int red) {}private AA() {}private AA(String ss) {}}
对应的指令集方法和属性:
Compiled from "AA.java"public final class com.xiaotang.AA extends java.lang.Enum{    public static final com.xiaotang.AA read;    public static final com.xiaotang.AA black;    public static final com.xiaotang.AA green;    private static final com.xiaotang.AA[] ENUM$VALUES;    static {};    private com.xiaotang.AA(java.lang.String, int, int);    private com.xiaotang.AA(java.lang.String, int);    private com.xiaotang.AA(java.lang.String, int, java.lang.String);    public static com.xiaotang.AA[] values();    public static com.xiaotang.AA valueOf(java.lang.String);}












0 0
原创粉丝点击