java enum(枚举)使用详解 + 总结

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

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

1. 通常的定义常量

2. Enum语法(定义)

3. Enum的常用用法

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

5. enum 自定义属性和方法

6. 原理分析

总结

通常的定义常量

public class IConstants {
    publicfinal static String MON = "Mon";
    publicfinalstaticString TUE = "Tue";
    publicfinalstaticString WED = "Wed";
    publicfinalstaticString THU = "Thu";
    publicfinalstaticString FRI = "Fri";
    publicfinalstaticString SAT = "Sat";
    publicfinalstaticString SUN = "Sun";
}

Enum的定义

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

package  com.hmw.test;
/**
  * 枚举测试类
  * @author <a href="mailto:hemingwang0902@126.com">何明旺</a>
  */
public  enum  EnumTest {
     MON, TUE, WED, THU, FRI, SAT, SUN;
}

这段代码实际上调用了7次 Enum(String name, int ordinal):

new  Enum<EnumTest>( "MON" , 0 );
new  Enum<EnumTest>( "TUE" , 1 );
new  Enum<EnumTest>( "WED" , 2 );
     ... ...

Enum的常用用法

用法一:常量

在JDK1.5 之前,我们定义常量都是: public static fianl.... 。现在好了,有了枚举,可以把相关的常量分组到一个枚举类型里,而且枚举提供了比常量更多的方法。

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

用法二:对enum进行遍历

对enum进行遍历操作示例代码:

public  class  Test {
     public  static  void  main(String[] args) {
         for  (EnumTest e : EnumTest.values()) {
             System.out.println(e.toString());
         }
     }
}
输出结果:
MON
TUE
WED
THU
FRI
SAT
SUN

用法三:switch

JDK1.6之前的switch语句只支持int,char,enum类型,使用枚举,能让我们的代码可读性更强。

复制代码
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;
            }
        }
    }
复制代码

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

如果打算自定义自己的方法,那么必须enum实例序列的最后添加一个分号。而且 Java 要求必须先定义 enum 实例。

定义枚举的时候,应该注意一下几个问题:

构造方法应该是private,为什么呢?根据我的理解,因为枚举是个常量,枚举的意思就是有限的可以列举的几个选项,如果其构造方法可以是public的,那么就违背了枚举的初衷,所以,枚举的构造方法是private的


复制代码
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;
        }
    }
复制代码

用法五:覆盖枚举的方法

下面给出一个toString()方法覆盖的例子。

复制代码
public class Test {
    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;
        }

        // 覆盖方法
        @Override
        public String toString() {
            return this.index + "_" + this.name;
        }
    }

    public static void main(String[] args) {
        System.out.println(Color.RED.toString());
    }
}
复制代码

用法六:实现接口

所有的枚举都继承自java.lang.Enum类。由于Java 不支持多继承,所以枚举对象不能再继承其他类。

复制代码
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
        }
    }
复制代码

用法八:关于枚举集合的使用

java.util.EnumSet和java.util.EnumMap是两个枚举集合。EnumSet保证集合中的元素不重复;EnumMap中的 key是enum类型,而value则可以是任意类型。关于这个两个集合的使用就不在这里赘述,可以参考JDK文档

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

 enum 自定义属性和方法


给 enum 对象加一下 value 的属性和 getValue() 的方法:

package com.hmw.test;
 
/**
 * 枚举测试类
 *
 * @author <a href="mailto:hemingwang0902@126.com">何明旺</a>
 */
public enum EnumTest {
    MON(1), TUE(2), WED(3), THU(4), FRI(5), SAT(6) {
        @Override
        public boolean isRest() {
            return true;
        }
    },
    SUN(0) {
        @Override
        public boolean isRest() {
            return true;
        }
    };
 
    private int value;
 
    private EnumTest(int value) {
        this.value = value;
    }
 
    public int getValue() {
        return value;
    }
 
    public boolean isRest() {
        return false;
    }
}


public class Test {
    public static void main(String[] args) {
        System.out.println("EnumTest.FRI 的 value = " + EnumTest.FRI.getValue());
    }
}

输出结果:

EnumTest.FRI 的 value = 5

原理分析

       

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

public class com.hmw.test.EnumTestextends 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 编译器帮我们做了语法的解析和编译而已。

总结

    

可以把 enum 看成是一个普通的 class,它们都可以定义一些属性和方法,不同之处是:enum 不能使用 extends 关键字继承其他类,因为 enum 已经继承了 java.lang.Enum(java是单一继承)。


--------------------------------------------


测试代码。


[java]  view plain  copy
  1. package com.lxk.enumTest;  
  2.   
  3. /** 
  4.  * Java枚举用法测试 
  5.  * <p> 
  6.  * Created by lxk on 2016/12/15 
  7.  */  
  8. public class EnumTest {  
  9.     public static void main(String[] args) {  
  10.         forEnum();  
  11.         useEnumInJava();  
  12.     }  
  13.   
  14.     /** 
  15.      * 循环枚举,输出ordinal属性;若枚举有内部属性,则也输出。(说的就是我定义的TYPE类型的枚举的typeName属性) 
  16.      */  
  17.     private static void forEnum() {  
  18.         for (SimpleEnum simpleEnum : SimpleEnum.values()) {  
  19.             System.out.println(simpleEnum + "  ordinal  " + simpleEnum.ordinal());  
  20.         }  
  21.         System.out.println("------------------");  
  22.         for (TYPE type : TYPE.values()) {  
  23.             System.out.println("type = " + type + "    type.name = " + type.name() + "   typeName = " + type.getTypeName() + "   ordinal = " + type.ordinal());  
  24.         }  
  25.     }  
  26.   
  27.     /** 
  28.      * 在Java代码使用枚举 
  29.      */  
  30.     private static void useEnumInJava() {  
  31.         String typeName = "f5";  
  32.         TYPE type = TYPE.fromTypeName(typeName);  
  33.         if (TYPE.BALANCE.equals(type)) {  
  34.             System.out.println("根据字符串获得的枚举类型实例跟枚举常量一致");  
  35.         } else {  
  36.             System.out.println("大师兄代码错误");  
  37.         }  
  38.   
  39.     }  
  40.   
  41.     /** 
  42.      * 季节枚举(不带参数的枚举常量)这个是最简单的枚举使用实例 
  43.      * Ordinal 属性,对应的就是排列顺序,从0开始。 
  44.      */  
  45.     private enum SimpleEnum {  
  46.         SPRING,  
  47.         SUMMER,  
  48.         AUTUMN,  
  49.         WINTER  
  50.     }  
  51.   
  52.   
  53.     /** 
  54.      * 常用类型(带参数的枚举常量,这个只是在书上不常见,实际使用还是很多的,看懂这个,使用就不是问题啦。) 
  55.      */  
  56.     private enum TYPE {  
  57.         FIREWALL("firewall"),  
  58.         SECRET("secretMac"),  
  59.         BALANCE("f5");  
  60.   
  61.         private String typeName;  
  62.   
  63.         TYPE(String typeName) {  
  64.             this.typeName = typeName;  
  65.         }  
  66.   
  67.         /** 
  68.          * 根据类型的名称,返回类型的枚举实例。 
  69.          * 
  70.          * @param typeName 类型名称 
  71.          */  
  72.         public static TYPE fromTypeName(String typeName) {  
  73.             for (TYPE type : TYPE.values()) {  
  74.                 if (type.getTypeName().equals(typeName)) {  
  75.                     return type;  
  76.                 }  
  77.             }  
  78.             return null;  
  79.         }  
  80.   
  81.         public String getTypeName() {  
  82.             return this.typeName;  
  83.         }  
  84.     }  
  85. }  



然后是测试的结果图:



简单的例子,大家基本都用过,看不懂的基本都是第二个例子。可以看到,在第二个例子里面,后面带有参数,其实可以这么理解。

enum这个关键字,可以理解为跟class差不多,这也个单独的类。可以看到,上面的例子里面有属性,有构造方法,有getter,也可以有setter,但是一般都是构造传参数。还有其他自定义方法。那么在这些东西前面的,以逗号隔开的,最后以分号结尾的,这部分叫做,这个枚举的实例。也可以理解为,class  new 出来的实例对象。这下就好理解了。只是,class,new对象,可以自己随便new,想几个就几个,而这个enum关键字,他就不行,他的实例对象,只能在这个enum里面体现。也就是说,他对应的实例是有限的。这也就是枚举的好处了,限制了某些东西的范围,举个栗子:一年四季,只能有春夏秋冬,你要是字符串表示的话,那就海了去了,但是,要用枚举类型的话,你在enum的大括号里面把所有的选项,全列出来,那么这个季节的属性,对应的值,只能在里面挑。不能有其他的。

我上面的例子,就是根据typeName,你可以从那些例子里面挑选到唯一的一个TYPE类型的枚举实例--TYPE.BALANCE。注意方法

TYPE type = TYPE.fromTypeName(typeName);
这个方法的返回类型就是这个TYPE枚举类型的。
这下就好理解,这个枚举是怎么在工作了吧

再补充一下:

上面那个带参数的枚举类型的实例里面实际上是三个属性,除了我自定义的typeName以外,还有2个是系统自带的。看下面源码的图:



看到这里之后,不知道你能不能理解下面图片内说明的话:下面图片主要说明在使用枚举时,的规范和标准。希望可以在实际开发时候用到



最后补充一点:

也许你知道呢,但是也许你不知道呢?我是真的不知道,测了之后才知道!!!

枚举类型对象之间的值比较,是可以使用==,直接来比较值,是否相等的,不是必须使用equals方法的哟。

具体,请参考下面的链接:

java 枚举类比较是用==还是equals?



2017.11.07 更新

有的老铁,说这个switch case怎么写,我就在下面再啰嗦一下。

[java]  view plain  copy
  1. private static void testSwitchCase() {  
  2.     String typeName = "f5";  
  3.     //这几行注释呢,你可以试着三选一,测试一下效果。  
  4.     //String typeName = "firewall";  
  5.     //String typeName = "secretMac";  
  6.     TypeEnum typeEnum = TypeEnum.fromTypeName(typeName);  
  7.     if (typeEnum == null) {  
  8.         return;  
  9.     }  
  10.     switch (typeEnum) {  
  11.         case FIREWALL:  
  12.             System.out.println("枚举名称(即默认自带的属性 name 的值)是:" + typeEnum.name());  
  13.             System.out.println("排序值(默认自带的属性 ordinal 的值)是:" + typeEnum.ordinal());  
  14.             System.out.println("枚举的自定义属性 typeName 的值是:" + typeEnum.getTypeName());  
  15.             break;  
  16.         case SECRET:  
  17.             System.out.println("枚举名称(即默认自带的属性 name 的值)是:" + typeEnum.name());  
  18.             System.out.println("排序值(默认自带的属性 ordinal 的值)是:" + typeEnum.ordinal());  
  19.             System.out.println("枚举的自定义属性 typeName 的值是:" + typeEnum.getTypeName());  
  20.             break;  
  21.         case BALANCE:  
  22.             System.out.println("枚举名称(即默认自带的属性 name 的值)是:" + typeEnum.name());  
  23.             System.out.println("排序值(默认自带的属性 ordinal 的值)是:" + typeEnum.ordinal());  
  24.             System.out.println("枚举的自定义属性 typeName 的值是:" + typeEnum.getTypeName());  
  25.             break;  
  26.         default:  
  27.             System.out.println("default");  
  28.     }  
  29. }  

然后,就是运行结果的截图。


老铁们,看完这个枚举,你要懂个概念,那就是,这个枚举,他是个对象,就像你定义的Student类,Person类,等等一些个类一样。

要有这么个概念。只要是个类,他就可以有构造函数,可以有属性,可以有方法。

对的,老铁,你对这个属性,构造函数啥的,有概念吧,没有的话,我可就郁闷啦。

然后,你就看到,这个地方有2个默认的属性,一个是name,一个是ordinal,这2个属性就像你定义Student类和Person类的name和age一样,

只不过,这2个是系统自带的属性,不用你自己去定义啦。

你也可以给这个枚举类,也就是你自己声明的枚举,随便加属性。

我上面代码例子里面的那个TypeEnum那个枚举,就是这么干的,就简单的添加了个自定义属性typeName,

虽然他有自己的name了,那姑且叫我这个自定义的属性叫别名吧。

可以看到,我例子里面就是通过自己写的那个构造方法给我这个自定义的属性初始化值的。

还有,这个构造方法是不可以,也不被运行public的,不信,你可以试试。

还有,你不能对系统自带的name属性,在构造函数里面赋值,没有为什么


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值