zoukankan      html  css  js  c++  java
  • 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,可知枚举项是常量,在定义枚举项时尽量用大写。

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

  • 相关阅读:
    委托系列整理
    EF Lambda 多表查询
    枚举,Enum,常规使用demo记录
    自定义Window 服务
    xpath 操作XML
    MVC 自定义过滤器
    时间比对,常用细节记录
    Lock锁_线程_线程域
    break、continue和goto 三者作用介绍
    .net 学习路线感想
  • 原文地址:https://www.cnblogs.com/SQP51312/p/6066927.html
Copyright © 2011-2022 走看看