`
足至迹留
  • 浏览: 485361 次
  • 性别: Icon_minigender_1
  • 来自: OnePiece
社区版块
存档分类
最新评论

枚举原理及使用

阅读更多
参考:《java入门经典》《java核心技术 卷1》网络
1.枚举引入
  通常需要一些只能从预定义的固定集合中取值的变量。例如,假设想要定义一个名为weekday的整型变量,用来存储表示星期几的整数值。该变量最好能限制在7个可能的值之内,每个值逐个对应星期一到星期日。这种情形下一种称作枚举类型(enumerated type),甚至简单地称作枚举(enumeration)的工具是自然的选择。可以使用如下声明语句为这种情况定义一个枚举:
enum Day {Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday }

这里定义了一个新的类型Day,变量只能存储在括号中设定的其中一个值。名称Monday、
Tuesday 一直到Sunday 都称为枚举常量,而且它们标识了Day 类型的变量中仅允许的值。事实上,这些名称会对应到整数值,默认情况下从0 开始,但是它们与整型变量不同,因为它们只在枚举Day的上下文中存在。

注意:在Day 枚举定义的最后没有分号(如果枚举中还定义了其他成员,则必须有分号)。因为这里是定义一个类型,所以在反括号的后面不需要
添加分号。类型名Day 的开头使用一个大写的D,因为按照约定,定义的类型都以大写字母开头。
枚举常量的名称通常用全大写命名,对应了普通public final static常量的规则。

有了这个新类型,现在可以按照如下方式定义变量weekday:
Day weekday = Day.Tuesday;
这里声明了Day 类型的变量weekday 并且将其初始化为Tuesday。注意提供weekday 初始值的
枚举常量必须用枚举类型名限定(switch语句中的case后面使用枚举常量时不需要用枚举类型限定)。如果不要限定名,编译器就无法认出该常量。

枚举通常能包含所需要数目的枚举常量。这里是一个表示每年月份的枚举类型:
enum Month { January, February, March , April , May , June,
July , August , September, October, November, December }

可以按如下方式定义这种类型的变量:
Month current = Month.September; // Initialize to September
如果之后想要修改变量中存储的值,可以将其设置为一个不同的枚举常量:
current = Month.October;
current 变量现在包含枚举常量值October。

2.枚举使用
枚举类型其实是一种特殊形式的类,声明enum时不能继承一个超类,默认所有的枚举都自动地继承java.lang.Enum。在代码中定义枚举类型时,设定的枚举常量以一个类的实例被创建,这个类以java.lang包中的Enum类作为超类,所以跟类一样,在类的定义中可以放置枚举类型的定义,也可以将枚举类型定义放在一个单独的源文件中。

每个枚举常量对应的对象将常量的名称存储在一个域中,而枚举类类型从Enum类中继承toString()方法。Enum类中的toString()方法返回 枚举常量的原始名称,所以使用println()方法输出枚举常量时会获得枚举常量的名称。

表示枚举常量的对象也存储一个整数域。默认地,枚举中的每个常量都被赋予一个与其他常量不同的整数值。这些值按照设定它们时的顺序被赋给枚举常量,第一个常量从0开始,然后第二个常量是1,后面依此类推。通过调用常量的ordinal()方法就能获得它的整数值,但是一般来说不应该需要这样做。

可以使用equals()方法来比较枚举类型的整数值是否相等(但是,永远不要使用equals,直接使用==比较,因为我们定义了枚举类型就不希望再创建新常量对象了,直接使用已经定义好的实例)。例如,假设已经定义了枚举类型Season,其中包括了枚举常量spring、summer、fall和winter,可以编写如下内容:
   Season now = Season.winter;
   if(now.equals(Season.winter))
   {
       System.out.println("It is definitely winter!");
   }

这个枚举类类型的equals()方法从Enum类继承而来,另外还继承了基于枚举类型实例的整数值进行比较的compareTo()方法。如果方 法调用的实例的整数值小于作为参数传入的实例,结果为负整数;如果它们相等,结果为0;如果当前实例的整数值大于作为参数传入的值,结果为正整数。因此, 当定义这些枚举常量时,它们的设定顺序决定了compareTo()方法实现的顺序。可以这样使用:
if(now.compareTo(Season.summer) > 0)
    System.out.println("It is definitely getting colder!");

枚举引入的values()方法是一个属于枚举类类型的静态方法,该方法返回一个包括在基于集合的for循环中使用的所有枚举常量的集合对象。

2.1 添加成员到枚举类

因为枚举是类,所以在定义枚举类型时可以添加自己的方法和域,还可以添加属于自己的构造函数来初始化任何引入的额外域。下面看一个例子。假设想要为衣服(比如夹克)的尺寸定义一个枚举类型,初始的定义可能如下:
public enum JacketSize { small, medium, large, extra_large, extra_extra_large }
然后会发现,可能想要记录应用到每个夹克尺寸的平均胸围。可以将上面的枚举定义修改为如
下形式:
public enum JacketSize {
small(36), medium(40), large(42), extra_large(46), extra_extra_large(48);
// Constructor
JacketSize(int chestSize) {
this.chestSize = chestSize;
}
// Method to return the chest size for the current jacket size
public int chestSize() {
return chestSize;
}
private int chestSize; // Field to record chest size
}


注意这里的枚举常量列表以分号结束。列表中的每个常量在括号中都有对应的胸围,而且这些值会被传递给添加到类中的构造函数。在之前的JacketSize 定义中每个枚举常量的出现都会导致对枚举类默认构造函数的一次调用
事实上,可以在每个常量名称的后面放一对空括号,这样仍然会编译。但是,这不会增加代码的清晰度。因为已经定义了一个构造函数,所以不会为枚举类型定义默认的构造函数。因此,不能只使用名称来编写枚举常量,必须在每个枚举常量的后面添加一对括号并在其中包括一个值作为胸围。当然,如果想要在枚举中选择省略一些常量的胸围尺寸,也可以定义自己的默认构造函数并且为chestSize 赋一个默认值。

即使已经添加了自己的构造函数,从基类Enum 中继承的用来存储常量名称及其整数值的域仍
然会被正确地设置。compareTo()方法实现的常量顺序仍然由定义中常量出现的顺序决定。注意决不能将枚举类中的构造函数声明为public。如果这样做,enum 类定义就不会被编译。唯一允许对定义为枚举的类的构造函数应用的修饰符是private,这导致只能在类的内部调用构造函数。

2.2 根据枚举值获取枚举类
   
public enum Day
    {
    	A(1),B(2),C(5);
    	private int value;

    	Day(int value)
    	{
    	    this.value = value;
    	}
    	
    	public int getValue()
    	{
    	    return value;
    	}
    	
    	public static Day getEnumByValue(int value)
    	{
    		Day[] day = Day.values();
    		
    		for (Day d : day)
    		{
    			if (d.getValue() == value)
    			{
    				return d;
    			}
    		}
    		
    		return null;
    	}
    	
    }

这里用到了枚举的静态方法values()返回所有的枚举常量,还可以使用Day.class.getEnumConstants()实现同样的功能。

3. 枚举原理
枚举类型经过编译器编译之后产生的是一个final class文件,该类继承了java.lang.Enum<E>。所以定义自己的枚举类型时不能再extends其他父类。而每个枚举常量都被编译成枚举类的public static final **Enum类型的常量。
比如:
public enum MessageTypeEnum {
    Order_New(1, "OrderPipe_ghost", 1, "abc");
    private Integer index;
    private String topic;
    private Integer messageType;
    private String description;
    private static ConcurrentHashMap<String, Integer> messageTypeMap = null;

    static {
        messageTypeMap = new ConcurrentHashMap<String, Integer>();
        for (MessageTypeEnum o: MessageTypeEnum.values()){
            messageTypeMap.put(o.getTopic(),o.getMessageType());
        }
    }

    public static Map<String, Integer> getMessageTypeMap(){
        return messageTypeMap;
    }

    private MessageTypeEnum(Integer index, String topic, Integer messageType, String description) {
        this.index = index;
        this.topic = topic;
        this.messageType = messageType;
        this.description = description;
    }
...
}

利用javap -s class文件可以看到:
ervice\render\MessageTypeEnum
Compiled from "MessageTypeEnum.java"
public final class com.an.service.render.MessageTypeEnum extends java.lan
.Enum{
public static final com.an.service.render.MessageTypeEnum Order_New;
  Signature: Lcom/an/libra/service/render/MessageTypeEnum;
public static com.an.service.render.MessageTypeEnum[] values();
  Signature: ()[Lcom/an/libra/service/render/MessageTypeEnum;
public static com.an.service.render.MessageTypeEnum valueOf(java.lang.Str
ng);
  Signature: (Ljava/lang/String;)Lcom/an/libra/service/render/MessageTypeEnum;
public static java.util.Map getMessageTypeMap();
  Signature: ()Ljava/util/Map;
public java.lang.Integer getIndex();
  Signature: ()Ljava/lang/Integer;
public void setIndex(java.lang.Integer);
  Signature: (Ljava/lang/Integer;)V
public java.lang.String getTopic();
  Signature: ()Ljava/lang/String;
public void setTopic(java.lang.String);
  Signature: (Ljava/lang/String;)V
public java.lang.Integer getMessageType();
  Signature: ()Ljava/lang/Integer;
public void setMessageType(java.lang.Integer);
  Signature: (Ljava/lang/Integer;)V
public java.lang.String getDescription();
  Signature: ()Ljava/lang/String;
public void setDescription(java.lang.String);
  Signature: (Ljava/lang/String;)V
static {};
  Signature: ()V
}

注意其中的:
public final class com.an.service.render.MessageTypeEnum extends java.lan.Enum
public static final com.an.service.render.MessageTypeEnum Order_New;


4.枚举示例
http://www.iteye.com/topic/1116193
http://www.cnblogs.com/linjiqin/archive/2011/02/11/1951632.html
http://socket.blog.163.com/blog/static/209873004201061440047/

枚举通常替代常量定义有固定范围的常量。经常会用在switch语句中。
查看enum的字节码可以看到,每个枚举常量被编译为枚举类的静态常量。
参考:http://www.cnblogs.com/frankliiu-java/archive/2010/12/07/1898721.html

5.枚举序列化部分讨论
http://www.iteye.com/topic/1052055
http://wenku.baidu.com/link?url=qQBwVRubjTG6QsiqBszi7wyfVrZE9m2oLEpLpOgR9en-k9YIvrfR_Nx3MlYIG3Bs8YCp3JtnXwpQ4as_1o6juhHd0xCvcHTLD3F0hHQI_E_

6.枚举异常IllegalArgumentException
jdk1.6源码:
public static <T extends Enum<T>> T valueOf(Class<T> enumType,
                                                String name) {
        T result = enumType.enumConstantDirectory().get(name);
        if (result != null)
            return result;
        if (name == null)
            throw new NullPointerException("Name is null");
        throw new IllegalArgumentException(
            "No enum const " + enumType +"." + name);
    }

当调用valueOf,且name不是定义的枚举常量(注意是枚举常量,不是ordinal也不是括号内指定的别名)时会抛出IllegalArgumentException异常,且会打出日志:"No enum const " + enumType +"." + name

还可以参见:
http://stackoverflow.com/questions/12639791/what-is-the-reason-for-java-lang-illegalargumentexception-no-enum-const-class-e
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics