zoukankan      html  css  js  c++  java
  • JDK阅读之Enum

    JDK学习之Enum

    enum的使用

    在没有enum之前如果想要定义一些常量,就会采用如下的方式

    假设要定义四个常量表示不同的季节

    public class SeasonWithoutEnum {
        public static final int spring=1;
        public static final int summer=2;
        public static final int autumn=3;
        public static final int winter=4;
    
        public static void getSession(int a){
            switch(a){
                case SeasonWithoutEnum.spring:
                    System.out.println("春天!");
                    break;
                case SeasonWithoutEnum.summer:
                    System.out.println("夏天!");
                    break;
                case SeasonWithoutEnum.autumn:
                    System.out.println("秋天!");
                    break;
                case SeasonWithoutEnum.winter:
                    System.out.println("winter is comming");
                    break;
                default:
                    System.out.println("查无此季");
                    break;
            }
        }
    
        public static void main(String[] args) {
            SeasonWithoutEnum.getSession(SeasonWithoutEnum.spring);
            SeasonWithoutEnum.getSession(5);
        }
    }
    

    缺点:

    1. 对于第一个调用,似乎没有什么问题也是常见的调用,但是第二个调用就存在这类型不安全的问题由于没有限制参数a的范围,导致随便传入一个数字都可以,如果还需要考虑这个数字的限制,那么代码的逻辑就会变得复杂

    2. 可读性差,对于上面的示例来说,使用了数字来表示季节,而我们通常使用String来进行季节的表示,如果我们使用String,虽然jdk现在提供了String的switch的支持,但是使用了String的hashcode做比较,还需要处理hash冲突,自然是比较麻烦的

    对于上面的这种需求场景采用enum来改进

    public enum Season {
        SPRING(1,"春天!"),SUMMER(2,"夏天!"),AUTUMN(3,"秋天!"),WINTTER(4,"冬天!");
        private int num;
        private String sName;
        Season(int num,String sName){
            this.num=num;
            this.sName=sName;
        }
    
        public String getsName(){
            return this.sName;
        }
    
        public static void getSession(Season season){
            switch(season){
                case SPRING:
                    System.out.println(season.getsName());
                    break;
                case SUMMER:
                    System.out.println(season.getsName());
                    break;
                case AUTUMN:
                    System.out.println(season.getsName());
                    break;
                case WINTTER:
                    System.out.println(season.getsName());
                    break;
            }
        }
    
        public static void main(String[] args) {
            Season.getSession(Season.SPRING);
            Season.getSession(Season.WINTTER);
        }
    }
    

    此时通过season进行枚举,避免了类型安全问题,只能传入已有的枚举实例,此外,又提高了表意性

    enum是如何实现的

    对于以下的代码:

    public enum Season{
        SPRING,SUMMER;
    }
    

    就这么简简单单的几行代码怎么就定义了枚举呢?为什么又说枚举类呢?

    通过jad反编译Season.class得到如下代码

    jad反编译工具的下载链接如下:https://varaneckas.com/jad/

    package com.hustdj.jdkStudy;
    
    //实际上它就是继承自Enum的一个final类,也就是我们声明的enum就是一个继承自Enum的final类
    public final class Season extends Enum
    {
    
        private Season(String s, int i)
        {
            super(s, i);
        }
    	
        //values方法,通过arraycoapy的方式返回所有枚举实例
        public static Season[] values()
        {
            Season aseason[];
            int i;
            Season aseason1[];
            System.arraycopy(aseason = ENUM$VALUES, 0, aseason1 = new Season[i = aseason.length], 0, i);
            return aseason1;
        }
    
        public static Season valueOf(String s)
        {
            return (Season)Enum.valueOf(com/hustdj/jdkStudy/Season, s);
        }
    
        public static final Season SPRING;
        public static final Season SUMMER;
        private static final Season ENUM$VALUES[];
    
        //静态代码块
        static 
        {
            SPRING = new Season("SPRING", 0);
            SUMMER = new Season("SUMMER", 1);
            ENUM$VALUES = (new Season[] {
                SPRING, SUMMER
            });
        }
    }
    

    结论

    1. enum关键字的背后,是编译器为我们做了事情,它其实是一个继承了Enum的final类,自然它就不能再被继承
    2. 我们所声明的枚举实例,实际上也是由静态代码块为我们创建的
    3. 有一个隐藏的方法values(),它提供所有的枚举实例对象的拷贝,有意思的是这个方法你是追踪不到的,也就是说你用ctrl是追踪 不进去的,原因就是它是在编译期生成的方法

    Enum

    既然知道了enum是通过继承了Enum的final类,那么就来看看Enum这个超类吧

    public abstract class Enum<E extends Enum<E>>
            implements Comparable<E>, Serializable {
    
        private final String name;
    
        public final String name() {
            return name;
        }
    	//优先级,这个默认从0开始累加,从反编译中可以看出来
        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;
        }
    	//对于enum来说==和equals的作用相同,因为equals使用的就是==
        public final boolean equals(Object other) {
            return this==other;
        }
    
        public final int hashCode() {
            return super.hashCode();
        }
    	//不允许使用clone方法
        protected final Object clone() throws CloneNotSupportedException {
            throw new CloneNotSupportedException();
        }
    	//compareTo比较的是ordinal
        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;
        }
    
        @SuppressWarnings("unchecked")
        public final Class<E> getDeclaringClass() {
            Class<?> clazz = getClass();
            Class<?> zuper = clazz.getSuperclass();
            return (zuper == Enum.class) ? (Class<E>)clazz : (Class<E>)zuper;
        }
    
        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() { }
    
        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");
        }
    }
    

    有以下几个点需要注意:

    1. enum的==与equals方法等效
    2. enum的排序使用的ordinal成员变量的大小排序
    3. enum禁止使用clone方法,会直接抛出异常

    Switch对于enum的支持

    对于上面enum使用的中的例子,反编译得到的代码如下:

    public static void getSession(Season season)
    {
        switch($SWITCH_TABLE$com$hustdj$jdkStudy$Season()[season.ordinal()])
        {
            case 1: // '01'
                System.out.println("u6625u5929uFF01");
                break;
    
            case 2: // '02'
                System.out.println("u590Fu5929uFF01");
                break;
    
            case 3: // '03'
                System.out.println("u79CBu5929uFF01");
                break;
    
            case 4: // '04'
                System.out.println("u51ACu5929uFF01");
                break;
        }
    }
    
    static int[] $SWITCH_TABLE$com$hustdj$jdkStudy$Season()
    {
        $SWITCH_TABLE$com$hustdj$jdkStudy$Season;
        if($SWITCH_TABLE$com$hustdj$jdkStudy$Season == null) goto _L2; else goto _L1
            _L1:
        return;
        _L2:
        JVM INSTR pop ;
        int ai[] = new int[values().length];
        try
        {
            ai[AUTUMN.ordinal()] = 3;
        }
        catch(NoSuchFieldError _ex) { }
        try
        {
            ai[SPRING.ordinal()] = 1;
        }
        catch(NoSuchFieldError _ex) { }
        try
        {
            ai[SUMMER.ordinal()] = 2;
        }
        catch(NoSuchFieldError _ex) { }
        try
        {
            ai[WINTTER.ordinal()] = 4;
        }
        catch(NoSuchFieldError _ex) { }
        return $SWITCH_TABLE$com$hustdj$jdkStudy$Season = ai;
    }
    

    通过查看反编译的代码可以看到,$SWITCH_TABLE$com$hustdj$jdkStudy$Season方法把enum的ordinal自定义的num关联起来返回数组,在switch时通过enum的ordinal获取到对应的num,然后再switch,比较的最终还是自定义的num

    常用枚举TimeUnit

    public enum TimeUnit {
        NANOSECONDS {
            public long toNanos(long d)   { return d; }
            public long toMicros(long d)  { return d/(C1/C0); }
            public long toMillis(long d)  { return d/(C2/C0); }
            public long toSeconds(long d) { return d/(C3/C0); }
            public long toMinutes(long d) { return d/(C4/C0); }
            public long toHours(long d)   { return d/(C5/C0); }
            public long toDays(long d)    { return d/(C6/C0); }
            public long convert(long d, TimeUnit u) { return u.toNanos(d); }
            int excessNanos(long d, long m) { return (int)(d - (m*C2)); }
        },
        /*还有很多就不一一列举了*/
    }
    

    非常的amazing啊,在实例NANOSECONDS中它重写了TimeUnit中定义的方法,这个可以通过反编译查看,在老朋友season中试了一下,效果如下

    //源代码
    SPRING(1,"春天!"){
            public String getsName(){
                return "春天!重载";
            }
    },
    //反编译
    static 
    {
        SPRING = new Season("SPRING", 0, 1, "u6625u5929uFF01") {
    
            public String getsName()
            {
                return "u6625u5929uFF01u91CDu8F7D";
            }
    
        }
        ;
        SUMMER = new Season("SUMMER", 1, 2, "u590Fu5929uFF01");
        AUTUMN = new Season("AUTUMN", 2, 3, "u79CBu5929uFF01");
        WINTTER = new Season("WINTTER", 3, 4, "u51ACu5929uFF01");
        ENUM$VALUES = (new Season[] {
            SPRING, SUMMER, AUTUMN, WINTTER
        });
    }
    

    枚举与单例

    【回头再好好补充】

  • 相关阅读:
    K8S实战(十七)| 通过 StorageClass 实现动态卷供应
    K8S实战(十六)| 持久化存储卷
    K8S实战(十五)| 存储卷概念
    K8S实战(十四)| ConfigMap 对象
    K8S实战(十三)| Secret 对象
    K8S实战(十二)| 为 Ingress 以及后端 Nginx 增加证书
    批量删除git 本地分支、远程分支、tag
    React 页面间传值的个人总结
    搭建一个属于自己的webpack config(-)
    HTTP 2 新特性
  • 原文地址:https://www.cnblogs.com/danzZ/p/14053041.html
Copyright © 2011-2022 走看看