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
- enum类型详解
- enum枚举类型详解
- Enum类型详解
- java枚举类型enum详解
- Java枚举类型enum使用详解 .
- c/c++之enum类型详解
- Android enum(枚举类型)使用详解
- C语言enum枚举类型详解
- 详解Swift中enum类型的用法
- C语言详解 - 枚举类型-enum
- JAVA中枚举类型enum综合详解
- Enum类型
- Enum类型
- Enum 类型
- Enum类型
- enum类型
- enum类型
- enum 类型
- gulp前端自动化构建工具(三):gulp工具配置文件
- 本地推送 到9点自动刷新界面
- Android Studio设置注解模版
- 解决 side-by-side 问题的一种方法
- git常见命令
- Enum类型详解
- [Leetcode]1. Two Sum
- 在lnmp服务器用file_put_contents函数写入文件无法写入的解决方法
- Android:30分钟弄明白Touch事件分发机制
- Directory Listing For解决方法-tomcat启动,首页报错
- hdu 5512 Pagodas gcd()
- [总结]视频质量评价技术零基础学习方法
- java,linux转义字符区别
- 人脸检测——Faster R-CNN