zoukankan      html  css  js  c++  java
  • Java枚举

    Enum类源码

    package java.lang;
    
    import java.io.Serializable;
    import java.io.IOException;
    import java.io.InvalidObjectException;
    import java.io.ObjectInputStream;
    import java.io.ObjectStreamException;
    
    /**
     * 这是所有java语言枚举类型的公共基类。 
     *
     *
     * 注意,当使用枚举类型作为一个set或者Map的keys,
     * {@linkplain java.util.EnumSet set} 和{@linkplain
     * java.util.EnumMap map} 专门且高效的实现。
     *
     * @param <E>枚举类型的子类
     * @author  Josh Bloch
     * @author  Neal Gafter
     * @see     Class#getEnumConstants()
     * @see     java.util.EnumSet
     * @see     java.util.EnumMap
     * @since   1.5
     */
    public abstract class Enum<E extends Enum<E>>
            implements Comparable<E>, Serializable {
        /**
         * 枚举常量的名称,在其枚举声明中对其进行声明。
         * 大多数程序员应该使用 {@link #toString} 方法而不是直接访问此字段。
         */
        private final String name;
    
        /**
         * 返回此枚举常量的名称,在其枚举声明中对其进行声明。
         *
         * 与此方法相比,大多数程序员应该优先考虑使用 toString() 方法,因为 toString 方法返回更加用户友好的名称。
         * 该方法主要设计用于特殊情形,其正确性取决于获取正确的名称,其名称不会随版本的改变而改变。
         *
         * @return 返回此枚举常量的名称。
         */
        public final String name() {
            return name;
        }
    
        /**
         * 枚举常量的序数(它在枚举声明中的位置,其中初始常量序数为零)。
         *
         * 大多数程序员不会使用此变量。它被设计用于复杂的基于枚举的数据结构,比如 
         * {@link java.util.EnumSet} 和 {@link java.util.EnumMap}。
         */
        private final int ordinal;
    
    
        /**
         * 返回枚举常量的序数(它在枚举声明中的位置,其中初始常量序数为零)。
         *
         * 大多数程序员不会使用此方法。它被设计用于复杂的基于枚举的数据结构,比如 
         * {@link java.util.EnumSet} 和 {@link java.util.EnumMap}。
         */
        public final int ordinal() {
            return ordinal;
        }
    
        /**
         * 唯一的构造函数。程序员不能调用这个构造函数。它是利用由响应枚举类型声明编译器产生的代码。
         * @param name - 此枚举常量的名称,它是用来声明该常量的标识符。
         * @param ordinal - 枚举常量的序数(它在枚举声明中的位置,其中初始常量序数为零)。
         */
        protected Enum(String name, int ordinal) {
            this.name = name;
            this.ordinal = ordinal;
        }
    
        /**
         * 返回枚举常量的名称,它包含在声明中。可以重写此方法,虽然一般来说没有必要。当存在更加“程序员友好的”字符串形式时,应该使用枚举类型重写此方法。
         * 覆盖类 Object 中的 toString 
         * 
         * @return  枚举常量的名称 
         */
        public String toString() {
            return name;
        }
    
        /**
         * 当指定对象等于此枚举常量时,返回 true。
         * 覆盖类 Object 中的 equals 
         * 
         * @param other 要与此对象进行相等性比较的对象。
         * @return   如果指定对象等于此枚举常量,则返回 true。
         */
        public final boolean equals(Object other) {
            return this==other;
        }
    
        /**
         * 返回枚举常量的哈希码。
         * 覆盖类 Object 中的 hashCode 
         *
         * @return 枚举常量的哈希码。
         */
        public final int hashCode() {
            return super.hashCode();
        }
    
        /**
         * 抛出 CloneNotSupportedException。这可保证永远不会复制枚举,以保证枚举是单例的。
         * 覆盖类 Object 中的clone
         * 
         * @return (不返回)
         */
        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() && // optimization
                self.getDeclaringClass() != other.getDeclaringClass())
                throw new ClassCastException();
            return self.ordinal - other.ordinal;
        }
    
        /**
         * 返回与此枚举常量的枚举类型相对应的 Class 对象。当且仅当 e1.getDeclaringClass() == e2.getDeclaringClass() 时,
         * 两个枚举常量 e1 和 e2 的枚举类型才相同。(由该方法返回的值不同于由 Object.getClass() 方法返回的值, Object.getClass() 
         * 方法用于带有特定常量的类主体的枚举常量。) 
         *
         * @return 与此枚举常量的枚举类型相对应的 Class 对象 
         */
        @SuppressWarnings("unchecked")
        public final Class<E> getDeclaringClass() {
            Class<?> clazz = getClass();
            Class<?> zuper = clazz.getSuperclass();
            return (zuper == Enum.class) ? (Class<E>)clazz : (Class<E>)zuper;
        }
    
        /**
         * 返回带指定名称的指定枚举类型的枚举常量。名称必须与在此类型中声明枚举常量所用的标识符完全匹配(不允许使用额外的空白字符)。
         * 
         * @param 要从中返回常量的枚举类型的 Class 对象 
         * @param 要返回的常量名称
         * @return 带指定名称的指定枚举类型的枚举常量 
         * @throws IllegalArgumentException  如果指定枚举类型不包含指定名称的常量,或者指定类对象不表示枚举类型 
         * @throws 如果 enumType 或 name 为空 
         * @since 1.5
         */
        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);
        }
    
        /**
         * 枚举类不能有 finalize 方法。 
         * 覆盖类 Object 中的 finalize。
    
         */
        protected final void finalize() { }
    
        /**
         * 禁止反序列化
         */
        private void readObject(ObjectInputStream in) throws IOException,
            ClassNotFoundException {
            throw new InvalidObjectException("can't deserialize enum");
        }
    
        private void readObjectNoData() throws ObjectStreamException {
            throw new InvalidObjectException("can't deserialize enum");
        }
    }

    源码理解

    Enum类位于java.lang包下实现了Comparable和Serializable接口,因此可以序列化,可以进行比较,并且Enum类是抽象类,因此不能被实例化,但是不能被继承。之所以不能被继承是因为枚举类使用enum定义后在编译后默认继承了java.lang.Enum类,采用enum声明后,该类会被编译器加上final声明(同String)。Enum<E extends Enum>就是一个Enum只接受一个Enum或者他的子类作为参数。相当于把一个子类或者自己当成参数,传入到自身,引起一些特别的语法效果。Enum类有两个成员变量name和ordinal,protected修饰的构造方法且唯一,重写了toString(),equals(),hashCode(),clone()和finalize()方法。clone()方法保证单例。finalize()方法这保证枚举不会拥有finalize方法(主要是为了在垃圾回收对象的时候做一些额外的清理工作)。readObject和readObjectNoData两个方法为了禁止反序列化。枚举禁止clone和禁止反序列化的原因是单例模式通常可以通过clone和反序列化来实现产生一个单例类多个不同的对象,在这里禁止clone和反序列化就能避免这种情况发生。

    Javadoc标签

    @author  作者
    @param  输入参数的名称  说明
    @return 输出参数说明
    @since JDK版本
    @see 链接目标
    @throws 异常
    @deprecated 解释
    @link 链接地址
    @linkplain 设定超链接地址,还可以设定其文本。格式是{@linkplain 地址 文本}

    enum使用语法

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

    enum的使用

    1、常量

    package ecut.enums;
    
    /**
     * 所有的枚举常量必须 列在 枚举类的 首行 ( 第一个 分号之前 )
     * 所有的枚举常量 直接写 常量名称即可 ( 默认全部是 public static final )
     * 多个枚举常量之间 用 逗号 隔开
     * 所有的枚举常量在声明时已经默认调用了相应的构造方法
     *所有的 枚举类型 都继承自 java.lang.Enum
     * 
     */
    public enum Gender {
        FEMALE ,MALE
    }

    2、switch

    package ecut.enums;
    
    public class GenderTest2 {
    
        public static void main(String[] args) {
            
            Gender[] genders = Gender.values() ;
            int index = ( int )( Math.random() * 2 );
            
            Gender g = genders[ index ];
            
            switch ( g ) {
                case FEMALE:
                    System.out.println( "万寿无疆" );
                    break;
                case MALE:
                    System.out.println( "一统江湖" );
                    break;
            }
            
        }
    
    }

    3、自定义属性和方法

    package ecut.enums;
    
    /**
     * 所有的枚举常量必须 列在 枚举类的 首行 ( 第一个 分号之前 )
     * 所有的枚举常量 直接写 常量名称即可 ( 默认全部是 public static final )
     * 多个枚举常量之间 用 逗号 隔开
     * 所有的枚举常量在声明时已经默认调用了相应的构造方法
     * 
     * public static final Gender MALE = new Gender( "靓仔" );
     * 
     *所有的 枚举类型 都继承自 java.lang.Enum
     * 
     */
    public enum Gender {
        FEMALE( "靓妹") ,
        MALE( "靓仔" )
        ;
        private String genderName  ;
        
        private Gender( String name ){
            System.out.println( "Gender");
            this.genderName = name ;
        }
    
        public String getGenderName() {
            return genderName;
        }
        
    }

    4、遍历

    package ecut.enums;
    
    public class GenderTest1 {
    
        public static void main(String[] args) {
            
            Gender g = Gender.FEMALE ;
            
            System.out.println( g.getGenderName() );
            
            // 所有的枚举类型 都有一个 valueOf 方法
            // 该方法可以根据给定的 枚举常量的名称 获得 相应的枚举常量
            g = Gender.valueOf( "MALE" );
            
            System.out.println( g.getGenderName() );
            
            // 所有的枚举类型 都有一个 values 方法 返回该枚举类内部的所有枚举常量
            Gender[] gs = Gender.values();
            for( Gender e : gs ) {
                System.out.println( e.ordinal() + " , " + e.name() + " , " + e.getGenderName() );
            }
    
        }
    
    }

    运行结果如下:

    Gender
    Gender
    靓妹
    靓仔
    0 , FEMALE , 靓妹
    1 , MALE , 靓仔

     从源码中我们可以看到是没有values()方法的,但所有的枚举类型 都有一个 values 方法,这是是因为java编译器在对enum关键字进行处理时,实际上是将enum转换成为了java.lang.Enum类的一个子类来完成,而这个子类中含有values()静态方法。

      enum Color {RED, BLUE, GREEN}
    

     编译器将会把他转成如下内容:

    public final class Color extends Enum<Color> {
      public static final Color[] values() { return (Color[])$VALUES.clone(); }
      public static Color valueOf(String name) { ... }
    
      private Color(String s, int i) { super(s, i); }
    
      public static final Color RED;
      public static final Color BLUE;
      public static final Color GREEN;
    
      private static final Color $VALUES[];
    
      static {
        RED = new Color("RED", 0);
        BLUE = new Color("BLUE", 1);
        GREEN = new Color("GREEN", 2);
        $VALUES = (new Color[] { RED, BLUE, GREEN });
      }
    } 

    5、使用接口组织枚举

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

    EnumSet和EnumMap的使用

    package ecut.enums;
    
    import java.util.EnumMap;
    import java.util.EnumSet;
    import java.util.Iterator;
    import java.util.Map.Entry;
    
    public class GenderTest3 {
        public static void main(String[] args) {
            // EnumSet的使用
            EnumSet<Gender> genderSet = EnumSet.allOf(Gender.class);
            for (Gender gender : genderSet) {
                System.out.println(gender);
            }
     
            // EnumMap的使用
            EnumMap<Gender, String> genderMap = new EnumMap<Gender, String>(Gender.class);
            genderMap.put(Gender.FEMALE, "妹纸");
            genderMap.put(Gender.MALE, "汉纸");
            // 迭代器遍历
            for (Iterator<Entry<Gender, String>> iter = genderMap.entrySet().iterator(); iter.hasNext();) {
                Entry<Gender, String> entry = iter.next();
                System.out.println(entry.getKey().name() + ":" + entry.getValue());
            }
        }
    }

    运行结果如下:

    Gender
    Gender
    FEMALE
    MALE
    FEMALE:妹纸
    MALE:汉纸

    枚举和常量定义的区别

    1、从源码equals方法中可以看出枚举可以用==直接对比,常量值地址唯一,性能比常量高,因为常量是由于开发人员可以直接写,所以不能用==对比,只能用equals对比。

    2、枚举定义方法的参数时,必须用枚举常量类类型,,这样就转变成了强类型,不会出现弱类型引用的问题。而常量作为参数时,是String,int等弱类型,开发人员可以传入没有在常量接口里定义的值,这个问题无法通过编译器发现。

    3、枚举编译时,没有把常量值编译到代码里,即使常量的值发生变化,也不会影响引用常量的类,而常量编译时,是直接把常量的值编译到类的二进制代码里,常量的值在升级中变化后,需要重新编译引用常量的类,因为里面存的是旧值。

     待解决问题

    使用接口组织枚举

    参考博客链接

    https://www.cnblogs.com/todaylovegoaway/p/6023609.html

    http://www.hollischuang.com/archives/92

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

    https://www.cnblogs.com/happyPawpaw/archive/2013/04/09/3009553.html

    http://blog.csdn.net/wangqisen/article/details/34072821

    http://blog.csdn.net/yin_pisces/article/details/52050427

    转载请于明显处标明出处

    http://www.cnblogs.com/AmyZheng/p/8445149.html

  • 相关阅读:
    理解AXI Quad Serial Peripheral Interface(SPI) IP核
    xilinx 高速收发器Serdes深入研究-Comma码(转)
    Zynq-PL中创建AXI Master接口IP及AXI4-Lite总线主从读写时序测试(转)
    一步一步开始FPGA逻辑设计
    万兆网调试(转)
    自定义AXI-IP核(转)
    在嵌入式设计中使用MicroBlaze(Vivado版本)(转)
    MicroBlaze核的串行接口实验:SPI UART
    Git超实用总结
    无法获取 vmci 驱动程序版本: 句柄无效
  • 原文地址:https://www.cnblogs.com/AmyZheng/p/8445149.html
Copyright © 2011-2022 走看看