enum枚举类的基本知识及使用技巧

1、前置知识

1、枚举类是一个特殊的类,,它一样有自己的成员变量、方法,可以实现一个或多个接口,也可以定义自己的构造器。
2、一个 java 源文件最多只能定义一个 public 访问权限的 枚举类。且该 java 源文件也必须和该枚举的类名相同
3、枚举类默认继承了 java.lang.Enum 类,而不是 Object 类,所以枚举类不能显示继承其他父类。其中
java.lang.Enum 类实现了 java.lang.Serializable 和 java.lang.Comparable 两个接口。
4、使用 enum 定义、非抽象的枚举类会默认使用 final 修饰
5、枚举类的构造器只能用 private 访问控制符,因为子类构造器总要调用父类构造器一次,所以枚举类不能派生子类
6、在枚举类中列出枚举值时,实际上就是调用构造器创建枚举类对象。只是这里无需使用 new 关键字。也无需显示调用
构造器。前面列出枚举值时无需传入参数(调用无参的构造方法),也可以传入参数(有参的构造方法)
7、枚举类的所有实例必须在枚举类的第一行显示列出,否则这个枚举类永远不能产生实例。列出这些实例时系统会自动
添加 public static final 修饰,无需自动添加

package main.enumkey;

/**
 * --------------------------------------------
 * ClassName:    SeasonEnum
 * CreateBy:     IntelliJ IDEA
 * Author:       醉瑾
 * Date:         2022-04-01
 * Description :
 * --------------------------------------------
 */
/*
 * 1、枚举类是一个特殊的类,,它一样有自己的成员变量、方法,可以实现一个或多个接口,也可以定义自己的构造器。
 * 2、一个 java 源文件最多只能定义一个 public 访问权限的 枚举类。且该 java 源文件也必须和该枚举的类名相同
 * 3、枚举类默认继承了 java.lang.Enum 类,而不是 Object 类,所以枚举类不能显示继承其他父类。其中
 *      java.lang.Enum 类实现了 java.lang.Serializable 和 java.lang.Comparable 两个接口。
 * 4、使用 enum 定义、非抽象的枚举类会默认使用 final 修饰
 * 5、枚举类的构造器只能用 private 访问控制符,因为子类构造器总要调用父类构造器一次,所以枚举类不能派生子类
 * 6、在枚举类中列出枚举值时,实际上就是调用构造器创建枚举类对象。只是这里无需使用 new 关键字。也无需显示调用
 *      构造器。前面列出枚举值时无需传入参数(调用无参的构造方法),也可以传入参数(有参的构造方法)
 * 7、枚举类的所有实例必须在枚举类的第一行显示列出,否则这个枚举类永远不能产生实例。列出这些实例时系统会自动
 *      添加 public static final 修饰,无需自动添加
 * */
public enum SeasonEnum {
    // 列出这些枚举值 之前不能有任何语句 见 7
    // 在枚举类中列出枚举值时,实际上就是调用构造器创建枚举类对象 见 6
    FALL,
    SPRING,
    SUMMER,
    WINTER("冬天"); // 本句后面如果没有其他代码语句可以不加 分号

    public String season;

    SeasonEnum() {
        // 无参构造方法
    }

    SeasonEnum(String season) {
        // 有参的构造方法
        this.season = season;
    }

    public String getSeason() {
        return season;
    }

    public void setSeason() {
        switch (this) { // 此处 this 为 某个实例对象
            case SPRING:
                this.season = "春天";
                break;
            case SUMMER:
                this.season = "夏天";
                break;
            case FALL:
                this.season = "秋天";
                break;
        }
    }
}

2、测试

package main.enumkey;

/**
 * --------------------------------------------
 * ClassName:    EnumTest
 * CreateBy:     IntelliJ IDEA
 * Author:       醉瑾
 * Date:         2022-04-01
 * Description :
 * --------------------------------------------
 */
public class EnumTest {
    public static void main(String[] args) {
        test1();
        test2();
        test3();
    }

    static void test1() {
        System.out.println(SeasonEnum.WINTER.getSeason()); // 有参构造,冬天
        System.out.println(SeasonEnum.SPRING.getSeason()); // 无参构造,所以 null
        System.out.println("-------------------test1-END------------------");
    }

    static void test2() {
        /*
         * 因为所有的枚举类都继承了 java.lang.Enum 类,所以枚举类可以直接使用 Enum 类中所包含的的方法
         * Enum 类包含方法:
         * 1、int compareTo():
         *      该方法用于与指定枚举对象比较顺序, 同一个枚举实例只能
         *      与相同类型的枚举实例进行比较。如果该 枚举对象位于指定枚举对象之后,
         *      则返回正整数;如果该枚举 对象位于指定枚举对象之前,则返回负整数,否则返回零
         * 2、String name():
         *      返回此枚举实例的名称,这个名称就是定义 枚举类时列出的所有枚举值之一。
         *      与此方法相比,大多数程序 员应该优先考虑使用toString()方法,
         *      因为toString()方法返 回更加用户友好的名称。
         * 3、int ordinal()
         *      int ordinal():返回枚举值在枚举类中的索引值(就是枚举 值在枚举声明中的位置,
         *      第一个枚举值的索引值为零)。
         * 4、String toString():
         *      返回枚举常量的名称,与name方法相 似,但toString()方法更常用。
         * 5、public static<T extends Enum<T>> T valueOf(Class<T> enumType,String name):
         *      这是一个静态方法,用于返回指定 枚举类中指定名称的枚举值。
         *      名称必须与在该枚举类中声明枚 举值时所用的标识符完全匹配,
         *      不允许使用额外的空白字符
         * */
        // 枚举类会有一个默认的 values() 方法,返回该枚举类的所有实例
        for (SeasonEnum seasonEnum : SeasonEnum.values()) {
            // 每一个seasonEnum 都是一个实例对象,都能访问枚举类中的成员变量和方法
            System.out.println(seasonEnum);
            System.out.println(seasonEnum.valueOf(SeasonEnum.class, seasonEnum.name()));
            System.out.println(seasonEnum.compareTo(SeasonEnum.SPRING));
            System.out.println(seasonEnum.ordinal());
            System.out.println(seasonEnum.toString());
            System.out.println("===================");
        }
        System.out.println("-------------------test2-END------------------");
    }

    static void test3() {
        SeasonEnum seasonEnum = Enum.valueOf(SeasonEnum.class, "SPRING");
        SeasonEnum seasonEnum1 = Enum.valueOf(SeasonEnum.class, "SPRING");
        System.out.println("修改前: " + seasonEnum.season);
        seasonEnum.season = "春秋季";
        seasonEnum1.season = "冬夏季";
        System.out.println("seasonEnum-被修改后: " + seasonEnum.season);
        System.out.println("seasonEnum1-被修改后: " + seasonEnum1.season); // 被连带修改了,出现连带修改,非常混乱
        System.out.println("-------------------test3-END------------------");
        /*
         * 在上边的枚举类中 season 字段被 public 修饰
         * 当枚举实例直接调用 season 字段并修改,下个枚举实例获取到的可能就是被修改到的值
         * (此处的枚举实例指的是由同一个 枚举值 创建的,如都是由 SPRING 创建的)
         *
         * 解决办法:
         * 将 season 字段用 private 修饰,禁止实例直接调用,利用 setter 方法选择赋值,同时设定的值不可自行修改。
         * 其实一般直接在列出枚举值时直接进行 构造函数的初始化,不需要 设置 setter 方法,同时设定变量被 private final 修饰
         * */
    }
}

test1
在这里插入图片描述
test2
在这里插入图片描述
test3
在这里插入图片描述

3、改进枚举类

package main.enumkey;

/**
 * --------------------------------------------
 * ClassName:    SeasonEnum1
 * CreateBy:     IntelliJ IDEA
 * Author:       醉瑾
 * Date:         2022-04-01
 * Description :
 * --------------------------------------------
 */
public enum SeasonEnum1 {
    // 列出这些枚举值 之前不能有任何语句 见 7
    // 在枚举类中列出枚举值时,实际上就是调用构造器创建枚举类对象 见 6
    FALL("秋天"),
    SPRING("春天"),
    SUMMER("夏天"),
    WINTER("冬天"); // 本句后面如果没有其他代码语句可以不加 分号
    public final String season;

    SeasonEnum1(String season) {
        // 有参的构造方法
        this.season = season;
    }

    public String getSeason() {
        return season;
    }
}

// 一个Java源文件中最多只能定义一 个public访问权限的枚举类,
// 且该Java源文件也必须和该枚举类的类 名相同。
// 此处不能用 public 修饰,道理和 普通java文件类里面不能有多个 public 修饰的类一样
enum test {

}

4、实现接口的枚举类

GanderDec接口

public interface GanderDec {
    void ganderInfo();
}

Gander枚举类实现GanderDec接口

package main.enumkey;

/**
 * --------------------------------------------
 * ClassName:    Gander
 * CreateBy:     IntelliJ IDEA
 * Author:       醉瑾
 * Date:         2022-04-01
 * Description : 实现接口的枚举类
 * --------------------------------------------
 */
public enum Gander implements GanderDec {
    MALE("男"), FEMALE("女");
    private final String gander;

    Gander(String gander) {
        this.gander = gander;
    }

    @Override
    public void ganderInfo() {
        System.out.println("我的性别是:" + gander);
    }
}

测试

static void test4() {
        System.out.println(Gander.MALE);
        System.out.println(Gander.FEMALE);
        Gander.MALE.ganderInfo();
        Gander.FEMALE.ganderInfo();
        System.out.println("-------------------test4-END------------------");
    }

如果由枚举类来实现接口里的方法,则每个枚举值在调用该方法
时都有相同的行为方式(因为方法体完全一样)。如果需要每个枚举 值在调用该方法时呈现出不同的行为方式,则可以让每个枚举值分别 来实现该方法,每个枚举值提供不同的实现方式,从而让不同的枚举 值调用该方法时具有不同的行为方式

第二种重写方法(分别重写)

package main.enumkey;

/**
 * --------------------------------------------
 * ClassName:    Gander
 * CreateBy:     IntelliJ IDEA
 * Author:       醉瑾
 * Date:         2022-04-01
 * Description : 实现接口的枚举类
 * --------------------------------------------
 */
public enum Gander implements GanderDec {
    /*
     *如果由枚举类来实现接口里的方法,则每个枚举值在调用该方法
     * 时都有相同的行为方式(因为方法体完全一样)。如果需要每个枚举
     * 值在调用该方法时呈现出不同的行为方式,则可以让每个枚举值分别
     * 来实现该方法,每个枚举值提供不同的实现方式,从而让不同的枚举
     * 值调用该方法时具有不同的行为方式。
     * */
    MALE("男") {
        @Override
        public void ganderInfo() {
            System.out.println("黑马王子");
        }
    },
    FEMALE("女") {
        @Override
        public void ganderInfo() {
            System.out.println("白雪公主");
        }
    };
    private final String gander;

    Gander(String gander) {
        this.gander = gander;
    }

    /*@Override
    public void ganderInfo() {
        System.out.println("我的性别是:" + gander);
    }*/
}

在这里插入图片描述
当创建MALE和FEMALE两个枚举值时,后面又紧跟了一对花括号,这对花括号里包含了一个 ganderInfo()方法定义。如果读者还记得匿名内部类语法的话,则可能对这 样的语法有点印象了,花括号部分实际上就是一个类体部分,在这种 情况下,当创建MALE、FEMALE枚举值时,并不是直接创建Gender枚举 类的实例,而是相当于创建Gender的匿名子类的实例。因为括 号部分实际上是匿名内部类的类体部分,所以这个部分的代码语法与匿名内部类语法大致相似,只是它依然是枚举类的匿名内 部子类。

枚举类不是用final修饰了吗?怎么还能派生子类呢?

并不是所有的枚举类都使用了final修饰!非抽象的枚举类才默 认使用final修饰。对于一个抽象的枚举类而言——只要它包含了抽
象方法,它就是抽象枚举类,系统会默认使用abstract修饰,而不是 使用final修饰。

拓展:

抽象方法:
java中的抽象方法就是以abstract修饰的方法,这种方法只声明返回的数据类型、方法名称和所需的参数,没有方法体,也就是说抽象方法只需要声明而不需要实现。
抽象方法与抽象类:
当一个方法为抽象方法时,意味着这个方法应该被子类的方法所重写,否则其子类的该方法仍然是abstract的,这个子类由于继承父类,拥有抽象方法,因此它也是抽象类,即声明为abstract。abstract抽象类不能用new实例化对象,abstract方法只允许声明不能实现。如果一个类中含有abstract方法,那么这个类必须用abstract来修饰,当然abstract类也可以没有abstract方法。 一个抽象类里面没有一个抽象方法可用来禁止产生这种类的对象。(摘自百度文库:java抽象类和方法,作者:余书慧先生)
抽象方法与接口:
在interface中所有的方法都是public abstract的,即使你没有申明它是public abstract的。在interface中所有的数据成员都是public static final的,即使你没有申明.但不能是blank final 在编译时候确定的。在Java,interface的地位和class是一样的。实现interface的类,其interface中所有的方法必须被“实现”,否则这个类成为一个抽象类。Interface可以从多个interface得到继承,但是不能继承类。一个类可以实现多个interface

编 译 上 面 的 程 序 , 可 以 看 到 生 成 了 Gender.class 、 Gender$1.class和Gender$2.class三个文件,这样的三个class文件正 好证明了上面的结论:MALE和FEMALE实际上是Gender匿名子类的实 例,而不是Gender类的实例。当调用MALE和FEMALE两个枚举值的方法 时,就会看到两个枚举值的方法表现不同的行为方式

上边是从实现某个接口,从而包含抽象方法,接下来定义一个包含抽象方法的枚举类

package main.enumkey;

/**
 * --------------------------------------------
 * ClassName:    Operation
 * CreateBy:     IntelliJ IDEA
 * Author:       醉瑾
 * Date:         2022-04-01
 * Description : 包含抽象方法的枚举类
 * --------------------------------------------
 */
public enum Operation {
    PLUS {
        // 加
        public double eval(double x, double y) {
            return x + y;
        }
    },
    MINUS {
        // 减
        public double eval(double x, double y) {
            return x - y;
        }
    },
    TIMES {
        // 乘
        public double eval(double x, double y) {
            return x * y;
        }
    },
    DIVIDE {
        // 除
        public double eval(double x, double y) {
            return x / y;
        }
    };

    // 为枚举类定义一个抽象方法,不定义此方法不会报错,但是调用Operation.PLUS.eval时会报找不到 eval 方法错误
    public abstract double eval(double x, double y);

    public static void main(String[] args) {
        System.out.println(Operation.PLUS.eval(2, 3));
        System.out.println(Operation.MINUS.eval(2, 3));
        System.out.println(Operation.TIMES.eval(2, 3));
        System.out.println(Operation.DIVIDE.eval(2, 3));
    }
}

在这里插入图片描述
编译上面程序会生成5个class文件,其实Operation对应一个 class文件,它的4个匿名内部子类分别各对应一个class文件。
枚举类里定义抽象方法时不能使用abstract关键字将枚举类定义成抽象类(因为系统自动会为它添加abstract关键字),但因为枚举 类需要显式创建枚举值,而不是作为父类,所以定义每个枚举值时必 须为抽象方法提供实现,否则将出现编译错误。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夏至xz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值