定义枚举类
//定义加减乘除四个枚举常量public enum Operator { ADD,SUBTRACT,MULTIPLY,DIVIDE }
可以在枚举中定义普通方法
和抽象方法
.
public enum Operator { ADD { @Override public int calculate(int a, int b) { return a + b; } }, SUBTRACT { @Override public int calculate(int a, int b) { return a - b; } }, MULTIPLY { @Override public int calculate(int a, int b) { return a * b; } }, DIVIDE { @Override public int calculate(int a, int b) { if (b == 0) throw new IllegalArgumentException("divisor must be not zero"); return a / b; } }; public abstract int calculate(int a, int b); }
也同样可在枚举类中定义属性
,构造方法
.
public enum Operator { ADD("+") { @Override public int calculate(int a, int b) { return a + b; } }, SUBTRACT("-") { @Override public int calculate(int a, int b) { return a - b; } }, MULTIPLY("*") { @Override public int calculate(int a, int b) { return a * b; } }, DIVIDE("/") { @Override public int calculate(int a, int b) { if (b == 0) throw new IllegalArgumentException("divisor must be not zero"); return a / b; } }; private String operator; Operator(String operator) { this.operator = operator; } public abstract int calculate(int a, int b); public String getOperator() { return operator; } }
实现原理
Java文件在编译后会生成.class
字节码文件.
对该文件使用javap xxx.class
命令可以进行查看.
Compiled from "Operator.java"public abstract class com.inkio.onepro.Operator extends java.lang.Enum<com.inkio.onepro.Operator> { public static final com.inkio.onepro.Operator ADD; public static final com.inkio.onepro.Operator SUBTRACT; public static final com.inkio.onepro.Operator MULTIPLY; public static final com.inkio.onepro.Operator DIVIDE; public static com.inkio.onepro.Operator[] values(); public static com.inkio.onepro.Operator valueOf(java.lang.String); public abstract int calculate(int, int); public java.lang.String getOperator(); com.inkio.onepro.Operator(java.lang.String, int, java.lang.String, com.inkio.onepro.Operator$1); static {}; }
可以看到,枚举类在经过编译后变成了一个抽象类,继承了
java.lang.Enum
类;我们所定义的枚举常量也变成了
public static final
属性,而且其类型为这个抽象类的类型,名字则还是枚举常量的名字.并且同时我们可以看见在我们的编译路径下看见四个内部类的
.class
文件.以及values()
和valutOf()
方法.image.png
构造方法由我们定义的两个参数变成三个参数.
生成一个静态代码块.
我们可以通过javap -c -v Operator.class
指令对文件进行反编译如下.
Classfile /Users/sean/Desktop/Enum/Operator.class Last modified Dec 20, 2018; size 1587 bytes MD5 checksum 0b06630621a6a49772e53495294f72cf Compiled from "Operator.java"public abstract class com.inkio.onepro.Operator extends java.lang.Enum<com.inkio.onepro.Operator> minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_SUPER, ACC_ABSTRACT, ACC_ENUMConstant pool: #1 = Methodref #5.#60 // com/inkio/onepro/Operator."<init>":(Ljava/lang/String;ILjava/lang/String;)V #2 = Fieldref #5.#61 // com/inkio/onepro/Operator.$VALUES:[Lcom/inkio/onepro/Operator; #3 = Methodref #62.#63 // "[Lcom/inkio/onepro/Operator;".clone:()Ljava/lang/Object; #4 = Class #39 // "[Lcom/inkio/onepro/Operator;" #5 = Class #64 // com/inkio/onepro/Operator #6 = Methodref #29.#65 // java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum; #7 = Methodref #29.#66 // java/lang/Enum."<init>":(Ljava/lang/String;I)V #8 = Fieldref #5.#67 // com/inkio/onepro/Operator.operator:Ljava/lang/String; #9 = Class #68 // com/inkio/onepro/Operator$1 #10 = String #31 // ADD #11 = String #69 // + #12 = Methodref #9.#60 // com/inkio/onepro/Operator$1."<init>":(Ljava/lang/String;ILjava/lang/String;)V #13 = Fieldref #5.#70 // com/inkio/onepro/Operator.ADD:Lcom/inkio/onepro/Operator; #14 = Class #71 // com/inkio/onepro/Operator$2 #15 = String #33 // SUBTRACT #16 = String #72 // - #17 = Methodref #14.#60 // com/inkio/onepro/Operator$2."<init>":(Ljava/lang/String;ILjava/lang/String;)V #18 = Fieldref #5.#73 // com/inkio/onepro/Operator.SUBTRACT:Lcom/inkio/onepro/Operator; #19 = Class #74 // com/inkio/onepro/Operator$3 #20 = String #34 // MULTIPLY #21 = String #75 // * #22 = Methodref #19.#60 // com/inkio/onepro/Operator$3."<init>":(Ljava/lang/String;ILjava/lang/String;)V #23 = Fieldref #5.#76 // com/inkio/onepro/Operator.MULTIPLY:Lcom/inkio/onepro/Operator; #24 = Class #77 // com/inkio/onepro/Operator$4 #25 = String #35 // DIVIDE #26 = String #78 // / #27 = Methodref #24.#60 // com/inkio/onepro/Operator$4."<init>":(Ljava/lang/String;ILjava/lang/String;)V #28 = Fieldref #5.#79 // com/inkio/onepro/Operator.DIVIDE:Lcom/inkio/onepro/Operator; #29 = Class #80 // java/lang/Enum #30 = Utf8 InnerClasses #31 = Utf8 ADD #32 = Utf8 Lcom/inkio/onepro/Operator; #33 = Utf8 SUBTRACT #34 = Utf8 MULTIPLY #35 = Utf8 DIVIDE #36 = Utf8 operator #37 = Utf8 Ljava/lang/String; #38 = Utf8 $VALUES #39 = Utf8 [Lcom/inkio/onepro/Operator; #40 = Utf8 values #41 = Utf8 ()[Lcom/inkio/onepro/Operator; #42 = Utf8 Code #43 = Utf8 LineNumberTable #44 = Utf8 valueOf #45 = Utf8 (Ljava/lang/String;)Lcom/inkio/onepro/Operator; #46 = Utf8 <init> #47 = Utf8 (Ljava/lang/String;ILjava/lang/String;)V #48 = Utf8 Signature #49 = Utf8 (Ljava/lang/String;)V #50 = Utf8 calculate #51 = Utf8 (II)I #52 = Utf8 getOperator #53 = Utf8 ()Ljava/lang/String; #54 = Utf8 (Ljava/lang/String;ILjava/lang/String;Lcom/inkio/onepro/Operator$1;)V #55 = Utf8 <clinit> #56 = Utf8 ()V #57 = Utf8 Ljava/lang/Enum<Lcom/inkio/onepro/Operator;>; #58 = Utf8 SourceFile #59 = Utf8 Operator.java #60 = NameAndType #46:#47 // "<init>":(Ljava/lang/String;ILjava/lang/String;)V #61 = NameAndType #38:#39 // $VALUES:[Lcom/inkio/onepro/Operator; #62 = Class #39 // "[Lcom/inkio/onepro/Operator;" #63 = NameAndType #81:#82 // clone:()Ljava/lang/Object; #64 = Utf8 com/inkio/onepro/Operator #65 = NameAndType #44:#83 // valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum; #66 = NameAndType #46:#84 // "<init>":(Ljava/lang/String;I)V #67 = NameAndType #36:#37 // operator:Ljava/lang/String; #68 = Utf8 com/inkio/onepro/Operator$1 #69 = Utf8 + #70 = NameAndType #31:#32 // ADD:Lcom/inkio/onepro/Operator; #71 = Utf8 com/inkio/onepro/Operator$2 #72 = Utf8 - #73 = NameAndType #33:#32 // SUBTRACT:Lcom/inkio/onepro/Operator; #74 = Utf8 com/inkio/onepro/Operator$3 #75 = Utf8 * #76 = NameAndType #34:#32 // MULTIPLY:Lcom/inkio/onepro/Operator; #77 = Utf8 com/inkio/onepro/Operator$4 #78 = Utf8 / #79 = NameAndType #35:#32 // DIVIDE:Lcom/inkio/onepro/Operator; #80 = Utf8 java/lang/Enum #81 = Utf8 clone #82 = Utf8 ()Ljava/lang/Object; #83 = Utf8 (Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum; #84 = Utf8 (Ljava/lang/String;I)V { public static final com.inkio.onepro.Operator ADD; descriptor: Lcom/inkio/onepro/Operator; flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM public static final com.inkio.onepro.Operator SUBTRACT; descriptor: Lcom/inkio/onepro/Operator; flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM public static final com.inkio.onepro.Operator MULTIPLY; descriptor: Lcom/inkio/onepro/Operator; flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM public static final com.inkio.onepro.Operator DIVIDE; descriptor: Lcom/inkio/onepro/Operator; flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM public static com.inkio.onepro.Operator[] values(); descriptor: ()[Lcom/inkio/onepro/Operator; flags: ACC_PUBLIC, ACC_STATIC Code: stack=1, locals=0, args_size=0 0: getstatic #2 // Field $VALUES:[Lcom/inkio/onepro/Operator; 3: invokevirtual #3 // Method "[Lcom/inkio/onepro/Operator;".clone:()Ljava/lang/Object; 6: checkcast #4 // class "[Lcom/inkio/onepro/Operator;" 9: areturn LineNumberTable: line 3: 0 public static com.inkio.onepro.Operator valueOf(java.lang.String); descriptor: (Ljava/lang/String;)Lcom/inkio/onepro/Operator; flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=1, args_size=1 0: ldc #5 // class com/inkio/onepro/Operator 2: aload_0 3: invokestatic #6 // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum; 6: checkcast #5 // class com/inkio/onepro/Operator 9: areturn LineNumberTable: line 3: 0 public abstract int calculate(int, int); descriptor: (II)I flags: ACC_PUBLIC, ACC_ABSTRACT public java.lang.String getOperator(); descriptor: ()Ljava/lang/String; flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: getfield #8 // Field operator:Ljava/lang/String; 4: areturn LineNumberTable: line 37: 0 com.inkio.onepro.Operator(java.lang.String, int, java.lang.String, com.inkio.onepro.Operator$1); descriptor: (Ljava/lang/String;ILjava/lang/String;Lcom/inkio/onepro/Operator$1;)V flags: ACC_SYNTHETIC Code: stack=4, locals=5, args_size=5 0: aload_0 1: aload_1 2: iload_2 3: aload_3 4: invokespecial #1 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;)V 7: return LineNumberTable: line 3: 0 static {}; descriptor: ()V flags: ACC_STATIC Code: stack=5, locals=0, args_size=0 0: new #9 // class com/inkio/onepro/Operator$1 3: dup 4: ldc #10 // String ADD 6: iconst_0 7: ldc #11 // String + 9: invokespecial #12 // Method com/inkio/onepro/Operator$1."<init>":(Ljava/lang/String;ILjava/lang/String;)V 12: putstatic #13 // Field ADD:Lcom/inkio/onepro/Operator; 15: new #14 // class com/inkio/onepro/Operator$2 18: dup 19: ldc #15 // String SUBTRACT 21: iconst_1 22: ldc #16 // String - 24: invokespecial #17 // Method com/inkio/onepro/Operator$2."<init>":(Ljava/lang/String;ILjava/lang/String;)V 27: putstatic #18 // Field SUBTRACT:Lcom/inkio/onepro/Operator; 30: new #19 // class com/inkio/onepro/Operator$3 33: dup 34: ldc #20 // String MULTIPLY 36: iconst_2 37: ldc #21 // String * 39: invokespecial #22 // Method com/inkio/onepro/Operator$3."<init>":(Ljava/lang/String;ILjava/lang/String;)V 42: putstatic #23 // Field MULTIPLY:Lcom/inkio/onepro/Operator; 45: new #24 // class com/inkio/onepro/Operator$4 48: dup 49: ldc #25 // String DIVIDE 51: iconst_3 52: ldc #26 // String / 54: invokespecial #27 // Method com/inkio/onepro/Operator$4."<init>":(Ljava/lang/String;ILjava/lang/String;)V 57: putstatic #28 // Field DIVIDE:Lcom/inkio/onepro/Operator; 60: iconst_4 61: anewarray #5 // class com/inkio/onepro/Operator 64: dup 65: iconst_0 66: getstatic #13 // Field ADD:Lcom/inkio/onepro/Operator; 69: aastore 70: dup 71: iconst_1 72: getstatic #18 // Field SUBTRACT:Lcom/inkio/onepro/Operator; 75: aastore 76: dup 77: iconst_2 78: getstatic #23 // Field MULTIPLY:Lcom/inkio/onepro/Operator; 81: aastore 82: dup 83: iconst_3 84: getstatic #28 // Field DIVIDE:Lcom/inkio/onepro/Operator; 87: aastore 88: putstatic #2 // Field $VALUES:[Lcom/inkio/onepro/Operator; 91: return LineNumberTable: line 5: 0 line 10: 15 line 15: 30 line 20: 45 line 3: 60} Signature: #57 // Ljava/lang/Enum<Lcom/inkio/onepro/Operator;>;SourceFile: "Operator.java"InnerClasses: static #24; //class com/inkio/onepro/Operator$4 static #19; //class com/inkio/onepro/Operator$3 static #14; //class com/inkio/onepro/Operator$2 static #9; //class com/inkio/onepro/Operator$1
代码那么多,看起来比较繁琐,那么我们翻译一下,使用jad将这个文件转成java的.执行指令jad -sjava Operator.class
// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.// Jad home page: http://www.kpdus.com/jad.html// Decompiler options: packimports(3) // Source File Name: Operator.javapackage com.inkio.onepro;public abstract class Operator extends Enum{ public static Operator[] values() { return (Operator[])$VALUES.clone(); } public static Operator valueOf(String s) { return (Operator)Enum.valueOf(com/inkio/onepro/Operator, s); } private Operator(String s, int i, String s1) { super(s, i); operator = s1; } public abstract int calculate(int i, int j); public String getOperator() { return operator; } public static final Operator ADD; public static final Operator SUBTRACT; public static final Operator MULTIPLY; public static final Operator DIVIDE; private String operator; private static final Operator $VALUES[]; static { ADD = new Operator("ADD", 0, "+") { public int calculate(int i, int j) { return i + j; } } ; SUBTRACT = new Operator("SUBTRACT", 1, "-") { public int calculate(int i, int j) { return i - j; } } ; MULTIPLY = new Operator("MULTIPLY", 2, "*") { public int calculate(int i, int j) { return i * j; } } ; DIVIDE = new Operator("DIVIDE", 3, "/") { public int calculate(int i, int j) { if(j == 0) throw new IllegalArgumentException("divisor must be not zero"); else return i / j; } } ; $VALUES = (new Operator[] { ADD, SUBTRACT, MULTIPLY, DIVIDE }); } }
这下代码是不是变得高端大次上档气了,嘿嘿,那么继续分析~
首先,声明了四个public static final
常量,并且在静态代码块中为其赋值,且静态代码块中还生成了一个$VALUES
字段,并初始化为定义的枚举类型的所有枚举常量的数组.
并且声明了一个枚举类型数组的静态方法values()
以及一个我们定义的枚举类型的静态方法valueOf()
,我们可以通过values()
方法获取这个枚举值的数组,它的实现就是在静态代码块中生成的$VALUES
并初始化的过程,同样也可以直接通过valueOf
方法得到定义的枚举类型常量,是通过强转实现的.
可以这样使用:
Operator operator = Operator.valueOf(Operator.ADD.name()); System.out.println(operator); >>> ADD
再说说构造方法,明明我们定义的时候只有一个参数,为什么现在变成三个了呢?
其实我们在使用时可以发现,每个枚举类其实都定义了两个属性,一个name
一个ordinal
,
Operator.ADD.name()Operator.ADD.ordinal()
name
表示我们定义枚举常量的名称,如ADD
、SUBTRACT
.ordinal
表示的是一个顺序号,按照我们定义常量的顺序自动分配,从0开始,在枚举常量初始化时,会自动初始化这两个字段设置相应的值,再加上我们为构造函数设置的字段,所以构造方法就有三个参数了.
我们可以从Enum类的代码中看到,定义的name和ordinal属性都是final的,而且大部分方法也都是final的,特别是clone、readObject、writeObject这三个方法,这三个方法和枚举通过静态代码块来进行初始化一起,它保证了枚举类型的不可变性,不能通过克隆,不能通过序列化和反序列化来复制枚举,这能保证一个枚举常量只是一个实例,即是单例的,所以在effective java中推荐使用枚举来实现单例.
对于Android开发,枚举还有一个面试相关的问题,在Android中为什么不推荐使用枚举,说出原因.通过本文其实我们就可以知道,枚举占用的内存会多很多.所以推荐使用注解来代替枚举.@IntDef
、@StringDef
等.
作者:Inkio
链接:https://www.jianshu.com/p/fe7891daeb36
共同学习,写下你的评论
评论加载中...
作者其他优质文章