java枚举

3515 字
9 分钟

每日一言

Hatred will never be erased! The only thing you can do is erase the ones you hate! — Hakuryū Ren from Magi - The Labyrinth of Magic

枚举(enum)

Java枚举是一个特殊的类,一般表示一组常量,比如一年四个季节,一年的12个月份,一个星期的7天,方向的东西南北等。

枚举通过关键字 enum 来声明:

enum Season{
    SPRING,SUMMER,AUTUMN,WINTER;
}

这样我们可以在代码中展示更加清晰的逻辑:

public class Main {
    public static void main(String[] args) {
        // Create a new instance of the MyClass class
        Season season = Season.SPRING;
        // Print the value of the instance
        System.out.println("The current season is: " + season);
    }
}

枚举类的实例化

对于我们上面的例子

Season season = Season.SPRING;

这句话只是创建了一个针对 Season.SPRING 的引用,也就是不论我们给多少个变量赋值为 Season.SPRING 在内存中都只有一个Season.SPRING的实例,其他都是指向它的引用。

这样的化我们可能会好奇一个问题:枚举类是在什么时候被实例化的呢?

我们写一个简单的程序来验证它:

enum Season {
    SPRING, SUMMER, AUTUMN, WINTER;
  
    Season() {
        System.out.println("Creating enum constant: " + this.name());
    }
}

public class Main {
    public static void main(String[] args) {
        System.out.println("Main method started");
    }
}

输出如下:

Main method started

当我们没有引用任何的枚举常量时,枚举类并没有被实例化。接下来我们创建一个枚举类的引用:

enum Season {
    SPRING, SUMMER, AUTUMN, WINTER;
  
    Season() {
        System.out.println("Creating enum constant: " + this.name());
    }
}

public class Main {
    public static void main(String[] args) {

        // 直接使用枚举常量
        Season season = Season.SPRING;
        System.out.println("Main method started");
        // 不需要显式使用枚举常量,就能看到构造函数的输出
    }
}

输出如下:

Creating enum constant: SPRING
Creating enum constant: SUMMER
Creating enum constant: AUTUMN
Creating enum constant: WINTER
Main method started

所以JVM会在首次引用枚举常量时,一次为所有的枚举常量进行实例化,顺序为我们申明的顺序。枚举类中的成员变量为每个创建的实例私有,方法为所有实例共有,与普通的类并无区别。

迭代枚举元素

可以使用for循环和 values()方法来遍历枚举的每一个值。

public class Main {
    public static void main(String[] args) {
        for(Season s : Season.values()){
            System.out.println(s);
        }
    }
}

枚举类型的 values() 方法是Java枚举类型自动提供的一个静态方法。当你定义一个枚举类型时,Java编译器会自动为该枚举类型生成 values()方法。

功能

这个方法会返回一个包含所有枚举常量的数组,数组中的元素按照它们在枚举声明中的顺序排序。

对于上面的例子来说,Season.values()会返回一个枚举常量数组:

[Season.SPRING,Season.SUMMER,Season.AUTOMN,Season.WINTER]

在switch中使用枚举类

枚举类常应用于 switch 语句中:

public class Main {
    public static void main(String[] args) {
        Season season = Season.SPRING;
        switch (season) {
            case SPRING:
                System.out.println("Spring is here!");
                break;
            case SUMMER:
                System.out.println("Summer is here!");
                break;
            case AUTUMN:
                System.out.println("Autumn is here!");
                break;
            case WINTER:
                System.out.println("Winter is here!");
                break;
        }
    }
} 

values(),ordinal(),valueOf()方法

enum 定义的枚举类默认继承了 java.lang.Enum() 类,这个类中包含了三种默认的方法:

  • values():返回枚举类中的值,返回为一个数组
  • ordinal():方法可以找到每个枚举常量的索引,就像数组的索引一样,顺序与声明顺序相同
  • valueOf():方法返回指定字符串值的枚举常量
public class Main {
    public static void main(String[] args) {
        Season[] seasons = Season.values();

        for (Season season : seasons) {
            System.out.println(season);
            System.out.println("The ordinal of SPRING is: " + season.ordinal());
        }

        Season tmp =  Season.valueOf("SPRING");
        Season tmp1 = Season.SPRING;
  
    }
}

输出:

SPRING
The ordinal of SPRING is: 0
SUMMER
The ordinal of SPRING is: 1
AUTUMN
The ordinal of SPRING is: 2
WINTER
The ordinal of SPRING is: 3

Season.valueOf("SPRING")方法返回的枚举常量与直接使用 Season.SPRING 创建的枚举常量完全相同,它们的不同之处在于获取枚举常量的过程不同

Season.valueOf(SPRING)

  • 这是一个 方法调用 ,通过字符串参数来获取对应的枚举常量
  • 参数必须是字符串形式的枚举常量名称,且必须与枚举常量的名称完全匹配(包括大小写)
  • 如果字符串参数不匹配任何枚举常量名称,会抛出 IllegalArgumentException 异常
  • 适用于在运行时动态获取枚举常量,例如从配置文件或用户输入中读取字符串后转换为枚举

Season.SPRING

  • 这是直接引用枚举常量
  • 编译时就确定了引用的是哪个枚举常量
  • 不会抛出异常(编译时检查)
  • 代码更简洁,性能更好(不需要方法调用和异常处理)
  • 适用于在编译时已知需要使用哪个枚举常量的情况

枚举类成员

枚举跟普通类一样可以有自己的变量,方法和构造函数,但是构造函数只能使用private访问修饰符,所以外部无法调用。构造函数用于给枚举赋初值。

public enum Color{
    RED(255,0,0);
    private int r,g,b;
    private Color(int r, int g, int b)
    {
	this.r = r;
	this.g = g;
	this.b = b;
    }
}

枚举既可以包含具体方法,也可以包含抽象方法。 如果枚举类具有抽象方法,则枚举类的每个实例都必须实现它。

public enum Operation {
    ADD {
        @Override
        public double apply(double x, double y) {
            return x + y;
        }
    },
    SUBTRACT {
        @Override
        public double apply(double x, double y) {
            return x - y;
        }
    },
    MULTIPLY {
        @Override
        public double apply(double x, double y) {
            return x * y;
        }
    },
    DIVIDE {
        @Override
        public double apply(double x, double y) {
            if (y == 0) {
                throw new ArithmeticException("Division by zero");
            }
            return x / y;
        }
    };

    // 抽象方法
    public abstract double apply(double x, double y);
}

枚举的实例都在类的里面申明。