枚举类型
在JavaSE5中添加了一个看似很小的特性,即enum关键字,它使得我们在需要群组并使用枚举类型集时,可以很方便地处理。
在此之前,你需要创建一个整型常量集,但是这些枚举值并不会必然地将其自身的取值限制在这个常量集的范围之内,因此它们显得更有风险,且更难以使用。
枚举类型属于非常普遍的需求,C、C++和其他许多语言都已经拥有它了。
在JavaSE5之前,Java程序员在需要使用枚举类型时,必须了解很多细节并需要格外仔细,以正确地产生enum的效果。
现在Java也有了enum,并且它的功能比C/C++中的枚举类型要完备得多。
下面是一个简单的例子:
//: initialization/Spiciness.java
public enum Spiciness {
NOT, MILD, MEDIUM, HOT, FLAMING
} ///:~
这里创建了一个名为Spiciness的枚举类型,它具有5个具名值。
由于枚举类型的实例是常量,因此按照命名惯例它们都用大写字母表示(如果在一个名字中有多个单词,用下划线将它们隔开)。
为了使用enum,需要创建一个该类型的引用,并将其赋值给某个实例:
//: initialization/SimpleEnumUse.java
public class SimpleEnumUse {
public static void main(String[] args) {
Spiciness howHot = Spiciness.MEDIUM;
System.out.println(howHot);
}
} /* Output:
MEDIUM
*///:~
在你创建enum时,编译器会自动添加一些有用的特性。
例如,它会创建toString()方法,以便你可以很方便地显示某个enum实例的名字,这正式上面的打印语句如何产生器输出的答案。
编译器还会创建ordinal()方法,用来表示某个特定enum常量的声明顺序,以及static vaues()方法,用来按照enum常量的声明顺序,产生由这些常量值构成的数组:
//: initialization/EnumOrder.java
public class EnumOrder {
public static void main(String[] args) {
for(Spiciness s : Spiciness.values())
System.out.println(s + ", ordinal " + s.ordinal());
}
} /* Output:
NOT, ordinal 0
MILD, ordinal 1
MEDIUM, ordinal 2
HOT, ordinal 3
FLAMING, ordinal 4
*///:~
尽管enum看起来像是一种新的数据类型,但是这个关键字只是为enum生成对应的类时,产生了某些编译器行为,因此在很大程度上,你可以将enum当作其他任何类来处理。
事实上,enum确实是类,并且具有自己的方法。
enum有一个特别实用的特性,即它可以在switch语句内使用:
//: initialization/Burrito.java
public class Burrito {
Spiciness degree;
public Burrito(Spiciness degree) { this.degree = degree;}
public void describe() {
System.out.print("This burrito is ");
switch(degree) {
case NOT: System.out.println("not spicy at all.");
break;
case MILD:
case MEDIUM: System.out.println("a little hot.");
break;
case HOT:
case FLAMING:
default: System.out.println("maybe too hot.");
}
}
public static void main(String[] args) {
Burrito
plain = new Burrito(Spiciness.NOT),
greenChile = new Burrito(Spiciness.MEDIUM),
jalapeno = new Burrito(Spiciness.HOT);
plain.describe();
greenChile.describe();
jalapeno.describe();
}
} /* Output:
This burrito is not spicy at all.
This burrito is a little hot.
This burrito is maybe too hot.
*///:~
由于switch是要在有限的可能值集合中进行选择,因此它与enum正是绝佳的组合。
请注意enum的命名能够倍加清楚地表明程序意欲何为的。
大体上,你可以将enum用作另外一种创建数据类型的方式,然后直接将所得到的类型拿来使用。
这正是关键所在,因此你不必过多地考虑它们。
在JavaSE5引进enum之前,你必须花费大量的精力去保证与其等价的枚举类型是安全可用的。
这些介绍对于你理解和使用基本的enum已经足够了,但是我们将在第19章中更加深入地探讨它们。
Summary
总结
构造器,这种精巧的初始化机制,应该给读者很强的暗示:初始化在Java中占有至关重要的地位。
C++的发明人Bjarne Stroustrup在设计C++期间,在针对C语言的生产效率所进行的最初调查中发现,大量编程错误都源于不正确的初始化。
这种错误很难发现,并且不恰当的清理也会导致类似问题。
因为构造器能保证正确的初始化和清理(没有正确的构造器调用,编译器就不允许创建对象),所以有了完全的控制,也很安全。
在C++中,析构(destruction)相当重要,因为用new创建的对象必须明确被销毁。
在Java中就不太需要了(不过当要用到的时候,你就只能自己动手了)。
在不需要类似析构函数的行为的时候,Java的垃圾回收器可以极大简化编程工作,而且在处理内存的时候也更安全。
有些垃圾回收器甚至能清理其他资源,比如图形和文件(file handles)。
然而,垃圾回收器确实也增加了运行时的开销。
而且Java解释器从来就很慢,所以这种开销到底造成了多大的影响也很难看出。
随着时间的推移,Java在性能方面已经取得长足的进步,但速度问题仍然是它涉足某些特定编程领域的障碍。
由于要保证所有对象都被创建,构造器实际上要比这里所讨论的更复杂。
特别当通过组合(composition)或继承(inheritance)生成新类的时候,这种保证仍然成立,并且需要一些附加的语法来提供支持。
在后面的章节中,读者将学习到有关组合、集成以及它们对构造器造成的影响等方面的知识。