• java枚举类详解


    什么是枚举

    ​ 实例(也叫对象)有限且固定不变的类,在Java里被称为枚举类。

    例如,季节类,它只有4个实例(春、夏、秋、冬),并且这4个实例不会改变。可以用枚举类来表示:

    public enum SeasonEnum{
     SPRING,SUMMER,FALL,WINTER;
    }
    
    • 1
    • 2
    • 3

    枚举类是一种特殊的类,它一样可以有自己的成员变量、方法,可以实现一个或多个接口,也可以有自己的构造器。

    为什么需要枚举类

    (1)就如上面所说的,有些类的实例有限且固定,需要有一种特定且方便的方式来表示这种类。

    (2)使用枚举类可以使程序更加健壮,避免创建对象的随意性。

    (3)避免一些常量值的意义不明确

    语法

    (1) 枚举类默认继承 java.lang.Enum 类,而不是 Object 类,因此枚举类不能显示继承其他父类。
    (2) 使用 enum 定义的非抽象的枚举类默认会使用 final 修饰,因此非抽象枚举类不能派生子类(即不能被继承)。
    
             > final关键字回顾:final修饰的类不能被继承、修饰的方法不能被重写、修饰的属性其值不能改变。
    
    (3) 枚举类的构造器只能使用 private 访问控制符,如果忽略访问控制符的话,则默认使用 private 修饰;如果强制指定其他的访问控制符(例如public、procted等),则会报错。
    
    (4) 枚举类的所有实例必须在枚举类的第一行显示列出,否则这个枚举类永远都不可能产生实例。列出的这些实例,系统会自动给它们加上 public static final 修饰。枚举类的实例以逗号分隔,分号结束,这些列出的枚举值代表了该枚举类的所有可能的实例
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    例子

    public enum Weekday {
        SUN(0),MON(1),TUS(2),WED(3),THU(4),FRI(5),SAT(6);
    
        private int value;
    
        private Weekday(int value){
            this.value = value;
        }
    
        public static Weekday getNextDay(Weekday nowDay){
            int nextDayValue = nowDay.value;
    
            if (++nextDayValue == 7){
                nextDayValue =0;
            }
    
            return getWeekdayByValue(nextDayValue);
        }
    
        public static Weekday getWeekdayByValue(int value) {
            for (Weekday c : Weekday.values()) {
                if (c.value == value) {
                    return c;
                }
            }
            return null;
        }
    }
    
    class Test2{
        public static void main(String[] args) {
            System.out.println("nowday ====> " + Weekday.SAT);
            System.out.println("nowday int ====> " + Weekday.SAT.ordinal());
            System.out.println("nextday ====> " + Weekday.getNextDay(Weekday.SAT)); // 输出 SUN
    
            //输出:
            //nowday ====> SAT
            //nowday int ====> 6
            //nextday ====> SUN
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    方法

    枚举类的方法

    Weekday可以调用的方法和参数。发现它有两个方法:value()和valueOf()。还有我们刚刚定义的七个变量。

    在这里插入图片描述

    枚举类变量的方法

    public enum Weekday {
        SUN,MON,TUS,WED,THU,FRI,SAT
    }
    
    class Test3{
        public static void main(String[] args) {
            //它的作用是传来一个字符串,然后将它转变为对应的枚举变量。前提是你传的字符串和定义枚举变量的字符串一抹一样,区分大小写。如果你传了一个不存在的字符串,那么会抛出异常。
            System.out.println(Weekday.valueOf("mon".toUpperCase()));
            //MON
    		//这个方法会返回包括所有枚举变量的数组。在该例中,返回的就是包含了七个星期的Weekday[]。可以方便的用来做循环。
            for (Weekday w : Weekday.values()){
                //默认请款下,枚举类会给所有的枚举变量一个默认的次序,该次序从0开始,类似于数组的下标。而.ordinal()方法就是获取这个次序(或者说下标)
                System.out.println(w + ".ordinal()  ====>" +w.ordinal());
            }
            //SUN.ordinal()  ====>0
            //MON.ordinal()  ====>1
            //TUS.ordinal()  ====>2
            //WED.ordinal()  ====>3
            //THU.ordinal()  ====>4
            //FRI.ordinal()  ====>5
            //SAT.ordinal()  ====>6
    		
            //该方法用来比较两个枚举变量的”大小”,实际上比较的是两个枚举变量的次序,返回两个次序相减后的结果,如果为负数,就证明变量1”小于”变量2 (变量1.compareTo(变量2),返回【变量1.ordinal() - 变量2.ordinal()】)
            System.out.println("Weekday.MON.compareTo(Weekday.FRI) ===> " + Weekday.MON.compareTo(Weekday.FRI));
            System.out.println("Weekday.MON.compareTo(Weekday.MON) ===> " + Weekday.MON.compareTo(Weekday.MON));
            System.out.println("Weekday.MON.compareTo(Weekday.SUM) ===> " + Weekday.MON.compareTo(Weekday.SUN));
            //Weekday.MON.compareTo(Weekday.FRI) ===> -4
            //Weekday.MON.compareTo(Weekday.MON) ===> 0
            //Weekday.MON.compareTo(Weekday.SUM) ===> 1
    		
            
            //它和toString()方法的返回值一样
            //唯一的区别是,你可以重写toString方法。name变量就是枚举变量的字符串形式。
            System.out.println("Weekday.MON.name() ====> " + Weekday.MON.name());
            //Weekday.MON.name() ====> MON
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    高级用法

    定义的扩展

    public enum Weekday {
        MON(1,"mon"),TUS(2,"tus"),WED(3,"wed"),THU(4,"thu"),FRI(5,"fri"),SAT(6,"sat"),SUN(0,"sun");
    
        private int value;
        private String label;
    
        private Weekday(int value,String label){
            this.value = value;
            this.label = label;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    枚举类中定义抽象方法

    public enum TrafficLamp {
        RED(30) {
            @Override
            public TrafficLamp getNextLamp() {
                return GREEN;
            }
        }, GREEN(45) {
            @Override
            public TrafficLamp getNextLamp() {
                return YELLOW;
            }
        }, YELLOW(5) {
            @Override
            public TrafficLamp getNextLamp() {
                return RED;
            }
        };
    
        private int time;
    
        private TrafficLamp(int time) {
            this.time = time;
        }
    
        //一个抽象方法
        public abstract TrafficLamp getNextLamp();
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    因为RED本身就是一个TrafficLamp对象的引用。实际上,在初始化这个枚举类的时候,你可以理解为执行的是TrafficLamp RED = new TrafficLamp(30) ,但是因为TrafficLamp里面有抽象方法,还记得匿名内部类么?

    TrafficLamp RED = new TrafficLamp30{
     @Override
     public TrafficLamp getNextLamp() {
         return GREEN;
     }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    而在枚举类中,我们只需要像上面那样写【RED(30){}】就可以了,因为java会自动的去帮我们完成这一系列操作。

    switch

    enum Signal {
        GREEN, YELLOW, RED
    }
    
    public class TrafficLight {
        Signal color = Signal.RED;
    
        public void change() {
            switch (color) {
            case RED:
                color = Signal.GREEN;
                break;
            case YELLOW:
                color = Signal.RED;
                break;
            case GREEN:
                color = Signal.YELLOW;
                break;
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    实现接口

    虽然枚举类不能继承其他类,但是还是可以实现接口的

    public interface Behaviour {
        void print();
    
        String getInfo();
    }
    
    public enum Color implements Behaviour {
        RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);
        // 成员变量
        private String name;
        private int index;
    
        // 构造方法
        private Color(String name, int index) {
            this.name = name;
            this.index = index;
        }
    
        // 接口方法
        @Override
        public String getInfo() {
            return this.name;
        }
    
        // 接口方法
        @Override
        public void print() {
            System.out.println(this.index + ":" + this.name);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    使用接口组织枚举类

    public interface Food {
        enum Coffee implements Food {
            BLACK_COFFEE, DECAF_COFFEE, LATTE, CAPPUCCINO
        }
    
        enum Dessert implements Food {
            FRUIT, CAKE, GELATO
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    使用枚举类创建单例模式

    public enum EasySingleton{
        INSTANCE;
    }
    
    
    • 1
    • 2
    • 3
    • 4

    双检索实现单例

    public class DoubleCheckedLockingSingleton{
         private volatile static  DoubleCheckedLockingSingleton INSTANCE;
    
         private DoubleCheckedLockingSingleton(){}
    
         public DoubleCheckedLockingSingleton getInstance(){
             if(INSTANCE == null){
                synchronized(DoubleCheckedLockingSingleton.class){
                    //double checking Singleton instance
                    if(INSTANCE == null){
                        INSTANCE = new DoubleCheckedLockingSingleton();
                    }
                }
             }
             return INSTANCE;
         }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    用静态工厂方法实现单例:

    public class Singleton{
        private static final Singleton INSTANCE = new Singleton();
    
        private Singleton(){}
    
        public static Singleton getSingleton(){
            return INSTANCE;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    参考:博文1

    博文2

  • 相关阅读:
    向指定文件夹创建文件,并且写入信息
    rsync远程同步
    开发者测评:阿里云 ACR 与其他的镜像仓库到底有什么不同?
    【JVS低代码平台】如何实现与外部系统/内部代码直接对接?
    SpringCloud无介绍快使用,Ribbon负载均衡工具与OpenFeign的使用(十五)
    js 锚点定位的方法
    鲲鹏代码迁移工具介绍
    Kafka25道面试题总结(答案解析)
    Linux 操作系统内核之进程
    NIO与BIO服务器端对比
  • 原文地址:https://blog.csdn.net/weixin_43604021/article/details/126242933