zoukankan      html  css  js  c++  java
  • 简单认识java enum枚举

    什么是枚举

    枚举是java5中新增的特性,他是一个特殊的数据类型,他的特殊性在于他既是一种类类型,又比类类型多了安全性,简洁性,便捷性。java枚举类型是功能十分强大齐全的类,功能比其他语言中的对等物要强大的多,java的枚举类型本质上是int值。

    java枚举类型背后的基本想法:就是通过共有的静态final域为每个枚举常量导出实例的类,因为没有可以访问的构造器,所以枚举类型是真正的final。枚举天生是不可变的。

    枚举基本实现

    首先来看看我们在项目中不使用枚举声明一段int类型的常量。

    /** 订单状态 0:未支付*/
    public static final int ORDER_DEPOT_UNPAY = 0;
    
    /** 订单状态 1:已支付*/
    public static final int ORDER_DEPOT_PAID = 1;
    
    /** 订单状态 2:支付超时*/
    public static final int ORDER_DEPOT_TIMEOUT = 2;
    
    /** 物流状态 0:物流状态*/
    public static final int ORDER_LOGISTICS_READY = 0;
    
    /** 物流状态 1:物流中*/
    public static final int ORDER_LOGISTICS_TRANSPORT = 1;
    
    /** 物流状态 2:已收货*/
    public static final int ORDER_LOGISTICS_ARRIVED = 2;
    

    这种方法被称为int枚举模式,我们可以看到对于每一种订单状态需要以ORDER_DEPOT作为前缀,如果当出现有多个int值具有相同的值时,前缀可以防止名称发生冲突。

    采用int枚举模式的程序是十分脆弱的,因为int枚举是编译时常量,被编译到使用它们的客户端中。如果与枚举常量关联的int发生了变化,客户端就必须要重新编译。如果没有重新编译,程序还是可以运行,但是他们的行为是不确定的。

    像这种我们就可以直接使用一下这种最简单的形式:

    /** 订单状态枚举类*/
    public enum DepotEnum{
        UNPAY,PAID,TIMEOUT;
    }
    
    /** 物流订单状态枚举类*/
    public enum LogisticsEnum{
        READY,TRANSPORT,ARRIVED;
    }
    
    public static void main (String[] args) {
        System.out.println (DepotEnum.UNPAY.ordinal ());
        System.out.println (LogisticsEnum.ARRIVED.ordinal ());
    }
    

    上面这个也能达到相同的效果。许多枚举天生就与一个单独的int值想关联,所有的枚举都有一个ordinal方法,它返回每个枚举常量在类型中的数字位置。

    枚举类型与数据库字段交互

    comment on column ORDER.PAY_STATE is '字符状态(0:未支付、1:已支付、2:支付超时)';
    

    我们在数据库中经常会有这种状态的字段,像这种如果不在界面上需要展示的话,要么在sql中使用decode函数,要么在前端使用if else 判断。所以我们可以使用一个EnumMap来存取这样的对象。EnumMap时一个用于存储key为枚举类型的map,底层使用数组实现。

    public class OrderEnumMap {
        /**
         * 定义一个基本的枚举类型
         */
        public enum BaseEnumMap{
            /**未支付*/
            UNPAY,
            /**已支付*/
            PAID,
            /**支付超时*/
            TIMEOUT;
        }
    
        /**声明枚举map*/
        private EnumMap<BaseEnumMap,String> enumMap = new EnumMap<BaseEnumMap,String>(BaseEnumMap.class);
    
        public OrderEnumMap(){
            enumMap.put(BaseEnumMap.UNPAY,"未支付");
            enumMap.put(BaseEnumMap.PAID,"已支付");
            enumMap.put(BaseEnumMap.TIMEOUT,"支付超时");
        }
    
        /**
         * 获取支付状态
         * @param code
         * @return
         */
        public String getOrderState(int code){
            if(BaseEnumMap.class.getEnumConstants().length > code) {
                return this.enumMap.get(BaseEnumMap.class.getEnumConstants()[code]);
            }
            return null;
        }
    
        public static void main(String[] args) {
            System.out.println(new OrderEnumMap().getOrderState(2));
        }
    }
    

    还有另外一种方式:

    /**
      * 订单状态枚举类
      */
    public enum OrderEnum{
        /** 标识订单的未支付状态*/
        UNPAY("未支付",0),
        /** 标识订单的已支付状态*/
        PAID("已支付",1),
        /** 标识订单的支付超时状态*/
        TIMEOUT("支付超时",2);
    
        /**描述*/
        private String desc;
    
        /**编码*/
        private int code;
    
        OrderEnum (String desc, int code) {
            this.desc = desc;
            this.code = code;
        }
    
        private static HashMap<Integer,String> map = new HashMap<Integer,String>();
        static {
            for (OrderEnum d : OrderEnum.values ()) {
                map.put (d.code, d.desc);
            }
        }
    
        public static String getDescByCode(int code){
            if(map.containsKey (code)){
                return map.get (code);
            }
            return null;
        }
    }
    public static void main(String[] args) {
           System.out.println(OrderEnum.getDescByCode(1));
    }
    

    个人认为方式二会更优一点,尽量不要写内部枚举类。

    枚举实现单例

    《Effective Java》一书中对使用枚举实现单例的方式推崇备至:使用枚举实现单例的方法虽然还没有广泛采用,但是单元素的枚举类型已经成为实现Singleton的最佳方法。

    使用枚举类创建单例的有点在于:线程安全,调用效率高,不能延时加载,可以天然的防止反射和反序列化调用。

    来对比一下普通的单例和使用枚举实现的单例:

    /**
      * 普通的单例(饿汉式)
      */
    public class Singleton{ 
         private static Singleton instance = new Singleton; 
         private Singleton(){} 
         public static Singleton getInstance(){  
              return instance;  
         } 
        public void doSomeThing(){
            System.out.println ("实现单例的方法是声明-普通单例类");
        }
    }
    /**
      * 枚举单例
      */
    public enum SingletonEnum {
        Singleton;
        public void doSomeThing(){
            System.out.println ("实现单例的方法是声明-枚举类");
        }
    }
    

    枚举实现单例的线程安全

    枚举的底层是依赖Enum类实现的,这个类的成员变量都是static类型的,并且在静态代码块中被实例化的,和饿汉模式有点像,所以他是天然线程安全的。

    枚举和行为绑定

    与枚举常量有关的有些行为,可能指需要用在定义了枚举的类或者包中,这种行为最好被实现成私有的或者包级私有的方法。每个常量都关联了不同的数据,本质上将不同的行为与每个常量关联起来。来看看计算器的四大基本操作:

    public class Operate {
        public enum NormalActive {
            PLUS, MINUS, MULIT, DIVIDS, DIFFER;
    
            double oper (double x, double y) {
                switch (this) {
                    case PLUS:
                        return x + y;
                    case MINUS:
                        return x - y;
                    case MULIT:
                        return x * y;
                    case DIVIDS:
                        return x / y;
                    case DIFFER:
                        return (x + 1) * y;
                    default:
                        throw new AssertionError();
                }
            }
        }
    
        public static void main (String[] args) {
            System.out.println (NormalActive.PLUS.oper (2, 3));
        }
    }
    

    上面这段代码可以执行,但是还不够好,如果没有throw语句的话,就不能通过编译。同时,这段代码也很脆弱,如果添加了新的枚举常量,却没有在switch种添加相应的条件,编译可以通过,但是执行却会报错。

    那么更好的方法是,在枚举类型中声明一个抽象的方法。并在特定于常量的类主体中。

    /**
      * 更好的实现枚举的加,减,乘,除
      */
    public enum BetterEnum{
    
        PLUS{
            @Override
            public double calc(double x,double y){
                return x+y;
            }
        },
        MINUS{
            @Override
            double calc (double x, double y) {
                return x - y;
            }
        },
        MULTI{
            @Override
            double calc (double x, double y) {
                return x * y;
            }
        },
        DIVIDS{
            @Override
            double calc (double x, double y) {
                return x / y;
            }
        };
    
        abstract double calc(double x,double y);
    }
    

    枚举实现策略模式

    public enum StrategyEnum {
    
        MONDAY (PayType.WORK), TUESDAY (PayType.WORK),
        WEDNESDAY (PayType.WORK), THURSDAY (PayType.WORK),
        FRIDAY (PayType.WORK), SATURDAY (PayType.REST),
        SUNDAY (PayType.REST);
    
        private final PayType payType;
    
        /**
         * 构造器
         *
         * @param payType 支付类型
         */
        StrategyEnum (PayType payType) {
            this.payType = payType;
        }
    
        double pay(double workTime){
            return payType.pay (workTime);
        }
    
        /**
         * 内部枚举类,计算加班费
         */
        public enum PayType {
            WORK {
                @Override
                double pay (double workTime) {
                    return workTime * HOURS_WORK;
                }
            }, REST {
                @Override
                double pay (double workTime) {
                    return workTime * HOURS_REST;
                }
            };
    
            /**
             * 工作日每小时加班费
             */
            private static final int HOURS_WORK = 200;
    
            /**
             * 休息日每小时加班费
             */
            private static final int HOURS_REST = 300;
    
            abstract double pay (double workTime);
    
        }
    
        public static void main (String[] args) {
            System.out.println (StrategyEnum.THURSDAY.pay (2.5));
        }
    
    }
    

    本文部分摘自《Effective Java》一书,仅作记录。如果想要深入了解enum类型,可以查看大佬的这篇博客:

    https://blog.csdn.net/javazejian/article/details/71333103

  • 相关阅读:
    Linux下支持mysql支持远程ip访问
    vscode显示php函数列表
    摘:关于php调用.net的web service 踩过的坑
    php接收json格式数据(text/xml)
    php查询快递信息
    php获取客户端ip
    PHP到浏览器的缓存机制
    soap缓存问题
    WordPress For SAE 移植
    使用 AWS CloudTrail 记录 IAM 和 AWS STS API 调用
  • 原文地址:https://www.cnblogs.com/pluto-charon/p/12768208.html
Copyright © 2011-2022 走看看