什么是枚举?
枚举是JDK5引入的新特性。在某些情况下,一个类的对象是固定的,就可以定义为枚举。在实际使用中,枚举类型也可以作为一种规范,保障程序参数安全。枚举有以下特点:
- Java中枚举和类、接口的级别相同。
- 枚举和类一样,都有自己的属性、方法、构造方法,不同点是:枚举的构造方法只能是private修饰,也就无法从外部构造对象。构造方法只在构造枚举值时调用。
-
使用enum关键字声明一个枚举类型时,就默认继承自Java中的
java.lang.Enum
类,并实现了java.lang.Seriablizable
和java.lang.Comparable
两个接口。 -
所有的枚举值都是
public static final
的,且非抽象的枚举类不能再派生子类。 - 枚举类的所有实例(枚举值)必须在枚举类的第一行显式地列出,否则这个枚举类将永远不能产生实例。
- 判断枚举是否相同时,使用 == 和 equals 是一样的。
下面是 java.lang.Enum
类中的 equals()
:
1
2
3
4
|
// 这里是final修饰的,不允许子类重写
public final boolean equals(Object other) {
return this ==other;
}
|
枚举的常用方法
int compareTo(E o)
比较此枚举与指定对象的顺序。在该对象小于、等于或大于指定对象时,分别返回负整数、零或正整数。 枚举常量只能与相同枚举类型的其他枚举常量进行比较。
1
2
3
4
5
6
7
8
9
|
// Enum 中的源码
public final int compareTo(E o) {
Enum other = (Enum)o;
Enum self = this ;
if (self.getClass() != other.getClass() && // optimization
self.getDeclaringClass() != other.getDeclaringClass())
throw new ClassCastException();
return self.ordinal - other.ordinal;
}
|
String name()
返回此枚举实例的名称。
static values()
返回一个包含全部枚举值的数组,可以用来遍历所有枚举值。
String toString()
返回此枚举实例的名称,即枚举值。与 name()
一样。
1
2
3
4
5
6
7
|
// Enum 中 name() 和 toString()
public String toString() {
return name;
}
public final String name() {
return name;
}
|
int ordinal()
返回枚举值在枚举类中的索引值(从0开始),即枚举值在枚举声明中的顺序,这个顺序根据枚举值声明的顺序而定。
<T extends Enum<T>> valueOf()
返回带指定名称的指定枚举类型的枚举常量,名称必须与在此类型中声明枚举常量所用的标识符完全匹配(不允许使用额外的空白字符)。这个方法与toString相对应,因此重写 toString()
方法,一定要重写 valueOf()
方法(我们可以重写 toString()
方法,但不能自己重写 valueOf()
方法,当我们重写 toString()
方法时,valueOf()
方法会自动重写,不用我们理会。)
枚举的应用
枚举是一种特殊的类型,其用法和普通的类使用非常相似。
代替一组常量
1
2
3
|
public enum Color {
RED, GREEN, BLANK, YELLOW
}
|
switch 语句中使用
1
2
3
4
5
6
7
8
9
10
11
12
|
// JDK1.6 中switch加入了对枚举的支持
enum Signal {
GREEN, YELLOW, RED
}
...
switch (color) {
case RED:
color = Signal.GREEN;
break ;
}
...
|
向枚举中添加方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
public enum Color {
RED( "红色" ), GREEN( "绿色" ), BLANK( "白色" ), YELLO( "黄色" );
// 成员变量
private String name;
// 构造方法
private Color(String name) {
this .name = name;
}
// get set 方法
public String getName() {
return name;
}
public void setName(String name) {
this .name = name;
}
}
|
实现接口
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public interface Behaviour {
void print();
}
public enum Color implements Behaviour{
RED( "红色" , 1 ), GREEN( "绿色" , 2 ), BLANK( "白色" , 3 ), YELLO( "黄色" , 4 );
//接口方法
@Override
public void print() {
System.out.println( this .index+ ":" + this .name);
}
}
|
包含抽象方法的枚举类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
public enum Operation {
// 用于执行加法运算
PLUS { // 花括号部分其实是一个匿名内部子类
@Override
public double calculate( double x, double y) {
return x + y;
}
},
// 用于执行减法运算
MINUS { // 花括号部分其实是一个匿名内部子类
@Override
public double calculate( double x, double y) {
// TODO Auto-generated method stub
return x - y;
}
},
// 用于执行乘法运算
TIMES { // 花括号部分其实是一个匿名内部子类
@Override
public double calculate( double x, double y) {
return x * y;
}
},
// 用于执行除法运算
DIVIDE { // 花括号部分其实是一个匿名内部子类
@Override
public double calculate( double x, double y) {
return x / y;
}
};
//为该枚举类定义一个抽象方法,枚举类中所有的枚举值都必须实现这个方法
public abstract double calculate( double x, double y);
}
|
使用枚举实现单例(单例的最佳实践)
好处:
1.利用的枚举的特性实现单例
2.由JVM保证线程安全
3.序列化和反射攻击已经被枚举解决
1
2
3
4
5
6
7
|
public enum Singleton {
INSTANCE;
public Singleton getInstance(){
// 增加这个方法是让别人明白怎么使用,因为这种实现方式还比较少见。
return INSTANCE;
}
}
|
其他关于枚举的使用
EnumSet
range(E from, E to)
从枚举值中获取一段范围的 Set。
1
2
3
|
for (WeekDayEnum day : EnumSet.range(WeekDayEnum.Mon, WeekDayEnum.Fri)) {
System.out.println(day);
}
|
of(E first, E... rest)
创建一个最初包含指定元素的枚举 Set。
noneOf(Class<E> elementType)
创建一个具有指定元素类型的空枚举 Set。
EnumMap
EnumMap(Class<K> keyType)
创建一个具有指定键类型的空枚举Map。
1
2
3
|
Map<Weather, String> enumMap = new EnumMap<Weather, String>(Weather. class );
enumMap.put(Weather.Sunny, "晴天" );
enumMap.put(Weather.Rainy, "雨天" );
|
Android中的枚举
Enum 需要占用较大的内存,如果对内存敏感,请尽量少使用 Enum,换成静态常量。
但是如果不使用枚举,会出现一些安全隐患,所以官方推出了两个注解,可以在编译时期进行类型检查,以此替代枚举。这两个注解分别是:@IntDef 和 @StringDef。位于compile 'com.android.support:support-annotations:+' 。
使用示例
@StringDef的使用与 @IntDef一致,这里以 @IntDef为例。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
public interface QRCodeType {
int WECHAT = 0 ;
int ALIPAY = 1 ;
@IntDef ({WECHAT , ALIPAY })
@Retention (RetentionPolicy.SOURCE)
@Target ({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
@interface Checker {
}
}
public class QRCode {
@QRCodeType .Checker // 定义在属性
private int type;
public void setType( @QRCodeType .Checker int type) { // 定义在参数
this .type= type;
}
@QRCodeType .Checker // 定义在方法(也就是检查返回值的类型)
public int getType() {
return type;
}
}
|
使用建议
开发中使用范围最广的就是利用枚举代替一组静态常量,这种情况可以使用以上注解方式替代。
当枚举还含有其它功能时(如:包含其它定义的方法),则不能替换。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。
原文链接:http://www.jianshu.com/p/6b7f715d06f6