JAVA提高二:枚举
JDK5.0中有一个非常有用的特性:枚举,这个特性以前在C语言中出现过,后来JDK出现后,开始觉得没有必要,但随着使用JAVA语言的人数增多,发现大家对枚举的需求非常大,于是又加入了此特性,下面我们来对枚举进行学习。
一、枚举的作用介绍
JDK5.0加入了一个全新类型的“类”——枚举类型。为此引入了一个新的关键字enum。可以这样来定义一个枚举类型:
public enum Color
{
RED,White,Blue
}
然后可以这样来使用:Color myColor = Color.Red;
枚举(enum),是指一个经过排序的、被打包成一个单一实体的项列表。一个枚举的实例可以使用枚举项列表中任意单一项的值。枚举在各个语言当中都有着广泛的应用,通常用来表示诸如颜色、方式、类别、状态等等数目有限、形式离散、表达又极为明确的量。作用简单来说就是:限定某个值得取值范围,就和我们在页面下拉列表一样,只能在有限的枚举值中选择,而不能跳出其范围,为说明枚举的用处,我们举个例子:
public class Entity {
private int id;
private int type; public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
}
上面定义了一个非常简单的实体类,其中类型采用的int类型来定义,当然,也不仅仅局限于int型,诸如char和String等也是不在少数。然而,无论使用什么样的类型,这样做都有很多的坏处。通常都是连续、有无穷多个值的量(比如例子中的实体int,那么setType的时候可以传入0 1 2 3 等等int),而类似这种表示类别type的量则是离散的,并且通常情况下只有有限个值。用连续的量去表示离散量,会产生很多问题。例如,针对上述的Entity类,如果要对Entity对象的type属性进行赋值,一般会采用如下方法:
Entity e = new Entity();
e.setId(10);
e.setType(2);
这样做的缺点有:(1)代码可读性差、易用性低。由于setType()方法的参数是int型的,在阅读代码的时候往往会让读者感到一头雾水,根本不明白这个2到底是什么意思,代表的是什么类型。当然,要保证可读性,还有这样一个办法就是定义一个常量类,然后常量类中定义如下:
public class Const
{
public static final int VIDEO = 1;//视频
public static final int AUDIO = 2;//音频
public static final int TEXT = 3;//文字
public static final int IMAGE = 4;//图片
}
然后:
e.setType(Const.AUDIO);
而这样的话,问题又来了。这样做,客户端必须对这些常量去建立理解,才能了解如何去使用这个东西。说白了,在调用的时候,如果用户不到Const类中去看看,还真不知道这个参数应该怎么传、怎么调。像是setType(2)这种用法也是在所难免,因为它完全合法,不是每个人都能够建立起用常量名代替数值,从而增加程序可读性、降低耦合性的意识。
(2)类型不安全。在用户去调用的时候,必须保证类型完全一致,同时取值范围也要正确。像是setType(-1)这样的调用是合法的,但它并不合理,今后会为程序带来种种问题。也许你会说,加一个有效性验证嘛,但是,这样做的话,又会引出下面的第(3)个问题。
(3)耦合性高,扩展性差。假如,因为某些原因,需要修改Const类中常量的值,那么,所有用到这些常量的代码也就都需要修改——当然,要仔细地修改,万一漏了一个,那可不是开玩笑的。同时,这样做也不利于扩展。例如,假如针对类别做了一个有效性验证,如果类别增加了或者有所变动,则有效性验证也需要做对应的修改,不利于后期维护。
如何来解决所遇到的问题呢,那就是采用枚举来进行解决,在解决前,我们先学习一些枚举的知识。
二、用普通类模拟枚举的实现原理
假设我们现在有一个要表示星期的想法,那么传统的方式和上面一样,我们会这样做:
package study.javaenhance; public class WeekDay
{
private int type; public int getType() {
return type;
} public void setType(int type) {
this.type = type;
} }
package study.javaenhance; public class EnumTest
{
public static void main(String[] args)
{ WeekDay weekDay = new WeekDay();
weekDay.setType(0); }
}
对于开发代码的人员而言,那么星期天,可能有些人用0 也会有些人用7 ,那么这个时候如果不熟悉业务代码就会出现错误现象,那么我们如果没有枚举类的情况,如何优化呢?
我们可以自己来实现枚举,如下:
package study.javaenhance; public class WeekDay
{
private WeekDay(){};//先把构造方法私有化,以防止外面可以new 出不同的对象来,从而没有办法达到约束的目的。 //定义可以选择的范围
public static final WeekDay SUN = new WeekDay(); public static final WeekDay MON = new WeekDay(); public WeekDay nextDay()
{
if(this == SUN){
return MON;
}else{
return SUN;
}
} public String toString()
{
return this==SUN?"SUN":"MON";
}
}
然后客户端在使用的时候,因为构造方法私有化了,那么只能选择已经列举好的实例对象,如下:
package study.javaenhance; public class EnumTest
{
public static void main(String[] args)
{ WeekDay weekDay = WeekDay.SUN;
System.out.println(weekDay.nextDay()); }
}
但是上面的代码存在一些缺陷,就是当类型很多的时候,那么就会存在大量的if else 的现象,如何来优化呢?如下:
package study.javaenhance; public abstract class WeekDay
{
private WeekDay(){};//先把构造方法私有化,以防止外面可以new 出不同的对象来,从而没有办法达到约束的目的。 //定义可以选择的范围
public static final WeekDay SUN = new WeekDay()
{ @Override
public WeekDay nextDay() {
// TODO Auto-generated method stub
return MON;
} }; public static final WeekDay MON = new WeekDay()
{ @Override
public WeekDay nextDay() {
// TODO Auto-generated method stub
return SUN;
} }; public abstract WeekDay nextDay();
/*public WeekDay nextDay()
{
if(this == SUN){
return MON;
}else{
return SUN;
}
}*/ public String toString()
{
return this==SUN?"SUN":"MON";
}
}
从上面的例子告诉我们,我们即将学习的枚举其实也一个类,只是一个特殊的类而已,继承了enum而已,具体的后面会讲到。
三、枚举的基本应用
使用枚举首先需要知道如何定义枚举,如下:
package study.javaenhance; public class EnumTest
{
public static void main(String[] args)
{ WeekDay1 weekDay = WeekDay1.SUN;
System.out.println(weekDay.nextDay()); WeekDay weekDay2 = WeekDay.MON;
System.out.println(weekDay2);
} public enum WeekDay
{
SUN,MON
}
}
关键字enum,枚举的基本使用如下:
package study.javaenhance; public class EnumTest
{
public static void main(String[] args)
{ WeekDay1 weekDay = WeekDay1.SUN;
System.out.println(weekDay.nextDay()); WeekDay weekDay2 = WeekDay.MON;
System.out.println(weekDay2);
sayDay(WeekDay.SUN);
} public enum WeekDay
{
SUN,MON;
} public static void sayDay(WeekDay weekDay)
{
switch(weekDay)
{
case SUN:
System.out.println("星期天");
break;
case MON:
System.out.println("星期一");
}
}
}
四、实现带有构造方法的枚举
结合我们自定义的枚举模拟例子和enum枚举,我们发现我们自定义类是可以加上构造方法来进行参数的传递,以便初始化值,那么枚举enum是否可以呢?
答案是肯定的,使用方法和我们自定义的枚举类似,如下:
package study.javaenhance; public class EnumTest
{
public static void main(String[] args)
{ WeekDay1 weekDay = WeekDay1.SUN;
System.out.println(weekDay.nextDay()); WeekDay weekDay2 = WeekDay.MON;
System.out.println(weekDay2);
sayDay(WeekDay.SUN);
} public enum WeekDay
{
SUN(1),MON(),TUE,WED,THI,FRI,SAT; //注意点,构造方法一定要私有化,其它和普通类的方式一样.
private WeekDay()
{
System.out.println("first");
}
private WeekDay(int day)
{
System.out.println("second");
} } public static void sayDay(WeekDay weekDay)
{
switch(weekDay)
{
case SUN:
System.out.println("星期天");
break;
case MON:
System.out.println("星期一");
}
}
}
枚举类型的静态方法:
values()返回枚举所有成员的数组
valueOf()将字符串转换成枚举值;当然需要在范围内的。
package study.javaenhance; public class EnumTest
{
public static void main(String[] args)
{ WeekDay1 weekDay = WeekDay1.SUN;
System.out.println(weekDay.nextDay()); WeekDay weekDay2 = WeekDay.MON;
System.out.println(weekDay2);
sayDay(WeekDay.SUN); //枚举类型提供了两个有用的静态方法values()和valueOf()
System.out.println(WeekDay.values().length);
System.out.println(WeekDay.valueOf("SUN").toString());
System.out.println(WeekDay.valueOf("aa").toString());//会抛异常,不在枚举的范围列表中
} public enum WeekDay
{
SUN(1),MON(),TUE,WED,THI,FRI,SAT; //注意点,构造方法一定要私有化,其它和普通类的方式一样.
private WeekDay()
{
System.out.println("first");
}
private WeekDay(int day)
{
System.out.println("second");
} } public static void sayDay(WeekDay weekDay)
{
switch(weekDay)
{
case SUN:
System.out.println("星期天");
break;
case MON:
System.out.println("星期一");
}
}
}
五、实现带有抽象方法的枚举
上面我们学习到枚举enum的基本使用,但是我们看到我们自己定义的枚举中可以定义抽象方法,然后去实现对应的功能,那么enum也是可以的,如下所示:
public enum WeekDay
{
SUN(1) {
@Override
public WeekDay nextDay() {
// TODO Auto-generated method stub
return MON;
}
},
MON() {
@Override
public WeekDay nextDay() {
// TODO Auto-generated method stub
return TUE;
}
},
TUE {
@Override
public WeekDay nextDay() {
// TODO Auto-generated method stub
return WED;
}
},
WED {
@Override
public WeekDay nextDay() {
// TODO Auto-generated method stub
return THI;
}
},
THI {
@Override
public WeekDay nextDay() {
// TODO Auto-generated method stub
return FRI;
}
},
FRI
{
@Override
public WeekDay nextDay() {
// TODO Auto-generated method stub
return SAT;
}
},
SAT {
@Override
public WeekDay nextDay() {
// TODO Auto-generated method stub
return SUN;
}
}; //注意点,构造方法一定要私有化,其它和普通类的方式一样.
private WeekDay()
{
System.out.println("first");
}
private WeekDay(int day)
{
System.out.println("second");
} public abstract WeekDay nextDay();
}
客户端调用:
public static void main(String[] args)
{ WeekDay1 weekDay = WeekDay1.SUN;
System.out.println(weekDay.nextDay()); WeekDay weekDay2 = WeekDay.MON;
System.out.println(weekDay2);
sayDay(WeekDay.SUN); //枚举类型提供了两个有用的静态方法values()和valueOf()
System.out.println(WeekDay.values().length);
System.out.println(WeekDay.valueOf("SUN").toString());
//System.out.println(WeekDay.valueOf("aa").toString());//会抛异常,不在枚举的范围列表中 //下一天
System.out.println(weekDay2.nextDay());
}
另外,我们可以看大在weekDay2.nextDay() 方法的时候会看到enum 提供一些自带的方法。如下:
//当前对象的名字
System.out.println(weekDay2.name());
//当前对象序号,从0开始
System.out.println(weekDay2.ordinal());
六、枚举的本质
讲枚举的本质前,再看一个例子:
public enum Coin
{
penny("hello"), nickle("world"), dime("welcome"), quarter("hello world"); private String value; public String getValue()
{
return value;
}
Coin(String value)
{
this.value = value;
} public static void main(String[] args)
{
Coin coin = Coin.quarter; System.out.println(coin.getValue());
} }
定义枚举类型时本质上就是在定义一个类别,只不过很多细节由编译器帮您完成,所以某种程度上,enum关键字的作用就像是class或interface。
当您使用“enum”定义枚举类型时,实质上您定义出来的类型比如class Coin继承自java.lang.Enum类型,而每个枚举的成员其实就是您定义的枚举类型的一个实例(Instance),它们都被预设为final,所以您无法改变它们,它们也是static成员,所以您可以通过类型名称直接使用它们,当然最重要的,它们都是公开的(public)。即枚举中的每个成员默认都是public static final的。
枚举的本质:每个枚举的成员其实就是您定义的枚举类型的一个实例(Instance)。
当定义了一个枚举类型后,在编译的时候就能够确定该枚举类型有多少个实例,这些对象的名字是什么。因为私有所以在运行期间无法再使用该枚举类型创建新的实例。
其他知识扩展:(使用频率较低)
EnumSet的名称说明了其作用,它是在J2SE 5.0后加入的新类别,可以协助您建立枚举值的集合,它提供了一系列的静态方法,可以让您指定不同的集合建立方式。
EnumSet有多重重载的of()方法,用于构造含有指定枚举对象的枚举集合。
与之对应的有complementOf()方法,返回某个枚举集合的补集。
noneOf()方法构造一个指定枚举类型的空枚举集合。之后可以用add()方法加入元素。
copyOf()方法有两种重载形式,一种形式的参数为枚举集合EnumSet,另一种方式的参数为Collection。说明可以利用集合来构造枚举集合,注意如果Collection中有重复元素,只有一个会被加入枚举集合。
下面的代码例子中使用了这些方法:
import java.util.List;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.Iterator; enum FontConstant
{
Plain, Bold, Italic, Hello,
} public class EnumSetDemo
{ public static void main(String[] args)
{
System.out.println("--------------of()-----------------");
// of()方法,构造含有指定元素的枚举集合
EnumSet<FontConstant> enumSet = EnumSet.of(FontConstant.Plain,
FontConstant.Bold);
showEnumSet(enumSet); System.out.println("------------complementOf()---------------");
// complementOf()方法,构造指定枚举集合的补集
showEnumSet(EnumSet.complementOf(enumSet)); System.out.println("--------------noneOf()-----------------");
// noneOf()方法构造一个指定枚举类型的空枚举集合
EnumSet<FontConstant> enumSet2 = EnumSet.noneOf(FontConstant.class);
enumSet2.add(FontConstant.Italic);
showEnumSet(enumSet2); System.out.println("---------------copyOf()------------------");
// copyOf()方法的一种重载可以由集合构造枚举集合 // 先构造一个List
List<FontConstant> list = new ArrayList<FontConstant>();
list.add(FontConstant.Bold);
list.add(FontConstant.Italic);
list.add(FontConstant.Plain);
list.add(FontConstant.Bold); // 然后使用copyO方法构造一个EnumSet
showEnumSet(EnumSet.copyOf(list)); } public static void showEnumSet(EnumSet<FontConstant> enumSet)
{
for (Iterator<FontConstant> iter = enumSet.iterator(); iter.hasNext();)
{
System.out.println(iter.next());
}
} }
EnumMap的声明是:Class EnumMap<K extends Enum<K>,V>,表明其中的Key是枚举类型。
使用例子如下:
import java.util.EnumMap;
import java.util.Map; public class EnumMapDemo
{
public static void main(String[] args)
{
Map<Action, String> map = new EnumMap<Action, String>(Action.class); map.put(Action.TURN_LEFT, "向左转");
map.put(Action.SHOOT, "射击");
map.put(Action.TURN_RIGHT, "向右转"); for (Action action : Action.values())
{
System.out.println(map.get(action)); }
}
} enum Action
{
TURN_LEFT, TURN_RIGHT, SHOOT,
}
七、解决前面提到的问题:
对于离散而固定选举的值,建议定义称为枚举类型而不要定义为常量,如下:
public enum TypeEnum {
VIDEO, AUDIO, TEXT, IMAGE
}
将实体类中的type类型修改如下:
public class Entity { private int id;
private TypeEnum type; public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
} public TypeEnum getType() {
return type;
}
public void setType(TypeEnum type) {
this.type = type;
}
}
这样客户端在赋予值得时候,就做到了限制,不能随便填入。
Entity e = new Entity();
e.setId(10);
e.setType(TypeEnum.AUDIO);
在调用setType()时,可选值只有四个,否则会出现编译错误,因此可以看出,枚举是类型安全的,不会出现取值范围错误的问题。同时,客户端不需要建立对枚举中常量值的了解,使用起来很方便,并且可以容易地对枚举进行修改,而无需修改客户端。如果常量从枚举中被删除了,那么客户端将会失败并且将会收到一个错误消息。枚举中的常量名称可以被打印,因此除了仅仅得到列表中项的序号外还可以获取更多信息。这也意味着常量可用作集合的名称,例如HashMap。
综上,我们可以看到,在JDK5中新引入的枚举完美地解决了之前通过常量来表示离散量所带来的问题,大大加强了程序的可读性、易用性和可维护性,并且在此基础之上又进行了扩展,使之可以像类一样去使用,更是为Java对离散量的表示上升了一个台阶。因此,如果在Java中需要表示诸如颜色、方式、类别、状态等等数目有限、形式离散、表达又极为明确的量,应当尽量舍弃常量表示的做法,而将枚举作为首要的选择。
JAVA提高二:枚举的更多相关文章
- JAVA提高二十:CopyOnWriteArrayList&CopyOnWriteArraySet&ConcurrentHashMap介绍
前面我们将java集合类的大部分类都进行了深入分析,但我们会发现一个共性问题就是并发的问题,那么如何解决呢?我们前面基本都是通过Collections的一个工具类来进行的解决,但实际大部分使用中人们普 ...
- Java 基础之-枚举
目录(?)[-] 用法一常量 用法二switch 用法三向枚举中添加新方法 用法四覆盖枚举的方法 用法五实现接口 用法六使用接口组织枚举 用法七关于枚举集合的使用 DK1.5引入了新的类型-- ...
- 【译】Java中的枚举
前言 译文链接:http://www.programcreek.com/2014/01/java-enum-examples/ Java中的枚举跟其它普通类很像,在其内部包含了一堆预先定义好的对象集合 ...
- java之enum枚举(2015年05月28日)
背景: 今天启动了一个新的项目,由于要从之前的旧项目中拿过来一些代码,所以就看了下公司之前项目代码,发现有定义的常量类,也有枚举类,然后就在想着两者的功能差不多,那他们之间到底有什么区别呢,所以就决定 ...
- 【转】Java基础笔记 – 枚举类型的使用介绍和静态导入--不错
原文网址:http://www.itzhai.com/java-based-notes-introduction-and-use-of-an-enumeration-type-static-impor ...
- Java中的枚举类型详解
枚举类型介绍 枚举类型(Enumerated Type) 很早就出现在编程语言中,它被用来将一组类似的值包含到一种类型当中.而这种枚举类型的名称则会被定义成独一无二的类型描述符,在这一点上和常量的定义 ...
- 黑马程序员:Java基础总结----枚举
黑马程序员:Java基础总结 枚举 ASP.Net+Android+IO开发 . .Net培训 .期待与您交流! 枚举 为什么要有枚举 问题:要定义星期几或性别的变量,该怎么定义?假设用1-7分别 ...
- Java中的枚举的治理
版权声明:本文为博主原创文章,转载请注明出处,欢迎使劲喷 一.为啥用枚举&为啥要对枚举进行治理 1.先来说说为啥用枚举 表中某个字段标识了这条记录的状态,我们往往使用一些code值来标识,例如 ...
- JAVA中的枚举类
某些情况下一个类的对象是有限而且固定的,例如性别就只有两个类(考虑大众情况).因此这种实例有限而且固定的类,java里面叫枚举类.枚举类的关键字是enum,一些基本的命名规则和文件命名等细节和一般的类 ...
随机推荐
- jvm系列 (五) ---类的加载机制
类的加载机制 目录 jvm系列(一):jvm内存区域与溢出 jvm系列(二):垃圾收集器与内存分配策略 jvm系列(三):锁的优化 jvm系列 (四) ---强.软.弱.虚引用 我的博客目录 什么是类 ...
- [C#] .Net Core 全局配置读取管理方法 ConfigurationManager
最近在学习.Net Core的过程中,发现.Net Framework中常用的ConfigurationManager在Core中竟然被干掉了. 也能理解.Core中使用的配置文件全是Json,不想F ...
- NWERC2016-Problem A(Arranging Hat)
Arranging Hat is a cushy job indeed; high impact work, absolute authority, and 364 days of holiday e ...
- c# HttpWebRequest 模拟HTTP post 传递JSON参数
//HTTP post JSON 参数 private string HttpPost(string Url, Object ticket) { ...
- js page click
DataTables Editor Your account: Login / Register Examples Manual Reference Options API Events Butt ...
- 避免subList/subString陷阱
避免subList/subString陷阱 java.util.List 接口提供了一个实例方法 List<E> subList(int fromIndex, int toIndex), ...
- Java Collections 源码分析
Java Collections API源码分析 侯捷老师剖析了不少Framework,如MFC,STL等.侯老师有句名言: 源码面前,了无秘密 这句话还在知乎引起广泛讨论. 我对教授程序设计的一点想 ...
- 团队作业4--第一次项目冲刺(Alpha版本)7
一.Daily Scrum Meeting照片 二.燃尽图 三.项目进展 1.完成全部基础功能 2.完成一些小改进与优化 四.困难与问题 软件基本是可以运行并且正常使用,但还没有实战过,遇到的问题与困 ...
- 201521123082 《Java程序设计》第12周学习总结
201521123082 <Java程序设计>第12周学习总结 标签(空格分隔): java 1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多流与文件相关内容. An ...
- 201521123005 《java程序设计》 第六周学习总结
1. 本周学习总结 1.1 面向对象学习暂告一段落,请使用思维导图,以封装.继承.多态为核心概念画一张思维导图,对面向对象思想进行一个总结. 注1:关键词与内容不求多,但概念之间的联系要清晰,内容覆盖 ...