【java基础】枚举
枚举的定义
public enum Color {
Red,Blue,Green;
}
枚举的使用
Color red = Color.Red;//枚举的其中一个类型
Color[] values = Color.values();//获取所有的枚举类型
String name = red.name();//可以获得枚举值的名称
int ordinal = red.ordinal();//可以获得枚举值的编号
原理
那我们定义枚举类型后,到底发生了什么呢?我们对枚举的实现原理进行探究。
我们来解析下Color.class文件,命令javap Color
public final class Color extends java.lang.Enum<Color> {
public static final Color Red;
public static final Color Blue;
public static final Color Green;
public static final Color[] $VALUES;
public static Color[] values();
public static Color valueOf(java.lang.String);
static {};
}
从解析后的文件中我们可以知道
- 枚举类是
final
的,不能被继承 - 枚举类在经过编译后生成一个继承
java.lang.Enum
的类Color
- 编译后的枚举值,是该类的Color类型的成员变量,如
Red、Bule、Green
,并且是final static
修饰 - 枚举编译后的类添加了静态的
values()
和valueOf(java.lang.String)
方法 - 静态代码块
static {}
进一步细化Color.class
命令javap -c Color
public final class Color extends java.lang.Enum<Color> {
public static final Color Red;
public static final Color Blue;
public static final Color Green;
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;)Ljav
a/lang/Enum;
6: checkcast #4 // class Color
9: areturn
static {};
Code:
0: new #4 // class Color
3: dup
4: ldc #7 // String Red
6: iconst_0
7: invokespecial #8 // Method "<init>":(Ljava/lang/String;I)V
10: putstatic #9 // Field Red:LColor;
13: new #4 // class Color
16: dup
17: ldc #10 // String Blue
19: iconst_1
20: invokespecial #8 // Method "<init>":(Ljava/lang/String;I)V
23: putstatic #11 // Field Blue:LColor;
26: new #4 // class Color
29: dup
30: ldc #12 // String Green
32: iconst_2
33: invokespecial #8 // Method "<init>":(Ljava/lang/String;I)V
36: putstatic #13 // Field Green: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 Blue:LColor;
54: aastore
55: dup
56: iconst_2
57: getstatic #13 // Field Green:LColor;
60: aastore
61: putstatic #1 // Field $VALUES:[LColor;
64: return
}
还原后的代码如下:
public final class Color extends java.lang.Enum<Color> {
//定义的枚举成员
public static final Color Red;
public static final Color Blue;
public static final Color Green;
//编译器自动生成的 javap -c 还查不出来,疑惑
public static final /* synthetic */ Color[] $VALUES;//编译器自动生成的
public static Color[] values(){
/**
* 0: getstatic #1 // Field $VALUES:[LColor;
* 3: invokevirtual #2 // Method "[LColor;".clone:()Ljava/lang/Object;
* 6: checkcast #3 // class "[LColor;"
* 9: areturn
*/
return $VALUES.clone();
}
public static Color valueOf(java.lang.String s){
/**
* 0: ldc #4 // class Color
* 2: aload_0
* 3: invokestatic #5 // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljav
* a/lang/Enum;
* 6: checkcast #4 // class Color
* 9: areturn
*/
return Enum.valueOf(Color.class,s);
}
protected Color(String name, int ordinal) {
super(name, ordinal);
}
static {
/**
* 0: new #4 // class Color
* 3: dup
* 4: ldc #7 // String Red
* 6: iconst_0
* 7: invokespecial #8 // Method "<init>":(Ljava/lang/String;I)V
* 10: putstatic #9 // Field Red:LColor;
*/
Red=new Color("Red",0);
/**
* 13: new #4 // class Color
* 16: dup
* 17: ldc #10 // String Blue
* 19: iconst_1
* 20: invokespecial #8 // Method "<init>":(Ljava/lang/String;I)V
* 23: putstatic #11 // Field Blue:LColor;
*/
Blue=new Color("Blue",1);
/**
* 26: new #4 // class Color
* 29: dup
* 30: ldc #12 // String Green
* 32: iconst_2
* 33: invokespecial #8 // Method "<init>":(Ljava/lang/String;I)V
* 36: putstatic #13 // Field Green:LColor;
*/
Green=new Color("Green",2);
/**
* 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 Blue:LColor;
* 54: aastore
* 55: dup
* 56: iconst_2
* 57: getstatic #13 // Field Green:LColor;
* 60: aastore
* 61: putstatic #1 // Field $VALUES:[LColor;
*/
$VALUES=new Color[]{Red,Blue,Green};
};
}
通过上面还原后的代码可知,在类的static代码块
中编译器帮我们生成枚举中的每个成员,实际上就是生成对象并赋值给静态变量。
枚举的扩展
单例模式
枚举其实就是编译帮我们在静态代码块中创建一个或多个枚举成员对象,如果我们只定义一个枚举成员,这样就是一个单例对象
public enum Singleton {
INSTANCE;
public void doSometing(){
System.out.println("doing");
}
}
代码如此简单,不仅如此,这样的方式还可以解决反射攻击
和反序列化
导致的单例失败的问题。
避免反射攻击
在获取实例时进行了枚举判断,如果是枚举类型的则直接抛出异常。
Jdk1.8 Class.clss
文件的416..417
行
public T newInstance(Object ... initargs)
throws InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException{
....
if ((clazz.getModifiers() & Modifier.ENUM) != 0)
throw new IllegalArgumentException("Cannot reflectively create enum objects");
.....
return inst;
}
反序列化
枚举的序列时仅降枚举对象的name属性输出到结果中,在反序列化时则是通过java.lang.Enum
的valueOf
方法来根据名字在jvm中查找对象的。
同时编译器还禁止对枚举的定制化序列机制,因此禁用了writeObject、readObject、readObjectNoData、writeReplace和readResolve等方法。
策略模式
一种行为,多种结果,不同结果的实现,名曰策略。
public enum Calculator {
//加法运算
ADD("+"){
public int exec(int a,int b){
return a+b;
}
},
//减法运算
SUB("-"){
public int exec(int a,int b){
return a - b;
}
};
String value = "";
//定义成员值类型
Calculator(String _value){
this.value = _value;
}
//获得枚举成员的值
public String getValue(){
return this.value;
}
//声明一个抽象函数
public abstract int exec(int a,int b);
}
总结
- 枚举其实就是编写代码方面的
语法糖
,仍然是一个完整的类,暴露了几个静态变量,隐藏掉了大部分细节 - 枚举的成员变量是在编译后的静态代码块逐个创建实例来实现的
- 枚举的
values()
和valueOf()
方法都是编译器帮我们创建出来的 - 在调用
values()
方法时,返回的对象是克隆出来的一份新的,修改对原对象没有影响 - 枚举可以实现单例模式,能避免反射攻击和反序列化问题
- 避免反射攻击是因为枚举类不允许通过反射实例化
- 反序列化是通过Enum.valueOf到jvm中根据class,name去查找的
- 枚举可以实现策略模式,优化代码,避免大量的if…else的出现