Java魔法堂:枚举类型详解

简介:

一、前言                                

  Java的枚举类型相对C#来说具有更灵活可配置性,Java的枚举类型可以携带更多的信息。

复制代码
// C#
enum MyColor{
  RED = 0,
  BLUE = 1
}
Console.Write(MyColor.RED);

// Java
enum MyColor{
  RED("Hot", 4), BLUE("SAD",8);
  
  private String mood;
  public String getMood{
    return mood;
  }
  private int index;
  public int getIndex(){
    return index;
  }
  private MyColor(String mood, int index){
    this.mood = mood;
    this.index = index;
  }
}
System.out.println(MyColor.RED.getMood());
复制代码

  本文将对枚举类型进行较为详细的叙述,以便日后查阅。

 

二、最简单的用法——常量                        

复制代码
/* 定义 */
// 形式1
enum MyColor{
  RED,BLUE
}

// 形式2
enum MyColor{
  RED,BLUE;
} 

/* 使用 */
System.out.println(MyColor.RED.name()); // 显示RED
System.out.println(MyColor.RED.ordinal()); // 显示0
System.out.println(MyColor.BLUE.name()); // 显示BLUE
System.out.println(MyColor.BLUE.ordinal()); // 显示1
复制代码

  枚举值的name()会返回枚举值的字面量,而ordinal()为返回枚举值的索引,而索引是以枚举值定义时的位置来确定,并在编译时设置的。下面我们来看看到底编译器为我们做了什么?

复制代码
final class MyColor extends java.lang.Enum<MyCorlor>{
public
static final MyColor RED; flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM public static final MyColor BLUE; flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM public static MyColor[] values(); flags: ACC_PUBLIC, ACC_STATIC Code: stack=1, locals=0, args_size=0 0: getstatic #1 // Field $VALUES:[LMyColor; 3: invokevirtual #2 // Method "[LMyColor;".clone:()Ljava/lang/Object; 6: checkcast #3 // class "[LMyColor;" 9: areturn LineNumberTable: line 1: 0 public static MyClass valueOf(java.lang.String); flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=1, args_size=1 0: ldc_w #4 // class MyColor 3: aload_0 4: invokestatic #5 // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum; 7: checkcast #4 // class MyColor 10: areturn LineNumberTable: line 1: 0 static {}; flags: ACC_STATIC Code: stack=4, locals=0, args_size=0 0: new #4 // class MyColor 3: dup 4: ldc #7 // String RED 6: iconst_0 7: invokespecial #8 // Method "<init>":(Ljava/lang/String;I)V 10: putstatic #9 // Field RED:LMyColor; 13: new #4 // class MyColor 16: dup 17: ldc #10 // String BLUE 19: iconst_1 20: invokespecial #8 // Method "<init>":(Ljava/lang/String;I)V 23: putstatic #11 // Field BLUE:LMyColor; 26: iconst_2 27: anewarray #4 // class MyColor 30: dup 31: iconst_0 32: getstatic #9 // Field RED:LMyColor; 35: aastore 36: dup 37: iconst_1 38: getstatic #11 // Field BLUE:LMyColor; 41: aastore 42: putstatic #1 // Field $VALUES:[LMyColor; 45: return LineNumberTable: line 2: 0 line 1: 26
}
复制代码

 可以看到编译器将enum MyColor编译为一个继承Enum<MyColor>并且带修饰符final的MyColor类。

 而枚举值RED和BLUE则被编译为MyColor的类常量,并且在类加载的初始化阶段实例化。MyColor默认的构造函数会调用父类Enum<MyColor>的构造函数Enum<E>(String name, int ordinal)来设置私有字段name和ordinal的值。其中iconst_0和iconst_1分别表示将0和1压栈,invokespecial #8则是调用构造函数Enum<E>(String name, int ordinal)。

复制代码
  0: new           #4                  // class MyColor
  3: dup           
  4: ldc           #7                  // String RED
  6: iconst_0                          // int 0
  7: invokespecial #8                  // Method "<init>":(Ljava/lang/String;I)V
  10: putstatic     #9                  // Field RED:LMyColor;
  13: new           #4                  // class MyColor
  16: dup           
  17: ldc           #10                 // String BLUE
  19: iconst_1                          // int 1
  20: invokespecial #8                  // Method "<init>":(Ljava/lang/String;I)V
  23: putstatic     #11                 // Field BLUE:LMyColor;
复制代码

  另外在类加载的初始化阶段会生成一个私有的$VALUE数组用于存放常量RED和BLUE,而在调用MyColor.values()返回的正是这个$VALUE数组的复制品。

复制代码
 26: iconst_2      
 27: anewarray     #4                  // class MyColor
 30: dup           
 31: iconst_0      
 32: getstatic     #9                  // Field RED:LMyColor;
 35: aastore       
 36: dup           
 37: iconst_1      
 38: getstatic     #11                 // Field BLUE:LMyColor;
 41: aastore       
 42: putstatic     #1                  // Field $VALUES:[LMyColor;
复制代码

  小结:

     1. 定义枚举类型本质上就是在定义带final修饰符的Enum<E>的子类;

     2. 枚举值本质为第1点所定义的类的类常量;

     3. 枚举值的ordinal值由其定义时的排序决定,并且在编译时已经被设置好了。

 

三、枚举类型的抽象父类Enum<E>                    

  其实我们大多数情况下都是调用父类Enum<E>的方法来操作自定义的枚举值,下面一起看看父类Enum<E>吧!

  1. 它为抽象类且继承了Comparable<E>和Serializable两个类。

  2. 内含私有字段name和ordinal和对应的公有get方法name()和ordinal()。

  3. 重写了equals方法,通过==比较两个枚举值的内存地址来判断两者是否相同。

  4. 实现compareTo方法,通过比较两个枚举值的ordinal值来做判断。

  5. getDeclaringClass方法,用于返回枚举的Class对象。

 

四、携带更多信息——自定义构造函数                   

  由于枚举最终被编译为类,因此我们通过自定义构造函数、自定义字段和方法来让枚举值携带更多信息

复制代码
public enum MyColor{
  RED("Hot", 2), BLUE("SAD",5);
  
  private String mood;
  private int index;
  private MyColor(String mood, int index){
    this.mood = mood;
    this.index = index;
  }
}
复制代码

  注意:

    1. 自定义的构造函数必须是私有的;

    2. 构造函数内不能显式调用父类的构造函数;

    3. RED、BLUE的ordinal值依然是0和1,那么值依然是RED和BLUE。

 上述3点规定和结果的原因是编译器会对我们的自定义构造函数进行加工变为

复制代码
private MyColor(String name, String ordinal, String mood, int index){
  super(name, ordinal);
  this.mood = mood;
  this.index = index;
}
复制代码

 

五、让相同枚举类型下的枚举值具有不同的行为——重写枚举值的方法    

复制代码
public enum MyColor{
  RED, BLUE(){
     @Override
     public boolean getFlag(){
       return false;
     }
  };
  public boolean getFlag(){
    return true;
  }
}
// 调用
System.out.println(MyColor.RED.getFlag()); // 显示true
System.out.println(MyColor.BLUE.getFlag()); // 显示false
复制代码

  可以看到枚举值RED和BLUE同一个方法具有不同的行为。其实这是通过匿名内部类的方式实现的,BLUE的类型为MyColor$1 extends MyColor,而RED的类型为MyColor。

 

六、使用接口组织枚举                          

复制代码
public interface Food {
        enum Coffee implements Food {
            BLACK_COFFEE, DECAF_COFFEE, LATTE, CAPPUCCINO
        }

        enum Dessert implements Food {
            FRUIT, CAKE, GELATO
        }
    }
复制代码

  

七、总结                                

  若有纰漏请大家指正,谢谢。

  尊重原创,转载请注明来自:http://www.cnblogs.com/fsjohnhuang/p/4297741.html ^_^肥仔John

 

八、参考                                

  http://www.tuicool.com/articles/YvQZFf

  http://www.cnblogs.com/hemingwang0902/archive/2011/12/29/2306263.html

  http://www.cnblogs.com/frankliiu-java/archive/2010/12/07/1898721.html

如果您觉得本文的内容有趣就扫一下吧!捐赠互勉!

posted @ 2015-02-23 16:00 ^_^肥仔John 阅读( 3089) 评论( 1) 编辑 收藏
  
#1楼 3131538 2015/2/25 23:04:45 2015-02-25 23:04 寻风问雨  
最后一个用接口实现的意义是啥?
http://pic.cnblogs.com/face/424274/20130126034040.png

公告

肥仔John@github
作品:
本文转自 ^_^肥仔John博客园博客,原文链接: http://www.cnblogs.com/fsjohnhuang/p/4297741.html如需转载请自行联系原作者
 
相关文章
|
1月前
|
安全 Java
Java 枚举(Enums)解析:提高代码可读性与易维护性
在 Java 中,实现抽象的另一种方式是使用接口。 接口定义 接口是一个完全抽象的类,用于将具有空方法体的相关方法分组:
71 1
|
2月前
|
存储 安全 Java
解密 Java 枚举的奥秘:超越常量的选择
解密 Java 枚举的奥秘:超越常量的选择
33 0
|
3月前
|
开发框架 Java 编译器
Java反射,枚举讲解
Java反射,枚举讲解
43 0
|
5月前
|
Kubernetes 数据可视化 安全
枚举探秘:Java中的神奇力量!
枚举探秘:Java中的神奇力量!
|
7月前
|
Java
java枚举
Java枚举是一种特殊的类,用于表示一组相关的常量。枚举类可以定义一组常量,并且枚举常量在使用时具有类型安全性,可以避免使用常量时出现类型错误。Java枚举可以继承其他类,也可以被其他类继承。Java枚举的实例称为枚举常量。
35 0
|
13天前
|
Java API
Java基础—笔记—内部类、枚举、泛型篇
本文介绍了Java编程中的内部类、枚举和泛型概念。匿名内部类用于简化类的创建,常作为方法参数,其原理是生成一个隐含的子类。枚举用于表示有限的固定数量的值,常用于系统配置或switch语句中。泛型则用来在编译时增强类型安全性,接收特定数据类型,包括泛型类、泛型接口和泛型方法。
9 0
|
1月前
|
Java
JAVA枚举
JAVA枚举
12 1
|
1月前
|
Java
Java枚举类型
Java枚举类型
10 0
|
1月前
|
Java Spring
31、学习 Java 中的枚举类型
31、学习 Java 中的枚举类型
101 1
|
2月前
|
Java
Java枚举简述
Java枚举简述
15 0