zoukankan      html  css  js  c++  java
  • JAVA枚举类型总结

     enum 的全称为 enumeration, 是 JDK 1.5  中引入的新特性,存放在 java.lang 包中。

        下面是我在使用 enum 过程中的一些经验和总结,主要包括如下内容:

    1.背景

    2.定义

    3.特点

    4.enum对象常用的方法介绍

    5.应用场景

    6.EnumSet,EnumMap的应用

    7.Enum原理分析

    8.总结

    1.背景

    在java语言中还没有引入枚举类型之前,表示枚举类型的常用模式是声明一组具有int常量。之前我们通常利用public final static 方法定义的代码如下,分别用1 表示春天,2表示夏天,3表示秋天,4表示冬天。

    1 public class Season { 
    2     public static final int SPRING = 1; 
    3     public static final int SUMMER = 2; 
    4     public static final int AUTUMN = 3;  
    5     public static final int WINTER = 4;
    6  } 

    这种方法称作int枚举模式。可这种模式有什么问题呢,我们都用了那么久了,应该没问题的。通常我们写出来的代码都会考虑它的安全性、易用性和可读性。 首先我们来考虑一下它的类型安全性。当然这种模式不是类型安全的。比如说我们设计一个函数,要求传入春夏秋冬的某个值。

    但是使用int类型,我们无法保证传入的值为合法。代码如下所示:

     1 private String getChineseSeason(int season){   
     2     StringBuffer result = new StringBuffer();   
     3     switch(season){    
     4         case Season.SPRING :     
     5         result.append("春天");     
     6         break;    
     7         case Season.SUMMER :     
     8         result.append("夏天");     
     9         break;    
    10         case Season.AUTUMN :     
    11         result.append("秋天");     
    12         break;    
    13         case Season.WINTER :    
    14         result.append("冬天");     
    15         break;    
    16         default :     
    17         result.append("地球没有的季节");     
    18         break;   
    19         }   
    20         return result.toString();  
    21 }    
    22 public void doSomething(){           
    23       System.out.println(this.getChineseSeason(Season.SPRING));//这是正常的场景     System.out.println(this.getChineseSeason(5));//这个却是不正常的场景,这就导致了类型不安全问题  
    24 }        

    程序getChineseSeason(Season.SPRING)是我们预期的使用方法。可getChineseSeason(5)显然就不是了,而且编译很通过,在运行时会出现什么情况,我们就不得而知了。这显然就不符合Java程序的类型安全。

    接下来我们来考虑一下这种模式的可读性。使用枚举的大多数场合,我都需要方便得到枚举类型的字符串表达式。如果将int枚举常量打印出来,我们所见到的就是一组数字,这是没什么太大的用处。我们可能会想到使用String常量代替int常量。虽然它为这些常量提供了可打印的字符串,但是它会导致性能问题,因为它依赖于字符串的比较操作,所以这种模式也是我们不期望的。

    从类型安全性和程序可读性两方面考虑,int和String枚举模式的缺点就显露出来了。幸运的是,从Java1.5发行版本开始,就提出了另一种可以替代的解决方案,可以避免int和String枚举模式的缺点,并提供了许多额外的好处。那就是枚举类型(enum type)。接下来的章节将介绍枚举类型的定义、特征、应用场景和优缺点。

    2.定义

     创建枚举类型要使用 enum 关键字,隐含了所创建的类型都是 java.lang.Enum 类的子类(java.lang.Enum 是一个抽象类)。

    枚举类型符合通用模式 Class Enum<E extends Enum<E>>,而 E 表示枚举类型的名称。枚举类型的每一个值都将映射到 protected Enum(String name, int ordinal) 构造函数中,在这里,每个值的名称都被转换成一个字符串,并且序数设置表示了此设置被创建的顺序。

    枚举类型(enum type)是指由一组固定的常量组成合法的类型。Java中由关键字enum来定义一个枚举类型。下面就是java枚举类型的定义。

    1 public enum Season { 
    2     SPRING, SUMMER, AUTUMN, WINER; 
    3 } 

    3.特点
    Java定义枚举类型的语句很简约。它有以下特点:

    1) 使用关键字enum

    2) 类型名称,比如这里的Season

    3) 一串允许的值,比如上面定义的春夏秋冬四季

    4) 枚举可以单独定义在一个文件中,也可以嵌在其它Java类中。

    除了这样的基本要求外,用户还有一些其他选择

    5) 枚举可以实现一个或多个接口(Interface)

    6) 可以定义新的变量

    7) 可以定义新的方法

    8) 可以定义根据具体枚举值而相异的类

    4.enum对象常用的方法介绍

    int compareTo(E o) 
              比较此枚举与指定对象的顺序。

    Class<E> getDeclaringClass() 
              返回与此枚举常量的枚举类型相对应的 Class 对象。

    String name() 
              返回此枚举常量的名称,在其枚举声明中对其进行声明。

    int ordinal() 
              返回枚举常量的序数(它在枚举声明中的位置,其中初始常量序数为零)。

    String toString()

            返回枚举常量的名称,它包含在声明中。

    static <T extends Enum<T>> T valueOf(Class<T> enumType, String name) 
              返回带指定名称的指定枚举类型的枚举常量。

    public class Test {
        public static void main(String[] args) {
            EnumTest test = EnumTest.TUE;
            
            //compareTo(E o)
            switch (test.compareTo(EnumTest.MON)) {
            case -1:
                System.out.println("TUE 在 MON 之前");
                break;
            case 1:
                System.out.println("TUE 在 MON 之后");
                break;
            default:
                System.out.println("TUE 与 MON 在同一位置");
                break;
            }
            
            //getDeclaringClass()
            System.out.println("getDeclaringClass(): " + test.getDeclaringClass().getName());
            
            //name() 和  toString()
            System.out.println("name(): " + test.name());
            System.out.println("toString(): " + test.toString());
            
            //ordinal(), 返回值是从 0 开始
            System.out.println("ordinal(): " + test.ordinal());
        }
    }

    输出结果:

    TUE 在 MON 之后
    getDeclaringClass(): com.hmw.test.EnumTest
    name(): TUE
    toString(): TUE
    ordinal(): 1

    5.应用场景
    以在背景中提到的类型安全为例,用枚举类型重写那段代码。代码如下:

     public enum Season {  
     SPRING(1), SUMMER(2), AUTUMN(3), WINTER(4);    
             private int code;  
             private Season(int code){   
             this.code = code;  
             }    
             public int getCode(){   
             return code;  
             } 
     }
    public class UseSeason {  
    /**   
    * 将英文的季节转换成中文季节   
    * @param season   
    * @return   
    */
    public String getChineseSeason(Season season){   
    StringBuffer result = new StringBuffer();   
    switch(season){    
        case SPRING :     
        result.append("[中文:春天,枚举常量:" + season.name() + ",数据:" + season.getCode() + "]");     
        break;    
        case AUTUMN :     
        result.append("[中文:秋天,枚举常量:" + season.name() + ",数据:" + season.getCode() + "]");     
        break;    
        case SUMMER :      
        result.append("[中文:夏天,枚举常量:" + season.name() + ",数据:" + season.getCode() + "]");     
        break;    
        case WINTER :     
        result.append("[中文:冬天,枚举常量:" + season.name() + ",数据:" + season.getCode() + "]");     
        break;    
        default :     
        result.append("地球没有的季节 " + season.name());     
        break;   
        }   
      return result.toString();
      }
        public void doSomething(){
         for(Season s : Season.values()){        
        System.out.println(getChineseSeason(s));//这是正常的场景   } //System.out.println(getChineseSeason(5)); //此处已经是编译不通过了,这就保证了类型安全 }   public static void main(String[] arg){ UseSeason useSeason = new UseSeason();
        useSeason.doSomething(); }
    }

    [中文:春天,枚举常量:SPRING,数据:1]

    [中文:夏天,枚举常量:SUMMER,数据:2]

    [中文:秋天,枚举常量:AUTUMN,数据:3]

    [中文:冬天,枚举常量:WINTER,数据:4]

    这里有一个问题,为什么我要将域添加到枚举类型中呢?目的是想将数据与它的常量关联起来。如1代表春天,2代表夏天。

    6.EnumSet,EnumMap的应用

    6.EnumSet,EnumMap的应用

    public class Test {
        public static void main(String[] args) {
            // EnumSet的使用
            EnumSet<EnumTest> weekSet = EnumSet.allOf(EnumTest.class);
            for (EnumTest day : weekSet) {
                System.out.println(day);
            }
    
            // EnumMap的使用
            EnumMap<EnumTest, String> weekMap = new EnumMap(EnumTest.class);
            weekMap.put(EnumTest.MON, "星期一");
            weekMap.put(EnumTest.TUE, "星期二");
            // ... ...
            for (Iterator<Entry<EnumTest, String>> iter = weekMap.entrySet().iterator(); iter.hasNext();) {
                Entry<EnumTest, String> entry = iter.next();
                System.out.println(entry.getKey().name() + ":" + entry.getValue());
            }
        }
    }

    7.Enum原理分析

      enum 的语法结构尽管和 class 的语法不一样,但是经过编译器编译之后产生的是一个class文件。该class文件经过反编译可以看到实际上是生成了一个类,该类继承了java.lang.Enum<E>。EnumTest 经过反编译(javap com.hmw.test.EnumTest 命令)之后得到的内容如下:

    public class com.hmw.test.EnumTest extends java.lang.Enum{
        public static final com.hmw.test.EnumTest MON;
        public static final com.hmw.test.EnumTest TUE;
        public static final com.hmw.test.EnumTest WED;
        public static final com.hmw.test.EnumTest THU;
        public static final com.hmw.test.EnumTest FRI;
        public static final com.hmw.test.EnumTest SAT;
        public static final com.hmw.test.EnumTest SUN;
        static {};
        public int getValue();
        public boolean isRest();
        public static com.hmw.test.EnumTest[] values();
        public static com.hmw.test.EnumTest valueOf(java.lang.String);
        com.hmw.test.EnumTest(java.lang.String, int, int, com.hmw.test.EnumTest);
    }

    所以,实际上 enum 就是一个 class,只不过 java 编译器帮我们做了语法的解析和编译而已。

    8.总结
    那么什么时候应该使用枚举呢?每当需要一组固定的常量的时候,如一周的天数、一年四季等。或者是在我们编译前就知道其包含的所有值的集合。Java 1.5的枚举能满足绝大部分程序员的要求的,它的简明,易用的特点是很突出的。

    用法一:常量

    public enum Color {   
        RED, GREEN, BLANK, YELLOW  
    } 

    用法二:switch

    enum Signal {   
        GREEN, YELLOW, RED  
        }  
    public class TrafficLight {   
        Signal color = Signal.RED;   
        public void change() {    
            switch (color) {    
                case RED:     
                color = Signal.GREEN;     
                break;    
                case YELLOW:     
                color = Signal.RED;     
                break;    
                case GREEN:     
                color = Signal.YELLOW;     
                break;    
                }   
            }  
        } 
                

    用法三:向枚举中添加新方法

    public enum Color {   
    RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);   
    // 成员变量   
        private String name;   
        private int index;   
    // 构造方法   
        private Color(String name, int index) {    
        this.name = name;    
        this.index = index;   
        }   
    // 普通方法   
    public static String getName(int index) {    
        for (Color c : Color.values()) {     
                if (c.getIndex() == index) {      
                    return c.name;     
                    }    
                }    
            return null;   
        }   
        // get set 方法   
        public String getName() {    
            return name;   
        }   
        public void setName(String name) {    
            this.name = name;   
        }   
        public int getIndex() {    
            return index;   
        }   
        public void setIndex(int index) {    
            this.index = index;   
        }  
    } 
            

    用法四:覆盖枚举的方法

     1 public enum Color {   
     2 RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);   
     3 // 成员变量   
     4 private String name;   
     5 private int index;   
     6 // 构造方法   
     7 private Color(String name, int index) {
     8     this.name = name;
     9     this.index = index;
    10    }   
    11 //覆盖方法   
    12 @Override  
    13 public String toString() {
    14     return this.index+"_"+this.name;
    15    }
    16   } 

    用法五:实现接口

    public interface Behaviour {
       void print();
       String getInfo();
      }
      public enum Color implements Behaviour{
       RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);
       // 成员变量
       private String name;
       private int index;
       // 构造方法
       private Color(String name, int index) {
        this.name = name;
        this.index = index;
       }
      //接口方法
       @Override  public String getInfo() {
        return this.name;
       }
       //接口方法
       @Override  public void print() {    System.out.println(this.index+":"+this.name);
       }
      } 

    用法六:使用接口组织枚举

    public interface Food {
       enum Coffee implements Food{
        BLACK_COFFEE,DECAF_COFFEE,LATTE,CAPPUCCINO
       }
       enum Dessert implements Food{
        FRUIT, CAKE, GELATO
       }
      } 

     参考资源:

    http://www.jb51.net/article/78351.htm

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

  • 相关阅读:
    Mysql登录错误:ERROR 1045 (28000): Plugin caching_sha2_password could not be loaded
    Docker配置LNMP环境
    Docker安装mysqli扩展和gd扩展
    Docker常用命令
    Ubuntu常用命令
    单例模式的优缺点和使用场景
    ABP 多租户数据共享
    ABP Core 后台Angular+Ng-Zorro 图片上传
    ERROR Error: If ngModel is used within a form tag, either the name attribute must be set or the form control must be defined as 'standalone' in ngModelOptions.
    AbpCore 执行迁移文件生成数据库报错 Could not find root folder of the web project!
  • 原文地址:https://www.cnblogs.com/winsker/p/6762512.html
Copyright © 2011-2022 走看看