Java中枚举类概念说明

枚举类入门

  在某些情况下,一个类的对象是有限且固定,比如季节类,它只有4个对象;再比如行星类,目前只有8个对象。这种实例有限而且固定的类,在Java里被称为枚举类。
  Java5新增了一个enum关键字(它与class,interface关键字的地位相同),用以定义枚举类。正如前面所看到的,枚举类是一种特殊的类,它一样可以有自己的成员变量、方法,可以实现一个或者多个接口,也可以定义自己的构造器。一个Java源文件最多只能定义一个public访问权限的枚举类,且该Java源文件也必须和该枚举类的类名相同。

  但枚举类终究不是普通类,它与普通类有如下简单区别。

  • 枚举类可以实现一个或多个接口,使用enum定义的枚举类默认继承了java.lang.Enum类,而不是默认继承Object类,因此枚举类不能显式继承其它父类。其中java.lang.Enum类实现了java.lang.Serializable和java.lang.Comparable两个接口。
  • 使用enum定义非抽象的枚举类默认会使用final修饰,因此枚举类不能派生子类。
  • 枚举类的构造器只能使用private访问控制符,如果省略了构造器的访问控制符,则默认使用private修饰;如果强制指定访问控制符,则只能指定private修饰符。
  • 枚举类的所有实例必须在枚举类的第一行显式列出,否则这个枚举类永远都不能产生实例。列出这些实例时,系统会自动添加public static final修饰,无须程序员显式添加。

额外说明:枚举类默认提供了一个values()方法,该方法可以很方便地遍历所有的枚举值。如下所示,其中SeasonEnum 为下面定义的枚举类

for(SeasonEnum s:SeasonEnum.values()){
	sout(s);
}

下面程序定义了一个SeasonEnum枚举类。

public enum SeasonEnum{
    	// 在第一行列出4个枚举实例
    	SPRING,SUMMER,FALL,WINTER;
    	}

  编译上面Java程序,将生成一个SeasonEnum.class文件,这表明枚举类是一个特殊的Java类。由此可见,enum关键字和class、interface关键字的作用大致类似。
  定义枚举类时,需要显式列出所有的枚举值,如上面的SPRING,SUMMER,FALL,WINTER;所示,所有的枚举值之间以英文逗号(,)隔开,枚举值列举结束后以英文分号作为结束。这些枚举值代表了该枚举类的所有可能的实例。
  如果需要使用该枚举类的某个实例,则可以使用EnumClass.variable的形式,如SeasonEnum.SPRING。也可以调用方法valueOf,具体如下:

    SeasonEnum seasonEnum1 = Enum.valueOf(SeasonEnum.class,"SPRING");
    SeasonEnum seasonEnum2 = SeasonEnum.valueOf("SPRING");
    SeasonEnum seasonEnum3 = SeasonEnum.SPRING;
枚举类的成员变量、方法和构造器

  枚举类也是一种类,只是它是一种比较特殊的类,因此它一样可以定义成员变量、方法和构造器。下面程序将定义一个Gender枚举类,该枚举类里包含了一个name实例变量。

	public enum Gender{
		MALE,FEMALE;
		private String name;
		public String getName() {
        	return name;
    	}

    	public void setName(String name) {
        	this.name = name;
    	}
	}

  对于里面的实例变量name,我们可以通过获取Gender枚举类的对象来进行调用,如下。

public class GenderTest{
	public static void main(String[] args){
			// 这里获取Gender对象(实例)使用上述3个方法都可以
			Gender g = Enum.valueOf(Gender.class,"MALE");
			g.setName("男");
		}
	}

  但是上述代码依然有可能存在问题,如获取的是MALE对象,但是设置name时却设置成了女。这样就存在问题了。同时枚举类通常应该设计成不可变类,也就是说,它的成员变量值不应该允许改变,这样会更安全,而且代码更加简洁。因此建议将枚举类的成员变量都使用private final修饰。
  如果将所有的成员变量都使用了final修饰符来修饰,所以必须在构造器里为这些成员变量指定初始值(或者在定义成员变量时指定默认值,或者在初始化块中指定初始值,但这两种情况并不常见),因此应该为枚举类显式定义带参数的构造器。
  一旦为枚举类显式定义了带参数的构造器,列出枚举值时就必须对应地传入参数。

public enum Gender{
	// 此处的枚举值必须调用对应的构造器来创建
	MALE("男"),FENALE("女");
	private final String name;
	// 枚举类的构造器只能使用private修饰
	private Gender(String name){
		this.name = name;
	}
	public String getName(){
		return this.name;
	}
}

  从上面程序中可以看出,当为Gender枚举类创建了一个Gender(String name)构造器之后,列出枚举值就应该采用第三行代码来完成。也就是说,在枚举类中列出枚举值时,实际上就是调用构造器创建枚举类对象,只是这里无须使用new关键字,也无须显式调用构造器。前面列举出枚举值是无须传入参数,甚至无须括号,仅仅是因为前面的枚举类包含无参数的构造器。
  不难看出,上面程序第三行的代码实际上等同于如下两行代码:

public static final Gender MALE = new Gender("男");
public static final Gender FEMALE = new Gender("女");
实现接口的枚举类

  这里主要是想介绍下每个枚举值(或者说是枚举实例)实现接口方法可以表现出不同的行为方式。代码示例如下。

public enum Gender implements GenderInfo{
	// 此处的枚举值必须自动阿勇对应的构造器来创建
	MALE("男"){
		public void info(){
			sout("这个枚举值代表男性");
		}
	}FEMALE("女"){
		public void info(){
			sout("这个枚举值代表女性");
		}
	}
}

  上面代码看起来有些奇怪:当创建MALE和FEMALE两个枚举值时,后面又紧跟了一堆花括号,这对花括号包含了一个info()方法定义。如果读者还记得匿名内部类语法的话,则可能对这样的语法有点印象了,花括号部分实际上就是一个类体部分,在这种情况下,当创建MALE、FEMALE枚举值时,并不是直接创建Gender枚举类的实例,而是相当于创建Gender的匿名子类的实例。因为上面代码75-77和80-82行实际上是匿名内部类的类体部分,所以这个部分的代码语法就和匿名内部类语法大致相似,只是它依然是枚举类的匿名内部子类。
  编译上面的程序,可以看到生成了Gender.class、Gender$1.class和Gender$2.class三个文件,这样的三个class文件正好证明了上面的结论:MALE和FEMALE实际上是Gender匿名子类的实例,而不是Gender类的实例。
  特别说明:并不是所有的枚举类都使用了final修饰!非抽象的枚举类才默认使用final修饰。对于一个抽象的枚举类而言----只要它包含了抽象方法,它就是抽象枚举类,系统会默认使用abstract修饰,而不是使用final修饰。因此枚举类里定义抽象方法时不能使用abstract关键字将枚举类定义成抽象类,因为系统已自动为它添加了abstract关键字,但因为枚举类需要显式创建枚举值(枚举实例),而不是作为父类,所以定义每个枚举值(枚举实例)时必须为抽象方法提供实现,否则将出现编译错误。
  本文主要参考自疯狂java讲义第四版中关于枚举讲解的内容,如若侵权,请联系删除。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值