Enum 类型

枚举类型(Enumerated Type)

什么是枚举?

枚举是一个被命名的整型常数的集合。在多种编程语言中都有使用(C/C++/c#/java等

示例

public enum Size {
    S, M, L
};

为什么使用枚举?

在JDK1.5 之前,定义常量都是: public static fianl …… 。

类常量

public class Size {
    public static final int S = 1;
    public static final int M = 2;
    public static final int L = 3;
}

接口常量

public interface Size {
    public static final int S = 1;
    public static final int M = 2;
    public static final int L = 3;
}

 使用

System.out.println(Size.L);//3

但是这种方式存在一些缺陷,详见下面使用枚举与直接定义常量的比较。

所以在5.0 版本 SDK中,在语言层面上增加了枚举类型。

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

枚举可以把相关的常量分组到一个枚举类型里,而且枚举提供了比常量更多的方法(如添加字段,方法,遍历访问)。

枚举可以用在变量的类型匹配的检测中,可以提前发现编程错误。

枚举定义了一个变量的可使用范围。能够使程序可读性提高。

在编译期间限定类型,不允许发生越界的情况。 

和一般的类中使用接口一样,enum枚举中同样可以实现接口,并实现接口中的所有方法,这样做的好处在于可以更方便地对枚举中的值进行排序、比较等操作,封装性更好。


使用枚举与直接定义常量的比较

1、两种引用方式相同,都是类名.属性”。

2、常量是直接编译在代码中的,而枚举则是一种类,你可以通过反射根据值反查出它的枚举形式是什么。

3、枚举可只定义枚举项而不定义枚举值,而类常量(或接口常量)则必须定义值,否则编译失败;

4、常量不是数据类型,只是说明被定义数据是不可变的常量;枚举是一种数据类型。

5、枚举定义了一组类型相同的成员且成员的语义清晰;常量也可以是一个数组(比如int[]),虽然其中元素类型相同,但是没法清晰指出其中元素的意义。

6、类常量可以被继承,枚举类型是不能有继承的,也就是说一个枚举常量定义完毕后,除非修改重构,否则无法做扩展。 


注意:

enum 类型不支持 public 和 protected 修饰符的构造方法,因此构造函数一定要是 private 或 默认的。所以枚举对象是无法在程序中通过直接调用其构造方法来初始化的。

所有的枚举都继承自java.lang.Enum类。由于Java 不支持多继承,所以枚举对象不能再继承其他类。enum枚举类型是一个不可以被继承的final类。

定义 enum 类型时候,如果是简单类型,那么最后一个枚举值后不用跟任何一个符号;但如果有定制方法,那么最后一个枚举值与后面代码要用分号';'隔开,不能用逗号或空格。

由于 enum 类型的值实际上是通过运行期构造出对象来表示的,所以在 cluster(集群) 环境下,每个虚拟机都会构造出一个同义的枚举对象。因而在做比较操作时候就需要注意,如果直接通过使用等号 ( ‘ == ’ ) 操作符,这些看似一样的枚举值一定不相等,因为这不是同一个对象实例。


方法

1、toString

返回枚举常量名。

System.out.println(Size.L.toString());//L

2、valueOf(Class, String)

Size s = (Size) Enum.valueOf(Size.class, "L");
System.out.println(s.toString());// L

3、values()

返回一个包含全部枚举值的数组。

Size[] arr = Size.values();
        for (Size s : arr) {
            System.out.println(s);//依次输出S、M、L
        }

4、ordinal()

返回枚举常量的位置,位置从0开始计数。

System.out.println(Size.M.ordinal());//1

枚举集合(EnumSet)

JDK5.0 中也增加了两个工具类 EnumSet 和 EnumMap,都放在 java.util 包中。

EnumSet 是一个针对枚举类型的高性能的 Set 接口实现。EnumSet 中装入的所有枚举对象都必须是同一种类型。

EnumSet保证集合中的元素不重复;

枚举 set 在内部表示为位向量。此表示形式非常紧凑且高效。此类的空间和时间性能应该很好,足以用作传统上基于 int 的“位标志”的替换形式,具有高品质、类型安全的优势。

如果其参数也是一个枚举 set,则批量操作(如 containsAllretainAll)也应运行得非常快。 

EnumSet 支持在枚举类型的所有值的某个范围中进行迭代。

EnumMap中的 key是enum类型,而value则可以是任意类型。

注意

像大多数 collection 实现一样,EnumSet 是不同步的。

如果多个线程同时访问一个枚举 set,并且至少有一个线程修改该 set,则此枚举 set 在外部应该是同步的。这通常是通过对自然封装该枚举 set 的对象执行同步操作来完成的。

如果不存在这样的对象,则应该使用 Collections.synchronizedSet(java.util.Set) 方法来“包装”该 set。最好在创建时完成这一操作,以防止意外的非同步访问。

 Set<MyEnum> s = Collections.synchronizedSet(EnumSet.noneOf(MyEnum.class));

方法

1、range(E from, E to)

创建一个最初包含由两个指定端点所定义范围内的所有元素的枚举 set。

for (Size s : EnumSet.range(Size.M, Size.L)) {
            System.out.println(s);// 依次打印M、L
        }

2、有多个of方法用于创建一个最初包含指定元素的枚举 set。


枚举集合(EnumMap)

EnumMap 是一个高性能的 Map 接口实现。

EnumMap中的 key是enum类型,而value则可以是任意类型。

枚举映射在内部表示为数组。此表示形式非常紧凑且高效。

EnumMap 将丰富的和安全的 Map 接口与数组快速访问结合到一起,如果你希望要将一个枚举类型映射到一个值,你应该使用 EnumMap。

枚举映射根据其键的自然顺序 来维护(该顺序是声明枚举常量的顺序)。

注意

像大多数 collection 一样,EnumMap不同步的。

如果多个线程同时访问一个枚举映射,并且至少有一个线程修改该映射,则此枚举映射在外部应该是同步的。这一般通过对自然封装该枚举映射的某个对象进行同步来完成。

如果不存在这样的对象,则应该使用 Collections.synchronizedMap(java.util.Map<k, v="">) 方法来“包装”该枚举。最好在创建时完成这一操作,以防止意外的非同步访问。

     Map<EnumKey, V> m
         = Collections.synchronizedMap(new EnumMap<EnumKey, V>(...));

应用

switch (Size.M) {
        case S:
            System.out.println("小");
            break;
        case M:
            System.out.println("中 ");
            break;
        default:
            System.out.println("无");
        }

自定义

public enum Size {
    // Java要求必须先定义 enum 实例。必须在enum实例序列的最后添加一个分号。
    S(1, "small", "小"), M(2, "middle", "中"), L(3, "large", "大");
    // 定义私有变量
    private int code;
    private String en;
    private String cn;

    // 构造函数,只能为私有或默认
    private Size(int code, String en, String cn) {
        this.code = code;
        this.en = en;
        this.cn = cn;
    }

    // 覆盖toString方法
    public String toString() {
        return this.name() + "(编号:" + this.code + ",英文:" + this.en + ",中文:"
                + this.cn + ")";
    }

    public int getCode() {
        return code;
    }

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

    public String getEn() {
        return en;
    }

    public void setEn(String en) {
        this.en = en;
    }

    public String getCn() {
        return cn;
    }

    public void setCn(String cn) {
        this.cn = cn;
    }
}

调用

Size[] arr = Size.values();
for (Size s : arr) {
System.out.println(s);
}

打印

S(编号:1,英文:small,中文:小)
M(编号:2,英文:middle,中文:中)
L(编号:3,英文:large,中文:大)

enum与class的关系?

代码:

public enum Size {
    S, M, L
};

反编译:

反编译后可以发现:

1、生成了一个Size类,该类继承了java.lang.Enum。

2、Size类被final修饰,可知枚举不能被继承。

3、属性是public static final,可知枚举项是常量,在定义枚举项时尽量用大写。

可以把枚举看成是一个不能被继承的类。

原文地址:https://www.cnblogs.com/SQP51312/p/6066927.html