在学枚举的时候,看到这样定义枚举感到很奇怪。
public enum WeekDay {
SUN,MON,TUE,WED,THI,FRI,SAT
}
感觉像一个类,但又不是类。。
想看一下这个被编译过的字节码文件里都是什么内容。。
javac WeekDay.java
在被编译后生成了WeekDay.class文件。
然后反编译javap WeekDay
Compiled from "WeekDay.java"
public final class WeekDay extends java.lang.Enum<WeekDay> {
public static final WeekDay SUN;
public static final WeekDay MON;
public static final WeekDay TUE;
public static final WeekDay WED;
public static final WeekDay THI;
public static final WeekDay FRI;
public static final WeekDay SAT;
public static WeekDay[] values();
public static WeekDay valueOf(java.lang.String);
static {};
}
会发现原来public enum WeekDay被编译器编译成了
public final class WeekDay extends java.lang.Enum<WeekDay>
所以枚举就相当于一个类,而且是final 的最终类。继承自java.lang.Enum。
枚举值SUN被编译器编译成了
public static final WeekDay SUN; 这是一个静态常量,类型是WeekDay。
--------------
那么静态常量的初始化在哪里?
查看Java API会发现java.lang.Enum有个构造函数
构造方法摘要 |
|
protected |
既然WeekDay 是个枚举类,肯定继承了java.lang.Enum。
WeekDay是Enum的子类,子类能调用父类的构造函数进行初始化。
枚举类型的每一个值都将映射到 protected Enum(String name, int ordinal) 构造函数中,在这里,每个值的名称都被转换成一个字符串,并且序数设置表示了此设置被创建的顺序。
public enum WeekDay {
SUN,MON,TUE,WED,THI,FRI,SAT
}
这段代码实际上调用了7次 Enum(String name, int ordinal):
new Enum<WeekDay>("SUN",0); new Enum<WeekDay>("MON",1); new Enum<WeekDay>("TUE",2); ... ... |
可以把 enum 看成是一个普通的 class,它们都可以定义一些属性和方法。
不同之处是:enum 不能使用 extends 关键字继承其他类,因为 enum 已经继承了 java.lang.Enum(java是单一继承)。
WeekDay继承了java.lang.Enum,当然也能使用Enum的方法。
方法摘要 |
||
protected Object |
clone() |
|
int |
||
boolean |
||
protected void |
finalize() |
|
getDeclaringClass() |
||
int |
hashCode() |
|
name() |
||
int |
ordinal() |
|
toString() |
||
static
|
valueOf(Class<T> enumType, String name) |
--------------
枚举也相当于一个类。当然也可以定义自己的构造函数。但是这个构造函数必须为private所修饰。
这样可以保证外部代码无法新构造枚举类的实例。
public enum WeekDay {
SUN,MON,TUE,WED,THI,FRI,SAT;
private WeekDay(){
System.out.println(this.name()+"被初始化");
}
public static void main(String[] args){
WeekDay wd = WeekDay.FRI;
}
}
结果输出:
SUN被初始化
MON被初始化
TUE被初始化
WED被初始化
THI被初始化
FRI被初始化
SAT被初始化
WeekDay内部的SUN,MON,TUE,WED,THI,FRI,SAT;等静态常量一一被初始化。
------------
WeekDay相当于类当然也可以定义自己的成员(包括成员方法和成员函数):
public enum WeekDay {
SUN,MON,TUE,WED,THI,FRI,SAT;
private int ss = 9;
private WeekDay(){
//System.out.println(this.name()+"被初始化");
}
public static void main(String[] args){
WeekDay wd = WeekDay.FRI;
wd.vv();
wd = WeekDay.MON;
wd.vv();
WeekDay.SAT.vv();
}
public void vv(){
System.out.println("vvvvv"+(++ss));
}
}
结果输出:
vvvvv10
vvvvv10
vvvvv10
----------------
发现使用几个枚举值输出的结果都是一样的。
能不能让每个枚举值输出的结果不同呢?
于是就想到了多态
由父类引用指向子类对象,子类重写父类的方法。在运行的时候,实际调用的是子类的方法。
--------------
在一个类里怎么让父类引用指向子类对象呢?
可以使用匿名内部类。
父类的方法不具体,子类重写父类方法后各自实现的也不同,所以将父类的方法定义为抽象的。父类的方法是抽象的了,当然父类也得是抽象类了。
例子:
public abstract class ListDemo {
ListDemo dd = new ListDemo(){ //new ListDemo()是什么具体的类?不知道,只知道是ListDemo的子类。。
//所以new ListDemo()是匿名内部类
public void zz() {
}
};
public abstract void zz();
}
-------------------------
可以再扩展下:
public abstract class TrafficLamp{
//RED是TrafficLamp类的成员,是TrafficLamp类的一个引用;
/* ---------
* new TrafficLamp(){
* public TrafficLamp nextLamp() {
return GREEN;
}
* };就是匿名内部类,是TrafficLamp的子类对象。
*/
public static final TrafficLamp RED = new TrafficLamp(){
public TrafficLamp nextLamp() {
return GREEN;
}
};
public static final TrafficLamp GREEN = new TrafficLamp(){
public TrafficLamp nextLamp() {
return YELLOW;
}
};
public static final TrafficLamp YELLOW = new TrafficLamp(){
public TrafficLamp nextLamp() {
return RED;
}
};
public abstract TrafficLamp nextLamp();
}
使用javac TrafficLamp.java编译这个程序得到4个class文件
类名$数字就代表一个匿名内部类。
-----------
会发现上面的TrafficLamp 怎么那么像枚举呢?
它和枚举比较相似。
那么在枚举中能不能定义抽象方法,并由子类实现呢?
当然是可以的。功能是一样的,而且实现起来会更简单。
public enum TrafficLamp{
/* RED是枚举的一个元素,
* 这个元素可以看做是由TrafficLamp的一个子类来实现的。
* */
RED{
public TrafficLamp nextLamp() {
return GREEN;
}
},
GREEN{
public TrafficLamp nextLamp() {
return YELLOW;
}
},
YELLOW{
public TrafficLamp nextLamp() {
return RED;
}
};
public abstract TrafficLamp nextLamp();
}
使用javac TrafficLamp.java编译这个程序
最后也是得到4个class文件
这就是实现带有抽象方法的枚举。
如果一下子就看到这样带有抽象方法的枚举会感到很突兀,不知从何而来,为什么这样写呢。有了以上分析的会感觉好懂些。