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

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

分类: Java
0
0
« 上一篇: JS读书心得:《JavaScript框架设计》——第12章 异步处理
» 下一篇: CSS魔法堂:再次认识font
posted @ 2015-02-23 16:00 ^_^肥仔John 阅读( 3089) 评论( 1) 编辑 收藏
  
#1楼 2015-02-25 23:04 寻风问雨  
最后一个用接口实现的意义是啥?
相关文章
|
2天前
|
安全 Java 测试技术
🎉Java零基础:全面解析枚举的强大功能
【10月更文挑战第19天】本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
92 60
|
2月前
|
安全 Java 索引
Java——反射&枚举
本文介绍了Java反射机制及其应用,包括获取Class对象、构造方法、成员变量和成员方法。反射允许在运行时动态操作类和对象,例如创建对象、调用方法和访问字段。文章详细解释了不同方法的使用方式及其注意事项,并展示了如何通过反射获取类的各种信息。此外,还介绍了枚举类型的特点和使用方法,包括枚举的构造方法及其在反射中的特殊处理。
58 9
Java——反射&枚举
|
2月前
|
Java
java小工具util系列4:基础工具代码(Msg、PageResult、Response、常量、枚举)
java小工具util系列4:基础工具代码(Msg、PageResult、Response、常量、枚举)
75 5
|
2月前
|
安全 Java 开发者
Java 枚举(enum)详解
Java 中的枚举(`enum`)是一种特殊的数据类型,用于定义一组固定的常量,提升代码的类型安全性和可读性。枚举使用 `enum` 关键字定义,支持方法和构造函数,具有类型安全、单例、自动序列化等特点,并且可以遍历和用于 `switch` 语句中。实际应用包括状态机、指令集、类型标识等场景。枚举使代码更加清晰易维护。
|
6月前
|
安全 Java 程序员
Java语言枚举(Enum)的深入探索
Java语言枚举(Enum)的深入探索
|
3月前
|
Java
Java枚举使用的基本案例
这篇文章是关于Java枚举的基本使用,通过一个指令下发的代码案例,展示了如何定义枚举、使用枚举以及如何通过枚举实现指令的匹配和处理。
|
3月前
|
Java 开发者
在Java编程中,if-else与switch作为核心的条件控制语句,各有千秋。if-else基于条件分支,适用于复杂逻辑;而switch则擅长处理枚举或固定选项列表,提供简洁高效的解决方案
在Java编程中,if-else与switch作为核心的条件控制语句,各有千秋。if-else基于条件分支,适用于复杂逻辑;而switch则擅长处理枚举或固定选项列表,提供简洁高效的解决方案。本文通过技术综述及示例代码,剖析两者在性能上的差异。if-else具有短路特性,但条件增多时JVM会优化提升性能;switch则利用跳转表机制,在处理大量固定选项时表现出色。通过实验对比可见,switch在重复case值处理上通常更快。尽管如此,选择时还需兼顾代码的可读性和维护性。理解这些细节有助于开发者编写出既高效又优雅的Java代码。
43 2
|
3月前
|
安全 Java 编译器
|
4月前
|
存储 缓存 Java
java枚举消除冗余代码问题之findByName和findByValue方法工作时的问题如何解决
java枚举消除冗余代码问题之findByName和findByValue方法工作时的问题如何解决
|
4月前
|
安全 Java
Java进阶之枚举
【7月更文挑战第11天】Java枚举是Java 5引入的特性,用于定义固定常量集合,如星期。枚举是继承自`java.lang.Enum`的特殊类,编译后成为final类,每个枚举值是静态final实例。定义枚举用`enum`关键字,如`public enum Weekday {MONDAY, TUESDAY, ...}`。枚举可包含方法和变量,能实现接口但不能继承其他类。例如,`Weekday`枚举可实现`Describe`接口,提供`describe()`方法。在实际应用中,枚举常用于表示如响应状态等固定选项,便于类型安全和代码阅读。
37 8