有的人说,不推荐使用枚举。有的人说,枚举很好用。究竟怎么使用,如何使用,仁者见仁智者见智。总之,先学会再说~html
为何要引入枚举类
一个小案例
你写了一个小程序,不过很久不用了,忽然有一天,你想使用一下它。程序要想正确运行,须要将今天星期几存到数据库里。这个时候,你开始犯难了。java
当初的你还很年轻,不懂程序界的险恶,设计这个程序的时候,傻不拉几把这个字段设计为int类型的,用0表明周日,1表明周一。。。6表明周六,添加的时候就setWeekday(0)。可是这么长时间没用了,你忘记本身是从周一开始计算仍是周日开始计算了,换句话说,你想不起来0表明的是周一仍是周日了!程序员
因而你各类翻代码,看数据库的字段,数据库保存的信息,终于搞懂了,你很开心,用了一次以后,以为这个程序没意思,又不用了。web
好久以后,你心血来潮,又想用一次它,很不幸,你又忘记到底0表明周一仍是周日了,一番查找以后。你决定重构代码,由于你受不了了!!数据库
静态变量来帮忙
通过一番思考,你决定使用七个静态变量来表明星期几,之后只要引用和静态变量就能够了,而不用本身输入012….你这么写:小程序
public class Weekday {
public final static int SUN =
0 ;
public final static int MON =
1 ;
public final static int TUE =
2 ;
public final static int WED =
3 ;
public final static int THU =
4 ;
public final static int FRI =
5 ;
public final static int SAT =
6 ;
}
机智如你,这个时候,只要Weekday.SUN
就能够了,不用操心到底应该填写0仍是填写1。数组
可是这个时候的你,也不是当初初出茅庐的小伙子了,很明显,这样写已经不能知足你了。你还想让这个类作更多的事,好比,你想知道下一天是星期几,还想把今天是星期几打印出来。一番深思熟虑后,你改为了这样:markdown
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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
public class Weekday {
private Weekday (){}
public final
static Weekday SUN =
new Weekday();
public final
static Weekday MON =
new Weekday();
public final
static Weekday TUE =
new Weekday();
public final
static Weekday WED =
new Weekday();
public final
static Weekday THU =
new Weekday();
public final
static Weekday FRI =
new Weekday();
public final
static Weekday SAT =
new Weekday();
public static Weekday
getNextDay (Weekday nowDay){
if (nowDay == SUN) {
return MON;
}
else if (nowDay == MON) {
return TUE;
}
else if (nowDay == TUE) {
return WED;
}
else if (nowDay == WED) {
return THU;
}
else if (nowDay == THU) {
return FRI;
}
else if (nowDay == FRI) {
return SAT;
}
else {
return SUN;
}
}
public static void printNowDay (Weekday nowDay){
if (nowDay == SUN)
System.
out .println(
"sunday" );
else if (nowDay == MON)
System.
out .println(
"monday" );
else if (nowDay == TUE)
System.
out .println(
"tuesday" );
else if (nowDay == WED)
System.
out .println(
"wednesday" );
else if (nowDay == THU)
System.
out .println(
"thursday" );
else if (nowDay == FRI)
System.
out .println(
"friday" );
else
System.
out .println(
"saturday" );
}
}
class Test1{
public static void main (String[] args) {
Weekday nowday = Weekday.SUN;
Weekday.printNowDay(nowday);
Weekday nextDay = Weekday.getNextDay(nowday);
System.
out .print(
"nextday ====> " );
Weekday.printNowDay(nextDay);
}
}
哟,不错。考虑的很详细。而且私有构造方法后,外界就不能建立该类的对象了,这样就避免了星期八星期九的出现,全部Weekday的对象都在该类内部建立。app
不对,好像缺了点什么,我要的是int!个人int呢?!。因此,你还须要一个这样的方法:框架
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static int toInt (Weekday nowDay){
if (nowDay == SUN)
return 0 ;
else if (nowDay == MON)
return 1 ;
else if (nowDay == TUE)
return 2 ;
else if (nowDay == WED)
return 3 ;
else if (nowDay == THU)
return 4 ;
else if (nowDay == FRI)
return 5 ;
else
return 6 ;
}
当你须要一个整形数据的时候,只须要Weekday.toInt(Weekday.SUN);
,看起来你好像完成了你的任务。
可是,你有没有发现,这样写,好麻烦啊。若是想要扩展一下功能,大量的ifelse会让人眼花缭乱。
有没有更好的方式呢?你大概已经知道了,没错,咱们须要枚举类!
咱们先来看看枚举类是什么。
一个简单的枚举类
话很少说,先来代码:
public enum Weekday {
SUN,MON,TUS,WED,THU,FRI,SAT
}
代码这么少?
没错,这就是枚举类,咱们来看看怎么使用它:
class Test2{
public static void main (String[] args) {
Weekday sun = Weekday.SUN;
System.out.println(sun);
}
}
看起来和上面的静态变量使用方式差很少,并且默认的toString方法返回的就是对应的名字。
咱们上面的那段代码重写toString也是不能够打印出当前是星期几的,由于toString方法没有参数。因此咱们本身写了一个printNowDay方法。
固然,这么简单的枚举类是不可能实现咱们的要求的,因此,咱们还要接着写:
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
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
public enum Weekday {
SUN(
0 ),MON(
1 ),TUS(
2 ),WED(
3 ),THU(
4 ),FRI(
5 ),SAT(
6 );
private int value ;
private Weekday (
int value ){
this .
value =
value ;
}
public static Weekday
getNextDay (Weekday nowDay){
int nextDayValue = nowDay.
value ;
if (++nextDayValue ==
7 ){
nextDayValue =
0 ;
}
return getWeekdayByValue(nextDayValue);
}
public static Weekday
getWeekdayByValue (
int value ) {
for (Weekday c : Weekday.values()) {
if (c.
value ==
value ) {
return c;
}
}
return null ;
}
}
class Test2{
public static void main (String[] args) {
System.
out .println(
"nowday ====> " + Weekday.SAT);
System.
out .println(
"nowday int ====> " + Weekday.SAT.ordinal());
System.
out .println(
"nextday ====> " + Weekday.getNextDay(Weekday.SAT));
}
}
这样就完成了咱们的目标,和以前的代码比起来,有没有以为忽然高大上了许多?没有那么多烦人的ifelse,世界都清净了。
好了,如今你大概知道为何要引入枚举类了吧?就是由于在没有枚举类的时候,咱们要定义一个有限的序列,好比星期几,男人女人,春夏秋冬,通常会经过上面那种静态变量的形式,可是使用那样的形式若是须要一些其余的功能,须要些不少奇奇怪怪的代码。因此,枚举类的出现,就是为了简化这种操做。
能够将枚举类理解为是java的一种语法糖。
枚举类的用法
最简单的使用
最简单的枚举类就像咱们上面第一个定义的枚举类同样:
public enum Weekday {
SUN,MON,TUS,WED,THU,FRI,SAT
}
如何使用它呢?
先来看看它有哪些方法:
这是Weekday能够调用的方法和参数。发现它有两个方法:value()和valueOf()。还有咱们刚刚定义的七个变量。
这些事枚举变量的方法。咱们接下来会演示几个比较重要的:
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
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
public enum Weekday {
SUN,MON,TUS,WED,THU,FRI,SAT
}
class Test3{
public static void main (String[] args) {
System.out.println(Weekday.valueOf(
"mon" .toUpperCase()));
for (Weekday w : Weekday.values()){
System.out.println(w +
".ordinal() ====>" +w.ordinal());
}
System.out.println(
"Weekday.MON.compareTo(Weekday.FRI) ===> " + Weekday.MON.compareTo(Weekday.FRI));
System.out.println(
"Weekday.MON.compareTo(Weekday.MON) ===> " + Weekday.MON.compareTo(Weekday.MON));
System.out.println(
"Weekday.MON.compareTo(Weekday.SUM) ===> " + Weekday.MON.compareTo(Weekday.SUN));
System.out.println(
"Weekday.MON.name() ====> " + Weekday.MON.name());
}
}
这段代码,咱们演示了几个经常使用的方法和功能:
Weekday.valueOf() 方法:
它的做用是传来一个字符串,而后将它转变为对应的枚举变量。前提是你传的字符串和定义枚举变量的字符串一抹同样,区分大小写。若是你传了一个不存在的字符串,那么会抛出异常。
Weekday.values()方法。
这个方法会返回包括全部枚举变量的数组。在该例中,返回的就是包含了七个星期的Weekday[]。能够方便的用来作循环。
枚举变量的toString()方法。
该方法直接返回枚举定义枚举变量的字符串,好比MON就返回【”MON”】。
枚举变量的.ordinal()方法。
默认请款下,枚举类会给全部的枚举变量一个默认的次序,该次序从0开始,相似于数组的下标。而.ordinal()方法就是获取这个次序(或者说下标)
枚举变量的compareTo()方法。
该方法用来比较两个枚举变量的”大小”,实际上比较的是两个枚举变量的次序,返回两个次序相减后的结果,若是为负数,就证实变量1”小于”变量2 (变量1.compareTo(变量2),返回【变量1.ordinal() - 变量2.ordinal()】)
这是compareTo的源码,会先判断是否是同一个枚举类的变量,而后再返回差值。
枚举类的name()方法。
它和toString()方法的返回值同样,事实上,这两个方法原本就是同样的:
这两个方法的默认实现是同样的,惟一的区别是,你能够重写toString方法。name变量就是枚举变量的字符串形式。
还有一些其余的方法我就暂时不介绍了,感兴趣的话能够本身去看看文档或者源码,都挺简单的。
要点:
使用的是enum关键字而不是class。
多个枚举变量直接用逗号隔开。
枚举变量最好大写,多个单词之间使用”_”隔开(好比:INT_SUM)。
定义完全部的变量后,以分号结束,若是只有枚举变量,而没有自定义变量,分号能够省略(例如上面的代码就忽略了分号)。
在其余类中使用enum变量的时候,只须要【类名.变量名】就能够了,和使用静态变量同样。
可是这种简单的使用显然不能体现出枚举的强大,咱们来学习一下复杂的使用:
枚举的高级使用方法
就像咱们前面的案例同样,你须要让每个星期几对应到一个整数,好比星期天对应0。上面讲到了,枚举类在定义的时候会自动为每一个变量添加一个顺序,从0开始。
假如你但愿0表明星期天,1表明周一。。。而且你在定义枚举类的时候,顺序也是这个顺序,那你能够不用定义新的变量,就像这样:
public enum Weekday {
SUN,MON,TUS,WED,THU,FRI,SAT
}
这个时候,星期天对应的ordinal值就是0,周一对应的就是1,知足你的要求。可是,若是你这么写,那就有问题了:
public enum Weekday {
MON,TUS,WED,THU,FRI,SAT,SUN
}
我吧SUN放到了最后,可是我仍是希0表明SUN,1表明MON怎么办呢?默认的ordinal是期望不上了,由于它只会傻傻的给第一个变量0,给第二个1。。。
因此,咱们须要本身定义变量!
看代码:
public enum Weekday {
MON(
1 ),TUS(
2 ),WED(
3 ),THU(
4 ),FRI(
5 ),SAT(
6 ),SUN(
0 );
private int value;
private Weekday (
int value){
this .value = value;
}
}
咱们对上面的代码作了一些改变:
首先,咱们在每一个枚举变量的后面加上了一个括号,里面是咱们但愿它表明的数字。
而后,咱们定义了一个int变量,而后经过构造函数初始化这个变量。
你应该也清楚了,括号里的数字,其实就是咱们定义的那个int变量。这句叫作自定义变量。
请注意:这里有三点须要注意:
必定要把枚举变量的定义放在第一行,而且以分号结尾。
构造函数必须私有化。事实上,private是多余的,你彻底没有必要写,由于它默认并强制是private,若是你要写,也只能写private,写public是不能经过编译的。
自定义变量与默认的ordinal属性并不冲突,ordinal仍是按照它的规则给每一个枚举变量按顺序赋值。
好了,你很聪明,你已经掌握了上面的知识,你想,既然能自定义一个变量,能不能自定义两个呢?
固然能够:
public enum Weekday {
MON(
1 ,
"mon" ),TUS(
2 ,
"tus" ),WED(
3 ,
"wed" ),THU(
4 ,
"thu" ),FRI(
5 ,
"fri" ),SAT(
6 ,
"sat" ),SUN(
0 ,
"sun" );
private int value ;
private String label;
private Weekday (
int value ,String label){
this .
value =
value ;
this .label = label;
}
}
你能够定义任何你想要的变量。学完了这些,大概枚举类你也应该掌握了,可是,还有没有其余用法呢?
枚举类中的抽象类
若是我在枚举类中定义一个抽象方法会怎么样?
你要知道,枚举类不能继承其余类,也不能被其余类继承。至于为何,咱们后面会说到。
你应该知道,有抽象方法的类必然是抽象类,抽象类就须要子类继承它而后实现它的抽象方法,可是呢,枚举类不能被继承。。你是否是有点乱?
咱们先来看代码:
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
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
public enum TrafficLamp {
RED(
30 ) {
@Override
public TrafficLamp
getNextLamp () {
return GREEN;
}
}, GREEN(
45 ) {
@Override
public TrafficLamp
getNextLamp () {
return YELLOW;
}
}, YELLOW(
5 ) {
@Override
public TrafficLamp
getNextLamp () {
return RED;
}
};
private int time;
private TrafficLamp (
int time) {
this .time = time;
}
public abstract TrafficLamp
getNextLamp ();
}
你好像懂了点什么。可是你好像又不太懂。为何一个变量的后边能够带一个代码块而且实现抽象方法呢?
别着急,带着这个疑问,咱们来看一下枚举类的实现原理。
枚举类的实现原理
从最简单的看起:
public enum Weekday {
SUN,MON,TUS,WED,THU,FRI,SAT
}
仍是这段熟悉的代码,咱们编译一下它,再反编译一下看看它究竟是什么样子的:
你是否是以为很熟悉?反编译出来的代码和咱们一开始用静态变量本身写的那个类出奇的类似!
并且,你看到了熟悉的values()方法和valueOf()方法。
仔细看,这个类继承了java.lang.Enum类!因此说,枚举类不能再继承其余类了,由于默认已经继承了Enum类。
而且,这个类是final的!因此它不能被继承!
回到咱们刚才的那个疑问:
RED(
30 ) {
@Override
public TrafficLamp
getNextLamp () {
return GREEN;
}
}
为何会有这么神奇的代码?如今你差很少懂了。由于RED自己就是一个TrafficLamp对象的引用。实际上,在初始化这个枚举类的时候,你能够理解为执行的是TrafficLamp RED = new TrafficLamp(30)
,可是由于TrafficLamp里面有抽象方法,还记得匿名内部类么?
咱们能够这样来建立一个TrafficLamp引用:
TrafficLamp RED =
new TrafficLamp(
30 ){
@Override
public TrafficLamp
getNextLamp () {
return GREEN;
}
};
而在枚举类中,咱们只须要像上面那样写【RED(30){}
】就能够了,由于java会自动的去帮咱们完成这一系列操做。
若是你仍是不太理解,那么你能够本身去反编译一下TrafficLamp这个类,看看jvm是怎么处理它的就明白了。
枚举类的其余用法
说一说枚举类的其余用法。
switch语句中使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
enum Signal {
GREEN, YELLOW, RED
}
public class TrafficLight {
Signal color = Signal.RED;
public void change () {
switch (color) {
case RED:
color = Signal.GREEN;
break ;
case YELLOW:
color = Signal.RED;
break ;
case GREEN:
color = Signal.YELLOW;
break ;
}
}
}
实现接口
虽然枚举类不能继承其余类,可是仍是能够实现接口的
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
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
public interface Behaviour {
void print();
String getInfo();
}
public enum Color implements Behaviour {
RED(
"红色" ,
1 ), GREEN(
"绿色" ,
2 ), BLANK(
"白色" ,
3 ), YELLO(
"黄色" ,
4 );
private String name;
private int index;
private Color (String name,
int index) {
this .name = name;
this .index = index;
}
@Override
public String
getInfo () {
return this .name;
}
@Override
public void print () {
System.
out .println(
this .index +
":" +
this .name);
}
}
使用接口组织枚举
public interface Food {
enum Coffee implements Food {
BLACK_COFFEE, DECAF_COFFEE, LATTE, CAPPUCCINO
}
enum Dessert implements Food {
FRUIT, CAKE, GELATO
}
}
使用枚举建立单例模式
使用枚举建立的单例模式:
public enum EasySingleton{
INSTANCE;
}
代码就这么简单,你可使用EasySingleton.INSTANCE调用它,比起你在单例中调用getInstance()方法容易多了。
咱们来看看正常状况下是怎样建立单例模式的:
用双检索实现单例:
下面的代码是用双检索实现单例模式的例子,在这里getInstance()方法检查了两次来判断INSTANCE是否为null,这就是为何叫双检索的缘由,记住双检索在java5以前是有问题的,可是java5在内存模型中有了volatile变量以后就没问题了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class DoubleCheckedLockingSingleton{
private volatile DoubleCheckedLockingSingleton INSTANCE;
private DoubleCheckedLockingSingleton (){}
public DoubleCheckedLockingSingleton
getInstance (){
if (INSTANCE ==
null ){
synchronized(DoubleCheckedLockingSingleton.class){
if (INSTANCE ==
null ){
INSTANCE =
new DoubleCheckedLockingSingleton();
}
}
}
return INSTANCE;
}
}
你能够访问DoubleCheckedLockingSingleTon.getInstance()来得到实例对象。
用静态工厂方法实现单例:
public class Singleton{
private static final Singleton INSTANCE =
new Singleton();
private Singleton (){}
public static Singleton
getSingleton (){
return INSTANCE;
}
}
你能够调用Singleton.getInstance()方法来得到实例对象。
上面的两种方式就是懒汉式和恶汉式单利的建立,可是不管哪种,都不如枚举来的方便。并且传统的单例模式的另一个问题是一旦你实现了serializable接口,他们就再也不是单例的了。可是枚举类的父类【Enum类】实现了Serializable接口,也就是说,全部的枚举类都是能够实现序列化的,这也是一个优势。
总结
最后总结一下:
能够建立一个enum类,把它看作一个普通的类。除了它不能继承其余类了。(java是单继承,它已经继承了Enum),能够添加其余方法,覆盖它自己的方法
switch()参数可使用enum
values()方法是编译器插入到enum定义中的static方法,因此,当你将enum实例向上转型为父类Enum是,values()就不可访问了。解决办法:在Class中有一个getEnumConstants()方法,因此即使Enum接口中没有values()方法,咱们仍然能够经过Class对象取得全部的enum实例
没法从enum继承子类,若是须要扩展enum中的元素,在一个接口的内部,建立实现该接口的枚举,以此将元素进行分组。达到将枚举元素进行分组。
enum容许程序员为eunm实例编写方法。因此能够为每一个enum实例赋予各自不一样的行为。
本文到这里就差很少结束了。可能举得例子不是很恰当,代码写的不是很优雅,不过我只是用来引出枚举的,你们不要鸡蛋里头挑骨头哈哈。
若是文章内容有什么问题,请及时与我联系。
除此以外,还有两个枚举集合:【java.util.EnumSet和java.util.EnumMap】没有讲。关于枚举集合的使用会在后面讲集合框架的时候再详细讲解。
本文地址:http://blog.csdn.net/qq_31655965/article/details/55049192 原创自csdn:http://blog.csdn.net/qq_31655965 博主:clever_fan
参考资料: http://www.cnblogs.com/happyPawpaw/archive/2013/04/09/3009553.html https://my.oschina.net/zhoujy/blog/134958