对于大部分面向对象编程来说,类和接口是完全够用的,但是一些特殊情况下,这些方法就不适合。例如:想定义一个Color类,它只能有Red,Green,Blue3个值,其他的任何值都是非法的,如果采用传统的构造是极其麻烦的,另外还有一些不安全的问题。引入枚举(Enum)就能避免这些问题。对于大部分面向对象编程来说,类和接口是完全够用的,但是一些特殊情况下,这些方法就不适合。
例如:想定义一个Color类,它只能有Red,Green,Blue3个值,其他的任何值都是非法的,如果采用传统的构造是极其麻烦的,另外还有一些不安全的问题。引入枚举(Enum)就能避免这些问题。
1,使用简单程序完成枚举的功能
使用类完成枚举的功能:
Java
package Test;
public class Color {
public static final Color RED = new Color("红色");
public static final Color GREEN = new Color("绿色");
public static final Color BLUE = new Color("蓝色");
private String name;
public Color(String name) {
this.name = name;
}
}
将Color类中的构造方法私有,之后在类中准备了若干个实例化对象,以后如果要取得Color类的实例,则只能在RED、GREEN、BLUE3个对象,这样有效限制了对象的取值范围。
使用接口完成枚举的功能
Java
package Test;
public interface Color {
public static final int RED=1;
public static final int GREEN=2;
public static final int BLUE=3;
}
使用上述方法定义,存在如下问题:
- 类型不安全:因为上面的每个颜色实际上是一个int型,两个颜色常量相加的结果,会让人感到迷惑,操作不明确。
- 没有命名空间:可能存在其他类中的静态常量与之混淆。
- 打印意义不明:当输出某个颜色时,实际上输出的确实数字。
2,定义一个枚举类型
Java 5 新增了一个enum关键字(与class、interface关键字地位相同),用以定义枚举类。枚举类是一个特殊的类,它一样可以有自己的成员变量、方法,可以实现一个或多个接口,也可以定义自己的构造器。一个Java源文件中最多只能定义一个public访问权限的枚举类,且该Java源文件也必须和该枚举类的类名相同。
Java
package Test;
public enum Color {
RED,GREEN,BLUE;
}
(1)枚举值可以直接输出。
Java
for (Color c:Color.values()){
System.out.println(c);
}
(2)枚举类型的数据也可以使用"枚举.values()"的形式,将全部的枚举类型变为对象数组的形式,之后使用foreach进行输出。
(3)枚举中的每个类型可以使用switch进行判断。
Java
for (Color c:Color.values()){
switch (c){
case BLUE:System.out.println("这里是蓝色");break;
case RED:System.out.println("这里是红色");break;
case GREEN:System.out.println("这里是绿色");break;
}
}
枚举类与普通类的区别:
- 枚举类可以实现一个或多个接口,使用enum定义的枚举类默认继承了java.lang.Enum类,而不是默认继承Object类,因此枚举类不能显式继承其他父类。其中java.lang.Enum实现了java.lang.Searializable和java.lang.Comparable两个接口。
- 使用enum定义、非抽象的枚举类默认会使用final修饰。
- 枚举类的构造器只能使用private访问修饰符,如果省略,依旧是private修饰。由于子类构造器总要调用父类构造器一次,因此枚举类不能派生子类。
- 枚举类的所有实例必须在枚举类的第一行显示列出,否则这个枚举类永远都不能产生实例。列出这些实例,系统会自动添加public static final修饰,无须显式添加。
3,深入理解枚举(Enum)
java.lang.Enum类的定义如下:
Java
public abstract class Enum<E extends Enum<E>> extends Object implements Comparable<E>,java.io.Serializable
从Enum类的定义可以看出来,此类实现了Comparable和Serializable两个接口,证明枚举类型可以使用比较器或进行序列化操作。
枚举类的主要操作方法:
方法 | 类型 | 描述 |
---|---|---|
protected Enum(String name,int ordinal) | 构造 | 接收枚举的名称和枚举的常量创建枚举对象 |
protected final Object clone() throws CloneNotSupportException | 普通 | 克隆枚举对象 |
public final int compareTo(E o) | 普通 | 对象的比较 |
public final boolean equals(Object other) | 普通 | 比较两个枚举对象 |
public final int hashCode() | 普通 | 返回枚举常量的哈希码 |
public final String name() | 普通 | 返回枚举的名称 |
public final int ordinal() | 普通 | 返回枚举常量的序数 |
public static <T extends Enum> T valueOf (Class enumType,String name) | 普通 | 返回带指定名称的指定枚举类型的枚举常量 |
取得枚举的信息:在枚举建立完成之后,实际上都会调用Enum类中的构造方法,为其赋值。在Enum类的构造方法中的第一个参数name就是定义的枚举的名称,第二个参数ordinal则会从0开发依次进行编号。之后可以使用Enum类中提供的name()和ordinal()方法取得名称和编号。
为每一个枚举对象属性赋值:
- 通过构造方法为属性赋值:每个枚举类中都有其指定好的若干对象,每一个枚举值对象中也可以包含多个属性。而这些属性也可以通过构造方法为其赋值。
Java
public enum Color {
RED("红色"),GREEN("绿色"),BLUE("蓝色");
private Color(String name){
this.setName(name);
}
private String name;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
---------------------------------
for(Color c:Color.values()){
System.out.println(c.ordinal()+"---->"+c.name()+"---->"+c.getName());
}
=================================
0---->RED---->红色
1---->GREEN---->绿色
2---->BLUE---->蓝色
- 通过setter方法为属性赋值:通过调用setter()的方式为指定的属性进行赋值。但是这样一来就必须明确每一个枚举类的对象,如果操作的对象是RED,则名字应该是"红色";如果操作的对象是GREEN,则名字应该是"绿色"等。
Java
public enum Color {
RED,GREEN,BLUE;
private String name;
public void setName(String name) {
switch (this){
case GREEN:{
if ("绿色".equals(name)){
this.name = name;
}else{
System.out.println("设置内容出错了!");
}
break;
}
case RED:{
if ("红色".equals(name)){
this.name = name;
}else{
System.out.println("设置内容出错了!");
}
break;
}
case BLUE:{
if ("蓝色".equals(name)){
this.name = name;
}else{
System.out.println("设置内容出错了!");
}
break;
}
}
}
public String getName() {
return name;
}
}
Java
public static void main(String[] args) throws BeansException {
Color c = Color.BLUE;
c.setName("兰色");
c.setName("蓝色");
System.out.println(c.getName());
}
=========================================
设置内容出错了!
蓝色
- 直接调用:如果不想使用通过"枚举类.valueOf()"的形式取得每一个枚举类的对象,可以使用Enum类定义的"枚举类.valueOf()"方法显式调用。
Java
Color c = Enum.valueOf(Color.class,"BLUE");
使用比较器 :在Enum类的定义中已经实现好了Comparable接口,所以枚举类的内容本身是可以进行排序的。
Java
enum Color{
RED,GREEN,BLUE;
}
public class Test {
public static void main(String[] args) throws BeansException {
Set<Color> t = new TreeSet<Color>();
t.add(Color.GREEN);
t.add(Color.BLUE);
t.add(Color.RED);
Iterator<Color> iterator = t.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next()+"---");
}
}
}
==================================
RED---
GREEN---
BLUE---
加入的顺序与输出顺序不同,证明已经排序过了,是使用Enum类中的ordinal属性排序的。
4,类集对枚举的支持
JDK1.5的java.util程序包中提供了两个新的集合操作类:EnumMap和EnumSet,这两个类与枚举类型的结合应用可使以前非常烦琐的程序变得简单方便。
EnumMap:EnumMap是Map接口的子类,所以本身还是以Map的形式进行操作,即"key------value"。如果要使用EnumMap,则首先应该常见EnumMap的对象,在创建此对象时必须指定要操作的枚举类型。
Java
enum Color{
RED,GREEN,BLUE;
}
public class Test {
public static void main(String[] args) throws BeansException {
Map<Color,String> desc = new EnumMap<Color, String>(Color.class);
desc.put(Color.RED,"红色");
desc.put(Color.GREEN,"绿色");
desc.put(Color.BLUE,"蓝色");
for (Color c:Color.values()){
System.out.println(c.name()+"--->"+desc.get(c));
}
}
}
===========================================
RED--->红色
GREEN--->绿色
BLUE--->蓝色
EnumSet:EnumSet是Set接口的子类,所以里面的内容是无法重复的。使用EnumSet时不能直接使用关键词new为其实例化,而是使用本类中提供的静态方法。
方法 | 描述 |
---|---|
public static<E extends Enum> EnumSet allOf(Class elementType) | 将枚举中的全部内容设置到EnumSet中 |
public static<E extends Enum> EnumSet of(E first,E...rest) | 创建一个包含枚举指定内容的EnumSet对象 |
public static<E extends Enum> EnumSet copyOf(Collection e) | 创建一个从指定Collection中指定的EnumSet对象 |
public static<E exends Enum> EnumSet complementOf(EnumSet s) | 创建一个其元素类型与指定枚举set相同的枚举set,最初包含指定集合中所包含的此类型的所有元素 |
public static<E extends Enum> EnumSet noneOf(Class elementType) | 创建一个可以接收指定类的空集合 |
(1)将全部的集合设置到EnumSet集合中
Java
EnumSet<Color> es = EnumSet.allOf(Color.class);
for (Color c:es){
System.out.print(c+"、");
}
=========================
RED、GREEN、BLUE、
(2)只设置一个枚举的类型到集合中
Java
EnumSet<Color> es = EnumSet.of(Color.BLUE);
for (Color c:es){
System.out.println(c+"、");
}
================================
BLUE、
(3)创建只能放入指定枚举类型的集合
Java
EnumSet<Color> es = EnumSet.noneOf(Color.class);
es.add(Color.BLUE);
es.add(Color.GREEN);
for (Color c:es){
System.out.println(c+"、");
}
(4)创建不包含指定元素的集合
Java
EnumSet<Color> esOld = EnumSet.noneOf(Color.class);
esOld.add(Color.BLUE);
esOld.add(Color.GREEN);
EnumSet<Color> esNew = EnumSet.complementOf(esOld);
for (Color c:esNew){
System.out.println(c+"、");
}
========================
RED、
(5)复制已有的内容
Java
EnumSet<Color> esOld = EnumSet.noneOf(Color.class);
esOld.add(Color.BLUE);
esOld.add(Color.GREEN);
EnumSet<Color> esNew = EnumSet.copyOf(esOld);
for (Color c:esNew){
System.out.println(c+"、");
}
5,让枚举类实现一个接口
枚举类也可以实现一个接口,但是因为接口中会存在抽象方法,所以枚举类中的每个对象都必须分别实现接口中的抽象方法。
Java
interface Print(){
public String getColor();
}
enum Color implements Print{
RED,GREEN,BLUE;
public String getColor() {
RED{
public String getColor(){
return "红色";
}
},
BLUE{
public String getColor(){
return "蓝色";
}
},
GREEN{
public String getColor(){
return "绿色";
}
};
}
}
6,在枚举中定义抽象方法
Java
enum Color implements Print {
RED {
public String getColor() {
return "红色";
}
},
BLUE {
public String getColor() {
return "蓝色";
}
},
GREEN {
public String getColor() {
return "绿色";
}
};
public abstract String getColor();
}