Java-枚举(Enum)

知识点1-枚举的概念

  • 在枚举出现之前,如果想要表示一组特定的离散值,往往使用一些常量。

    例如:

    public interface Gender {
        //
        int MALE = 1;
        //
        int FEMALE = 0;
    }
    public class GenderTest {
        public static void main(String[] args) {
            System.out.println(Gender.MALE);
            System.out.println(Gender.FEMALE);
        }
    }
    /**
     * 1
     * 0
     */
  • 当然,常量也不仅仅局限于int型,诸如char和String等也是不在少数

  • 定义常规的静态常量后使用存在的一些小问题:

    • 代码可读性差、易用性低。由于test方法的返回值代表类型,但却是int型的,在阅读代码的时候往往会感到一头雾水,根本不明白这个数值到底是什么意思,代表的是什么类型

    • 类型不安全。在用户去调用的时候,必须保证类型完全一致,同时取值范围也要正确。像是对某些对象的类型属性进行赋值时,-1等非法的值满足类型要求,但却不存在与离散值列表之中,会出现较多的问题

    • 耦合性高,扩展性差。例如,假如针对类别做了一个有效性验证,如果类别增加了或者有所变动,则有效性验证也需要做对应的修改,不利于后期维护

  • 枚举就是为了这样的问题而诞生的。它们给出了将一个任意项同另一个项相比较的能力,并且可以在一个已定义项列表中进行迭代

  • 枚举(在Jave中简称为Enum)是一个特定类型的类。所有枚举都是Java中的新类java.lang.Enum的隐式子类。此类不能手工进行子类定义

知识点2-枚举的声明

  • 枚举的基本声明

    • 在枚举中需写全该枚举类型变量的每一个值,这些值称为枚举元素。

    • 命名规范:枚举名称,首字母大写,驼峰标识;其枚举值,全大写,下划线分割;

    • 任意两个枚举成员不能具有相同的名称,且它的常数值必须在该枚举的基础类型的范围之内,多个枚举成员之间使用逗号分隔。

  • 枚举的使用

    枚举名 变量名=枚举名.枚举值

    可以使用枚举名.枚举值取得每一个枚举值

    或者

    枚举名 变量名;

    变量名=枚举名.枚举值

    public enum Gender {
        MALE,//
        FEMALE//
    }
    public class GenderTest {
        public static void main(String[] args) {
            //定义枚举变量
            Gender g = null;
            //赋值
            g = Gender.MALE; //只能获取从已经定义好的枚举列表中枚举值
            System.out.println("性别为:"+g);
        }
    }
    /**
     * 性别为:MALE
     */
  • switch 语句使用枚举

    public enum Signal {
        RED, YELLOW, GREEN
    }
    public class TrafficLight {
        Signal color = Signal.GREEN;
        public void change() {
            switch (color) {
                case RED: {
                    color = Signal.GREEN;
                    break;
                }
                case GREEN: {
                    color = Signal.YELLOW;
                    break;
                }
                case YELLOW: {
                    color = Signal.RED;
                    break;
                }
            }
        }
        public static void main(String[] args) {
            TrafficLight light = new TrafficLight();//初始化信号灯颜色
            light.color = Signal.GREEN;
            System.out.println("初始红绿灯为:" + light.color);
            //改变红绿灯
            light.change();
            System.out.println("转变后的红绿灯为:" + light.color);
        }
    }
    /**
     * 初始红绿灯为:GREEN
     * 转变后的红绿灯为:YELLOW
     */

知识点3-Enum类的常用方法

  • values() 方法

    通过调用枚举类型的 values() 方法可以将枚举的所有成员以数组形式返回,也可以通过该方法获取枚举类型的成员。

    public enum Signal {
        RED, YELLOW, GREEN
    }
    public class SignalValuesTest {
        public static void main(String[] args) {
            //values() 获取定义枚举值列表,以数组的形式返回
            Signal[] values = Signal.values();
            for (int i = 0; i < values.length; i++) {
                System.out.println("枚举成员为:" + values[i]);
            }
        }
    }
    /**
     * 枚举成员为:RED
     * 枚举成员为:YELLOW
     * 枚举成员为:GREEN
     */
  • compareTo() ,valueOf()方法

    调用 valueOf() 方法获取枚举的一个成员,再调用 compareTo() 方法进行比较,并输出结果

    Enum默认实现了java.lang.Comparable接口

    枚举类型 变量=枚举类型.valueOf("枚举值")

    enum Sex {
        MALE,FEMALE;
    }
    public class TestEnum {
        public static void compare(Sex s) {
            for (int i = 0; i < Sex.values().length; i++) {
                System.out.println(s + "与" + Sex.values()[i] +
                        "的比较结果是:" + s.compareTo(Sex.values()[i]));
            }
        }
        public static void main(String[] args) {
            compare(Sex.valueOf("MALE"));
        }
    }
    /**
     * MALE与MALE的比较结果是:0
     * MALE与FEMALE的比较结果是:-1
     */
  • ordinal() 方法

    通过调用枚举类型实例的 ordinal() 方法可以获取一个成员在枚举中的索引位置。下面的示例创建一个包含 3 个成员的枚举类型 Signal,然后调用 ordinal() 方法输出 成员及对应索引位置。

    public enum Signal {
        RED, YELLOW, GREEN
    }
    public class SignalTest {
        public static void main(String[] args) {
            /*for (int i = 0; i < Signal.values().length; i++) {
                System.out.println("索引:"+Signal.values()[i].ordinal()
                +",值:"+Signal.values()[i]);
            }*/
    
            Signal[] values = Signal.values();
            for (Signal s : values) {
                System.out.println("索引:" + s.ordinal() + ",值:" + s);
            }
        }
    }
    /**
     * 索引:0,值:RED
     * 索引:1,值:YELLOW
     * 索引:2,值:GREEN
     */

知识点4-枚举的构造方法

  • 枚举相当于一个类,所以它也是有构造方法的

  • 枚举中的每一个元素,其实就相当于一个对象的引用,这个元素可以有方法和字段。

  • 枚举类被初始化时调用构造方法,每个构造方法将以成员声明顺序被调用:有几个枚举值就按顺序 依次调用构造方法创建几个对象。

  • 不能有public的构造方法,这样做可以保证客户代码没有办法新建一个enum的 实例。

  • 如果显式地写出了枚举的构造方法,那么就必须在创造枚举对象时,相应地显式引用,否则就会出错。这也是 Java的运行机制之一。

  • 我们可以和在普通类里面定义变量一样定义其它任何类型的非枚举变量,这些变量可以用任何你想用的修饰符。

    public enum ColorOthers {
        RED(), YELLOW("yellow"), GREEN("green"), BLACK("black");
        //枚举的字段,非枚举变量
        private String name;
        //枚举的方法,获取枚举元素的name字段值
        public String getName() {
            return name;
        }
        //枚举的方法,设定枚举元素的name字段的值
        public void setName(String name) {
            this.name = name;
        }
        private ColorOthers() {
            System.out.println("无参构造");
        }
        private ColorOthers(String name) {
            this.setName(name);
            System.out.println("有参构造:" + name);
        }
    }
    public class ColorTest {
        public static void main(String[] args) {
            String name = ColorOthers.RED.getName();
            System.out.println("name = "+name);
        }
    }
    /**
     *无参构造
     * 有参构造:yellow
     * 有参构造:green
     * 有参构造:black
     * name = null
     */

知识点5-枚举实现接口

  • 所有的枚举都继承自java.lang.Enum类。由于Java不支持多继承,所以枚举对象不能再继承其他类

  • 方案一:枚举定义可以实现接口(像类一样)

public interface DemoInterface {
    String getColorName(); //抽象方法
}
package com.tjetc.myenum;
public enum Color1 implements DemoInterface {
    RED(), GREEN("lvse"), YELLOW("huangse"), BLUE("lanse");
    //实现接口的方案一
    @Override
    public String getColorName() {
        return name;
    }
    //成员变量
    private String name;
    //获取 name值
    public String getName() {
        return name;
    }
    //设置name值
    public void setName(String name) {
        this.name = name;
    }
    //定义构造方法, 是private 构造方法 只能在类的内部被使用
    //构造方法 private 可以省略 不写 也是private
    Color1() {
        System.out.println("无参构造方法被调用");
    }
    //从语法不可以定义public构造方法,不希望枚举由其他人创建对象,只自己内部来创建对象,可以很好的控制枚举列表
    private Color1(String name) {
        this.name = name;
        System.out.println("有参的构造方法被调用,name=" + name);
    }
    public static void main(String[] args) {
        System.out.println(Color1.RED.getColorName());
        for (Color1 c : Color1.values()) {
            System.out.println(c + c.getColorName());
        }
    }
}
/**
 *无参构造方法被调用
 * 有参的构造方法被调用,name=lvse
 * 有参的构造方法被调用,name=huangse
 * 有参的构造方法被调用,name=lanse
 * BLUE
 * lanse
 * ======================
 * RED
 * GREEN
 * YELLOW
 * BLUE
 * =============================
 * lvse
 * ==============================
 * 123456
 * 579
 * =================================
 * lvse
 * huangse
 * ==================================
 * BLUE:ma()
 */
  • 方案二:当一个枚举对象实现了一个接口之后,枚举中的每一个对象都要分别实现接口中的所有抽象方法

  • 除了实现抽象接口,枚举自己内部也可以定义抽象方法。一旦枚举自己也定义了 抽象方法,那么只能枚举中的每个对象都要分别的实现这些方法,不然会报错。

    public interface DemoInterface {
        String getColorName(); //抽象方法
    }
    package com.tjetc.myenum;
    
    public enum Color2 implements DemoInterface {
        //实现接口的方案二:
        RED() {
            //重写抽象方法
            @Override
            public void ma() {
                System.out.println("RED:ma()");
            }
    
            public String getColorName() {
                return this.getName();
            }
        },
        GREEN("lvse") {
            //重写抽象方法
            @Override
            public void ma() {
                System.out.println("GREEN:ma()");
            }
    
            public String getColorName() {
                return this.getName();
            }
        },
        YELLOW("huangse") {
            //重写抽象方法
            @Override
            public void ma() {
                System.out.println("YELLOW:ma()");
            }
    
            public String getColorName() {
                return this.getName();
            }
        },
        BLUE("lanse") {
            //重写抽象方法
            @Override
            public void ma() {
                System.out.println("BLUE:ma()");
            }
            public String getColorName() {
                return this.getName();
            }
        };
    
    //    RED(), GREEN("lvse"), YELLOW("huangse"), BLUE("lanse");
    //
    //    //实现接口的方案一
    //    @Override
    //    public String getColorName() {
    //        return name;
    //    }
        //定义抽象方法
        public abstract void ma();
        //成员变量
        private String name;
        //获取 name值
        public String getName() {
            return name;
        }
        //设置name值
        public void setName(String name) {
            this.name = name;
        }
        //定义构造方法, 是private 构造方法 只能在类的内部被使用
        //构造方法 private 可以省略 不写 也是private
        Color2() {
            System.out.println("无参构造方法被调用");
        }
        //从语法不可以定义public构造方法,不希望枚举由其他人创建对象,只自己内部来创建对象,可以很好的控制枚举列表
        private Color2(String name) {
            this.name = name;
            System.out.println("有参的构造方法被调用,name=" + name);
        }
        public static void main(String[] args) {
            Color2 blue = Color2.BLUE;
            System.out.println(blue);
            System.out.println(blue.getName());
            System.out.println("======================");
            for (Color2 c : Color2.values()) {
                System.out.println(c);
            }
    
            System.out.println("=============================");
            //给定一个字符串 GREEN 转换成Color的枚举,打印name
            //str = "GREEN" 和 Color2.GREEN
            String str = "GREEN";
            //转换成枚举
            Color2 c = Color2.valueOf(str);
            System.out.println(c.getName());
            System.out.println("==============================");
            String a = "123";
            String b = "456";
    
            System.out.println(a + b); //字符串拼接
            //a+b进行算术运算结果
            System.out.println(Integer.valueOf(a) + Integer.valueOf(b));
            System.out.println("=================================");
    
            System.out.println(Color2.GREEN.getColorName());
            System.out.println(Color2.YELLOW.getColorName());
            System.out.println("==================================");
            Color2.BLUE.ma();
        }
    }
    /**
     * 无参构造方法被调用
     * 有参的构造方法被调用,name=lvse
     * 有参的构造方法被调用,name=huangse
     * 有参的构造方法被调用,name=lanse
     * BLUE
     * lanse
     * ======================
     * RED
     * GREEN
     * YELLOW
     * BLUE
     * =============================
     * lvse
     * ==============================
     * 123456
     * 579
     * =================================
     * lvse
     * huangse
     * ==================================
     * BLUE:ma()
     */

知识点6-自定义枚举类

  • 自定义方法:Java 为枚举类型提供了一些内置的方法,同时枚举常量也可以有自 己的方法。此时要注意必须在枚举实例的最后一个成员后添加分号,而且必须先定义枚举实例。

  • 自定义变量:所有枚举值都是public , static , final的。注意这一点只是针对于枚举值,我们可以和在普通类里面定义变量一样定义其它任何类型的非枚举变量, 这些变量可以用任何你想用的修饰符。

  • 步骤如下:

    • 声明枚举类型,在枚举值列表最后一列加分号,注意枚举值必须写在最前面.

    • 在枚举值列表后加括号,括号里边写实参值,是传给构造方法用的

    • 写枚举的私有的构造方法,形参类型要跟枚举值的实参列表类型一致

    • 在枚举值后面写私有的成员变量,一般情况下应该和构造方法的形参保持一致 写成员变量的getter和setter方法

  • 下面的代码创建了一个枚举类型 WeekDay,而且在该类型中添加了自定义的方法

    public enum Weekday {
        //枚举列表
        MON("星期一"),
        TUE("星期二"),
        WED("星期三"),
        THU("星期四"),
        FRI("星期五"),
        STA("星期六"),
        SUN("星期日");
    
        //成员变量
        private String name;
    
        //get方法
        public String getName() {
            return name;
        }
    
        //构造方法
        Weekday(String str) {
            this.name = str;
        }
    
        //可以覆盖Object的toString方法
        @Override
        public String toString() {
            return "Weekday{" +
                    "name='" + name + '\'' +
                    '}';
        }
    
        public static void printName(int i) {
            switch (i) {
                case 1: {
                    System.out.println(MON.getName());
                    break;
                }
                case 2: {
                    System.out.println(Weekday.TUE.getName());
                    break;
                }
                case 3: {
                    System.out.println(Weekday.WED.getName());
                    break;
                }
                case 4: {
                    System.out.println(Weekday.THU.getName());
                    break;
                }
                case 5: {
                    System.out.println(Weekday.FRI.getName());
                    break;
                }
                case 6: {
                    System.out.println(Weekday.STA.getName());
                    break;
                }
                case 7: {
                    System.out.println(Weekday.SUN.getName());
                    break;
                }
                default: {
                    System.out.println("错误的数字");
                }
            }
        }
    
        public static void main(String[] args) {
            Weekday.printName(6);
            Weekday.printName(2);
            System.out.println(Weekday.MON);
        }
    }
    /**
     * 星期六
     * 星期二
     * Weekday{name='星期一'}
     */

知识点7-enum 还可以跟 Class 类一样覆 盖基类的方法

  • Java 中的 enum 还可以跟 Class 类一样覆盖基类的方法。下面示例代码创建的 Color 枚举类型覆盖了 toString() 方法。

    public class Test {
        public enum Color {
            RED("red", 1),
            YELLOW("yellow", 2),
            GREEN("green", 3),
            ;
            private String name;
            private int index;
            private Color(String name, int index) {
                this.name = name;
                this.index = index;
            }
            //覆盖方法
            @Override
            public String toString() {
                return this.index + "-" + this.name;
            }
        }
        public static void main(String[] args) {
            System.out.println(Color.RED.toString());
        }
    }
    /**
     * 1-red
     */
posted @ 2022-03-20 20:24  carat9588  阅读(437)  评论(0编辑  收藏  举报