您的位置:首页 > 编程语言 > Java开发

学习笔记:java枚举

2017-11-30 11:31 253 查看
java自1.5后支持枚举。Java中,枚举的本质是一种类,Java在其上做了很多特殊的约束,这也使得枚举具备了简洁、安全和便捷的特性。

一、Java枚举(enum)的定义。

Java5之前,没有枚举,定义常量的常见方式是这样的:

/**
* Created by gyx on 2017/11/29.
* 没有枚举时,常见的常量定义方式
*/
public class Sex {
public final static int MALE = 1;//男性
public final static int FEMALE = 0;//女性
public final static int  LADY_BOY = -1;//人妖
}

这种定义方式没错,但是存在以下几点不足:

1、存在类型安全隐患。看下面代码:

public void setSex(int sex) {
this.sex = sex;
}

方法的参数是int型,即任何的int类型的值不限1、0、-1都能够作为该方法的参数。为了避免意料之外的结果,必须限定参数的上限和下限,否则就容易误传入非正确的int值,编译器是检查不出这类错误的,全靠人为控制。另一方面,使用静态常量时候,常常不够直观,不看定义,谁也不知道1、0、-1究竟表示什么,而且当有要罗列多少种方向的需求的时候,也不方便。

2、如果存在int值相同的常量,很有可能产生混淆。看下面代码:

public class Role {
public final static int MANAGER = 1;
}


/**
* 入门登记
* @param sex
*/
private void bookIn(int sex){
if(sex == Sex.MALE){
System.out.println("男");
}
}

这时如果传入的是Role.MANAGER,也是一样的。

因此这样的在枚举出现以后,这样的定义并不提倡。

以下是枚举的定义。

public enum EnumSex {
MALE,FEMALE,LADY_BOY
}

再来这段代码,出错的概率就小很多,代码也清晰很多。

private void bookIn(EnumSex sex){
if(sex == EnumSex.MALE){
System.out.println("男");
}
}

二、枚举的实现原理。

正如前面所说,枚举是一种特殊的类。使用enmu关键字定义枚举,编译后会生成一个相关类,继承自java.lang.Enum,也就是说,在Java中,关键字enum实际上是一个语法糖(Android的AIDL貌似一回事),enum经过编译器,变成一个继承自java.lang.Enum的类。查看EnumSex编译后的EnumSex.class

public final class EnumSex extends java.lang.Enum<EnumSex>

{
public static final EnumSex MALE;
descriptor: LEnumSex;
flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM

public static final EnumSex FEMALE;
descriptor: LEnumSex;
flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM

public static final EnumSex LADY_BOY;
descriptor: LEnumSex;
flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM

public static EnumSex[] values();
descriptor: ()[LEnumSex;
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=0, args_size=0
0: getstatic     #1                  // Field $VALUES:[LEnumSex;
3: invokevirtual #2                  // Method "[LEnumSex;".clone:()Ljava/lang/Object;
6: checkcast     #3                  // class "[LEnumSex;"
9: areturn
LineNumberTable:
line 4: 0

public static EnumSex valueOf(java.lang.String);
descriptor:
fb4c
(Ljava/lang/String;)LEnumSex;
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
0: ldc           #4                  // class EnumSex
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 EnumSex
9: areturn
LineNumberTable:
line 4: 0
LocalVariableTable:
Start  Length  Slot  Name   Signature
0      10     0  name   Ljava/lang/String;

static {};
descriptor: ()V
flags: ACC_STATIC
Code:
stack=4, locals=0, args_size=0
0: new           #4                  // class EnumSex
3: dup
4: ldc           #7                  // String MALE
6: iconst_0
7: invokespecial #8                  // Method "<init>":(Ljava/lang/String;I)V
10: putstatic     #9                  // Field MALE:LEnumSex;
13: new           #4                  // class EnumSex
16: dup
17: ldc           #10                 // String FEMALE
19: iconst_1
20: invokespecial #8                  // Method "<init>":(Ljava/lang/String;I)V
23: putstatic     #11                 // Field FEMALE:LEnumSex;
26: new           #4                  // class EnumSex
29: dup
30: ldc           #12                 // String LADY_BOY
32: iconst_2
33: invokespecial #8                  // Method "<init>":(Ljava/lang/String;I)V
36: putstatic     #13                 // Field LADY_BOY:LEnumSex;
39: iconst_3
40: anewarray     #4                  // class EnumSex
43: dup
44: iconst_0
45: getstatic     #9                  // Field MALE:LEnumSex;
48: aastore
49: dup
50: iconst_1
51: getstatic     #11                 // Field FEMALE:LEnumSex;
54: aastore
55: dup
56: iconst_2
57: getstatic     #13                 // Field LADY_BOY:LEnumSex;
60: aastore
61: putstatic     #1                  // Field $VALUES:[LEnumSex;
64: return
LineNumberTable:
line 5: 0
line 4: 39
}

不太看的懂,大概就是编译器为我们生产了EnumSex类,定义了MALE,FEMALE,LADY_BOY三个静态整型常量,static{}静态初始化实例,value和valueof两个静态方法。

三、枚举的常见用法。

既然继承自Enum类,先看看Enum类。

public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializable

这是一个抽象类,实现了Comparable和Serializalble接口。这在后面单例模式应用中很关键。

常见方法:

返回类型方法名称方法说明
int
compareTo(E o)
比较此枚举与指定对象的顺序
boolean
equals(Object other)
当指定对象等于此枚举常量时,返回 true。
Class<?>
getDeclaringClass()
返回与此枚举常量的枚举类型相对应的 Class 对象
String
name()
返回此枚举常量的名称,在其枚举声明中对其进行声明
int
ordinal()
返回枚举常量的序数(它在枚举声明中的位置,其中初始常量序数为零)
String
toString()
返回枚举常量的名称,它包含在声明中
static<T extends Enum<T>> T
static valueOf(Class<T> enumType, String name)
返回带指定名称的指定枚举类型的枚举常量。
    这里主要说明一下
ordinal()
方法,该方法获取的是枚举变量在枚举类中声明的顺序,下标从0开始,如日期中的MONDAY在第一个位置,那么MONDAY的ordinal值就是0,如果MONDAY的声明位置发生变化,那么ordinal方法获取到的值也随之变化,注意在大多数情况下我们都不应该首先使用该方法,毕竟它总是变幻莫测的。
compareTo(E
o)
方法则是比较枚举的大小,注意其内部实现是根据每个枚举的ordinal值大小进行比较的。
name()
方法与
toString()
几乎是等同的,都是输出变量的字符串形式。至于
valueOf(Class<T>
enumType, String name)
方法则是根据枚举类的Class对象和枚举名称获取枚举常量,注意该方法是静态的,后面在枚举单例时,我们还会详细分析该方法。
编译器生成的value()和valueof()方法
    Enum类没有value()方法,方法value是编译器生成的static方法,用于返回所有枚举值。

   方法Enum.valueof()有两个参数,编译器生成的valueof()只有一个参数,实际上调用的还是Enum.valueof, 只是编译器简化了一下,返回对应的枚举值。


枚举与Class对象

上述我们提到当枚举实例向上转型为Enum类型后,values()方法将会失效,也就无法一次性获取所有枚举实例变量,但是由于Class对象的存在,即使不使用values()方法,还是有可能一次获取到所有枚举实例变量的,在Class对象中存在如下方法:
返回类型方法名称方法说明
T[]
getEnumConstants()
返回该枚举类型的所有元素,如果Class对象不是枚举类型,则返回null。
boolean
isEnum()
当且仅当该类声明为源代码中的枚举时返回 true
因此通过getEnumConstants()方法,同样可以轻而易举地获取所有枚举实例变量下面通过代码来演示这个功能:
//正常使用
Day[] ds=Day.values();
//向上转型Enum
Enum e = Day.MONDAY;
//无法调用,没有此方法
//e.values();
//获取class对象引用
Class<?> clasz = e.getDeclaringClass();
if(clasz.isEnum()) {
Day[] dsz = (Day[]) clasz.getEnumConstants();
System.out.println("dsz:"+Arrays.toString(dsz));
}

/**
输出结果:
dsz:[MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY]
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

正如上述代码所展示,通过Enum的class对象的getEnumConstants方法,我们仍能一次性获取所有的枚举实例常量.

部分摘转自:http://blog.csdn.net/javazejian/article/details/71333103
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java 枚举 笔记