Java枚举类型

关键字enum可以将一组具名的值的有限集合创建为一种新的类型,就是枚举类型。枚举类型是Java 1.5新增的特性,这是一个非常有用的特性。

Enum

在这一小部分,我们将探讨枚举的最基本的用法。首先我们必须要明白一点:enum是一个常规类型,只不过它还有一些限制。定义枚举类型很简单,使用enum关键字把所有的值组织起来,就像下面一样:

enum Season {SPRING,SUMMER,FULL,WINTER}

Season拥有四个实例,在一般的命名规范中,通常用大写的字母表示枚举的实例。枚举类型的实例是有序的,每个实例都有一个对应的序号,从0开始,由于enum天然的有序性,可以在switch()语句中使用枚举示例,这样语义更清晰。在创建enum时,编译器会自动生成一个相关的类,这个类继承自java.lang.Enum,Enum是一个普通类,它提供了一些方法,其中有几个是需要我们关注的:

public final int ordinal():返回枚举常量的序号,从0开始,也就是SPRING的序号是0,SUMMER是1,.....

public static <T extends Enum<T>> T valueOf(Class<T> enumType,String name):将给定的name转换成对应枚举类的实例,name必须要与枚举类中的常量完全匹配,它的实现方式大概是这样:在Enum的内部有一个方法,它把枚举的名称和枚举常量组成一个Map,调用valueOf()方法时,就是去Map中get(name);

出了上面的两个方法比较重要外,它还两个比较诡异的方法,待会再解释一下这两个方法:

public static T[] values():返回枚举的所有常量

public static <T extends Enum<T>> T valueOf(String name):将给定的字符串转换成枚举常量,和valueOf(Class<T> enumType,String name)方法类似。

enum中的方法

enum可以添加构造方法,还可以添加自定义的方法,还可以覆盖父类中的方法,所有的这一切都和普通的类一样,只不过会有一些小限制,还是以Season为例,演示enum中的基本用法

package com.think.enumeration;

public enum Season {
	//Java要求必须先定义枚举实例,
	SPRING(10,20),
	SUMMER(20,32),
	FULL(10,20),
	//这就是哈尔滨的真实写照
	WINTER(-40,-5);//注意这个分号,比不可少
	
	private  double lowTemp;
	private  double highTemp;
	//一旦enum定义结束,编译器就不允许我们再使用
	//其构造器来创建任何实例了,所以即使不是private也没关系
	private Season(double low,double high) {
		lowTemp = low;
		highTemp = high;
	}
	
	//自定义的方法,就像在普通的类中一样
	public void setLow(double low) {
		this.lowTemp = low;
	}
	
	//还可以覆盖父类中的方法
	@Override
	public String toString() {
		return "(" + lowTemp + "," + highTemp + ")";
	}
	
	//甚至还可以有main方法
	public static void main(String[] args) {
		for(Season e : Season.values()) {
			if(e == SPRING)
				e.setLow(-10);
			System.out.println(e);
		}
	}
}
我们一定要抓住enum的本质: 它是一组常量的集合。所以很多限制都是为了确保“常量性”,比如在定义枚举类时,必须先定义enum,它的构造方法在定义enum实例结束后就不能使用。出了围绕常量性的限制之外,其他的语法与普通类无异,我们可以添加普通方法,可以覆盖父类方法等。在所有的特性中,最具特色的要数 常量相关方法

在enum中除了给enum类型添加方法之外,每个实例还可以有自己的方法,这样的方法叫做常量相关方法:对于同一个方法,不同的实例可以实现自己的行为。这样甚至有了多态性。实现常量相关的方法时,可以将一个方法实现成抽象方法,每个实例提供具体实现;也可以把方法实现成普通方法,每个实例根据需求,选择是否覆盖该方法,示例如下:

public enum ConstantSpecificMethod {
	MALE {
		//常量相关
		public String getSex() {
			return "male";
		}
		
		//覆盖公共方法
		public void f() {
			System.out.println("male and male and ");
		}
	},
	FEMALE {

		@Override
		public String getSex() {
			return "female";
		}
	};
	
	//将方法定义为抽象方法
	abstract public String getSex();
	
	//提供公有的实现,实例可以考虑是否覆盖
	public void f() {
		System.out.println("nothing else");
	}
}
对于abstract方法,每个实例必须提供具体实现,对于提供的默认实现,每个实例课根据实际选择是否覆盖。通过常量相关方法,使枚举实例间具有了多态性,但是枚举实例毕竟不是class类型。

编译器的动作

编译器会为enum类型生成class文件,相应的enum类自动继承Enum<T>,这是编译器的常规处理。在前面我们提到了enum类型有values()方法和只有一个参数的valueOf()方法,但是遍查API文档,在Enum类中并没有发现这两个方法,而在前面的例子中我们又确确实实的使用了values()方法,这是怎么一回事呢?下面是根据前面的Season的class文件反编译的结果,选择其中的一部分:

/**
 * 下面是编译器为我们生成的的对应的类
 * 类被标记为final,这意味着它不可被继承
 * 自动继承Enum<T>,Enum中的所有方法它都可以使用,但是它不能再继承其他类了
 */
public final class com.think.enumeration.Season extends java.lang.Enum<com.think.enumeration.Season> {
	//所有的实例都被实现为final的
	  public static final com.think.enumeration.Season SPRING;

	  public static final com.think.enumeration.Season SUMMER;

	  public static final com.think.enumeration.Season FULL;

	  public static final com.think.enumeration.Season WINTER;
	  
	  
	  static {};
	    Code:
	       0: new           #1                  // class com/think/enumeration/Season
	       3: dup           
	       4: ldc           #18                 // String SPRING
	       6: iconst_0      
	       7: ldc2_w        #19                 // double 10.0d
	      10: ldc2_w        #21                 // double 20.0d
	      13: invokespecial #23                 // Method "<init>":(Ljava/lang/String;IDD)V
	      16: putstatic     #27                 // Field SPRING:Lcom/think/enumeration/Season;
	      ........
	      76: iconst_4      
	      77: anewarray     #1                  // class com/think/enumeration/Season
	      80: dup           
	      81: iconst_0      
	      82: getstatic     #27                 // Field SPRING:Lcom/think/enumeration/Season;
	      85: aastore       
	      86: dup           
	      87: iconst_1      
	      ........
	     107: return        
	  
	  //编译器为我们提供的方法
	  public static com.think.enumeration.Season[] values();
	    Code:
	       0: getstatic     #44                 // Field ENUM$VALUES:[Lcom/think/enumeration/Season;
	       3: dup           
	       4: astore_0      
	       5: iconst_0      
	       6: aload_0       
	       7: arraylength   
	       8: dup           
	       9: istore_1      
	      10: anewarray     #1                  // class com/think/enumeration/Season
	      13: dup           
	      14: astore_2      
	      15: iconst_0      
	      16: iload_1       
	      17: invokestatic  #110                // Method java/lang/System.arraycopy:(Ljava/lang/Object;ILjava/lang/Object;II)V
	      20: aload_2       
	      21: areturn       
      //编译器为我们提供的方法
	  public static com.think.enumeration.Season valueOf(java.lang.String);
	    Code:
	       0: ldc           #1                  // class com/think/enumeration/Season
	       2: aload_0       
	       //实际上是调用Enum.valueOf(Class enumType,String name)方法,只不过第一个参数默认是本枚举类型
	       3: invokestatic  #116                // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
	       6: checkcast     #1                  // class com/think/enumeration/Season
	       9: areturn       
	}
通过上面的代码,我们可以发现:

(1)编译器自动的把对应的类设置为final,这样它就不能有子类

(2)编译器自动的使对应的类继承子Enum<T>,这样它就不能再继承其他的类,同时可以使用Enum中的方法

(3)Season实例的初始化在一个静态代码块中执行,这一点很重要,这意味这枚举类的实现是在类加载的初始化阶段进行的,一旦把一个枚举类加载到JVM中后,就不能改变枚举类的实例,而且在类加载的初始化阶段,JVM会确保初始化的安全,不会存在多线程的问题。所以在使用单例模式时,把单例类实现成枚举类,其中只有一个实例是一个非常好的手段。

(4)每个实例都是final的

(5)编译器为我们添加了values()方法,它返回此枚举的所有实例;还添加了只有一个参数的valueOf(String name)方法,它其实是调用Enum的valueOf()方法,只不过把该枚举类对应的Class对象传递过去作为第一个参数。也就是如果把一个枚举的实例转换为Enum类型,那么是不可以使用values()方法的,这个时候可以通过Class类的getEnumConstants()方法

Enum e  = Season.SPRING;
//不能调用values()方法
//e.values()
//可以使用getEnumConstants()
e.getClass().getEnumConstants()

以上就是枚举的基本用法,枚举是一项很有用的特性,由于它天然的有序性,它可以在很多方面发挥作用,同时使用枚举在构造程序时,结构清晰,语义清晰。

转载请注明出处:喻红叶《Java枚举》

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值