定义枚举类
//定义加减乘除四个枚举常量
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
属性,而且其类型为这个抽象类的类型,名字则还是枚举常量的名字. -
并且同时我们可以看见在我们的编译路径下看见四个内部类的
image.png.class
文件.以及values()
和valutOf()
方法.
-
构造方法由我们定义的两个参数变成三个参数.
-
生成一个静态代码块.
我们可以通过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_ENUM
Constant 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.java
package 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
等.
如果本文对您有帮助,请您帮忙点个喜欢.谢谢~
网友评论