首页 > 技术文章 > 大话 java枚举

DDgougou 2020-07-21 09:36 原文

  说实话,本人之前对java的枚举并不是特别的了解,直到到了新公司,看到好多都在用枚举,渐渐的了解了。在这里我总结一下,查漏补缺吧。

一、优点

1)增强代码可读性。
2)传递参数错误。
3)去除equals两者判断:由于常量值地址唯一,使用枚举可以直接通过“==”进行两个值之间的对比,性能会有所提高。
4)编译优势(与常量类相比):常量类编译时,常量被直接编译进二进制代码中,常量值在升级中变化后,需要重新编译引用常量的类,因为二进制代码中存放的是旧值。枚举类编译时,没有把常量值编译到代码中,即使常量值发生改变,也不会影响引用常量的类。
5)修改优势(与常量类相比):枚举类编译后默认final class,不允许继承可防止被子类修改。常量类可被继承修改、增加字段等,易导致父类不兼容。
6)枚举型可直接与数据库交互。
7)Switch语句优势:使用int、String类型switch时,当出现参数不确定的情况,偶尔会出现越界的现象,这样我们就需要做容错操作(if条件筛选等),使用枚举,编译期间限定类型,不允许发生越界。

 

 二、创建

这是一个最基本的枚举定义,我感觉有以下几个特点:

1) 定义时,使用了关键字 enum ,而不是class,或者interface;使用了enum,相当于被 static(能静态调用),final(不能被重写)修饰;

2) 枚举的成员一般大写,之间用逗号,末尾用封号;

3) 枚举也需要构造方法、属性、setter方法、getter方法;

4) 尽量避免使用java.lang.Enum中的已有属性,如name,ordinal。不然可能会混淆——下文涉及;

5) 尽量避免使用ordinal属性判断枚举成员。因为ordinal属性返回成员的索引,如下代码MAN返回0,WOMAN返回1,但是只要你在枚举定义的时候,错乱了顺序,或者在枚举成员之前添加新的成员,都会导致ordinal的值发生变化。试想一个例子,假如没有code,你数据库中根据ordinal存入了男(索引0),女(索引1),但是后来随着开发的必要,你感觉应该加一个未知性别,所以你就不小心在MAN和WOMAN的之间加了UNKNOW(2,"未知")。最终是这样 MAN(1,“男”),UNKNOW(2,“未知”),WOMAN(0,“女”),这时WOMAN的ordinal值已经变为了2,不是1了,所以数据库发生脏数据了。所以为了避免发生意外,尽量避免使用,或者严格规定,添加枚举成员只能在后面追加,不能中间插入。

6)构造方法,枚举的构造方法默认是private,而且是强制的;

7)枚举值默认被public static final修饰;

public enum SexEnum {
    //
    MAN(1, "男"),

    //
    WOMAN(0, "女");

    /**
     * 属性
     */
    private int code;

    private String desc;

    /**
     * 构造方法
     * @param code
     * @param desc
     */
    SexEnum(Integer code, String desc) {
        this.code = code;
        this.desc = desc;
    }

    /**
     * getter and setter
     * @return
     */
    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }
}

 

三、API

   java里面的枚举自动继承了java.lang.Enum,由于java不能多继承,所以枚举不能再继承其他类了。这是java.lang.Enum的源码,我们看到它实现了Comparable接口,所以java里面的所有枚举都是可以进行比较的。其中有几个重要的方法:

1)name():返回枚举成员的名称;toString就是返回name值。

2)ordinal():返回枚举成员的索引;

3)compareTo():根据ordinal的值比较大小;

  a<b,a.compareTo(b) = -1;

  a=b,a.compareTo(b) = 0;

  a>b,a.compareTo(b) = 1;

4)valueOf():将一个字符串映射成枚举;不存在就转化异常。

5)values():获取该枚举对象的所有枚举值。——该方法暂未找到出处。

package java.lang;

import java.io.Serializable;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectStreamException;

public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializable {
   
    private final String name;

    public final String name() {
        return name;
    }

    private final int ordinal;

    public final int ordinal() {
        return ordinal;
    }

    protected Enum(String name, int ordinal) {
        this.name = name;
        this.ordinal = ordinal;
    }

    
    public String toString() {
        return name;
    }
    public final boolean equals(Object other) {
        return this==other;
    }
    public final int hashCode() {
        return super.hashCode();
    }
    protected final Object clone() throws CloneNotSupportedException {
        throw new CloneNotSupportedException();
    }

    
    public final int compareTo(E o) {
        Enum<?> other = (Enum<?>)o;
        Enum<E> self = this;
        if (self.getClass() != other.getClass() && self.getDeclaringClass() != other.getDeclaringClass())
            throw new ClassCastException();
        return self.ordinal - other.ordinal;
    }

    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 constant " + enumType.getCanonicalName() + "." + name);
    }

    protected final void finalize() { }
}
public class EnumTest {
    /**
     * name属性:返回枚举名称
     */
    @Test
    public void test_name(){
        //MAN
        System.out.println(SexEnum.MAN.name());
        //WOMAN
        System.out.println(SexEnum.WOMAN.name());
    }

    /**
     * ordinal属性:返回枚举索引
     */
    @Test
    public void test_ordinal(){
        //0
        System.out.println(SexEnum.MAN.ordinal());
        //1
        System.out.println(SexEnum.WOMAN.ordinal());
    }

    /**
     * compareTo():根据ordinal的值比较大小
     */
    @Test
    public void test_compareTo(){
        //-1
        System.out.println(SexEnum.MAN.compareTo(SexEnum.WOMAN));
        //1
        System.out.println(SexEnum.WOMAN.compareTo(SexEnum.MAN));
        //0
        System.out.println(SexEnum.MAN.compareTo(SexEnum.MAN));
    }

    /**
     * valueOf():将字符串映射为枚举
     */
    @Test
    public void test_valueOf(){
        //MAN
        System.out.println(SexEnum.valueOf("MAN"));
        //java.lang.IllegalArgumentException: No enum constant SexEnum.MAN123
        System.out.println(SexEnum.valueOf("MAN123"));
    }

    /**
     * values():获取该枚举对象的所有枚举值
     */
    @Test
    public void test_values(){
        SexEnum[] enums = SexEnum.values();
        for (SexEnum sexEnum:enums){
            //MAN
            //WOMAN
            System.out.println(sexEnum);
        }
    }

}

   

四、集合

  3.1、EnumMap

  因为HashMap是一种通过对key计算hashCode(),通过空间换时间的方式,直接定位到value所在的内部数组的索引,因此,查找效率非常高。如果作为key的对象是enum类型,那么,还可以使用Java集合库提供的一种EnumMap,它在内部以一个非常紧凑的数组存储value,并且根据enum类型的key直接定位到内部数组的索引,并不需要计算hashCode(),不但效率最高,而且没有额外的空间浪费。使用EnumMap的时候,我们总是用Map接口来引用它,因此,实际上把HashMap和EnumMap互换,在客户端看来没有任何区别。

  3.2、EnumSet

    同EnumSet

@Test
public void test_EnumMap(){
    Map<SexEnum,String> enumMap = new EnumMap<SexEnum, String>(SexEnum.class);
    enumMap.put(SexEnum.MAN,"这是一个男生");
    enumMap.put(SexEnum.WOMAN,"这是一个女生");
}

@Test
public void test_EnumSet(){
    EnumSet enumSet = EnumSet.allOf(SexEnum.class);
}

 

  EnumMap、EnumSet的更多资料请查看 java.util.EnumMap实战java.util.EnumSet实战

 

推荐阅读