Java Enum的使用和深入理解

本文详细介绍了Java枚举的用法,包括常量、switch、新增方法、覆盖方法、实现接口、接口组织枚举、集合使用等。通过实例展示了枚举类的编译过程,并解释了枚举的构造函数、toString方法和ordinal属性的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参考:


1、Enum的使用


详细介绍了enum的一下几个用法:
用法一:常量;
用法二:switch;
用法三:向枚举中添加新方法;
用法四:覆盖枚举的方法;
用法五:实现接口;
用法六:使用接口组织枚举;
用法七:关于枚举集合的使用;
枚举和常量定义的区别;

注意:在case标签中,枚举前缀不能出现,即case Color.RED是不合法的,只能直接用枚举值RED。而在其他地方出现时则必须用Color.RED。

2、深入理解Enum

2.1 首先看看最简单的enum用法:
public class EnumMonth {
    public static void main(String[] args) {
        for (Month month : Month.values()) {
            System.out.println(month);
        }
    }
    
    private enum Month {
        JAN, SEP, MAR, APR, MAY;
    }
}
结果:
JAN
SEP
MAR
APR
MAY

2.2 使用javap EnumMonth$Month看到反编译的结果,Month会被编译成一个Java类:
Compiled from "EnumMonth.java"
final class com.demo.mytest.enumTest.EnumMonth$Month extends java.lang.Enum<com.demo.mytest.enumTest.EnumMonth$Month> {
  public static final com.demo.mytest.enumTest.EnumMonth$Month JAN;
  public static final com.demo.mytest.enumTest.EnumMonth$Month SEP;
  public static final com.demo.mytest.enumTest.EnumMonth$Month MAR;
  public static final com.demo.mytest.enumTest.EnumMonth$Month APR;
  public static final com.demo.mytest.enumTest.EnumMonth$Month MAY;
  public static com.demo.mytest.enumTest.EnumMonth$Month[] values();
  public static com.demo.mytest.enumTest.EnumMonth$Month valueOf(java.lang.String);
  static {};
}
1. 这个Month枚举类是final class,即不能被继承,而它本身是继承于Enum;
2. 这些枚举值是Month对象,而且是static final修饰的;
3. 注意values()方法,它能得到所有的枚举值,但这方法在Enum里面没有找到,在看到反编译结果后才确认的。

2.3  valueOf(String)方法和static静态块
valueOf(String)方法,返回带指定名称的指定枚举类型的枚举常量。
在使用javap -c EnumMonth$Month进一步查看汇编代码后,知道原来是调用Enum.valueOf(Class<T> enumType, String name):
 public static com.demo.mytest.enumTest.EnumMonth$Month valueOf(java.lang.String);
    Code:
       0: ldc_w         #4                  // class com/demo/mytest/enumTest/EnumMonth$Month
       3: aload_0
       4: invokestatic  #5                  // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
       7: checkcast     #4                  // class com/demo/mytest/enumTest/EnumMonth$Month
      10: areturn
static{}里面主要是new这些枚举值对象:
  static {};
    Code:
       0: new           #4                  // class com/demo/mytest/enumTest/EnumMonth$Month
       3: dup
       4: ldc           #7                  // String JAN
       6: iconst_0
       7: invokespecial #8                  // Method "<init>":(Ljava/lang/String;I)V
      10: putstatic     #9                  // Field JAN:Lcom/demo/mytest/enumTest/EnumMonth$Month;
      13: new           #4                  // class com/demo/mytest/enumTest/EnumMonth$Month
      16: dup
      17: ldc           #10                 // String SEP
      19: iconst_1
      20: invokespecial #8                  // Method "<init>":(Ljava/lang/String;I)V
      23: putstatic     #11                 // Field SEP:Lcom/demo/mytest/enumTest/EnumMonth$Month;
      26: new           #4                  // class com/demo/mytest/enumTest/EnumMonth$Month

2.4看一下enum类:
package java.lang;

import java.io.Serializable;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectStreamException;

public abstract class Enum<E extends Enum<E>>
        implements Comparable<E>, Serializable {

    private final String name;

    public final String name() {
        return name;
    }
    private final int ordinal;

    public final int ordinal() {
        return ordinal;
    }

    protected Enum(String name, int ordinal) {
        this.name = name;
        this.ordinal = ordinal;
    }
    public String toString() {
        return name;
    }


    public static <T extends Enum<T>> T valueOf(Class<T> enumType,
                                                String name) {
        T result = enumType.enumConstantDirectory().get(name);
        if (result != null)
            return result;
        if (name == null)
            throw new NullPointerException("Name is null");
        throw new IllegalArgumentException(
            "No enum constant " + enumType.getCanonicalName() + "." + name);
    }
<span style="white-space:pre">	</span>//........其他方法
}
这里要说说Enum的构造函数Enum(String name, int ordinal),形参name就是枚举值的string形式("JAN"),ordinal值则是编译器按照枚举值的顺序从0排着赋值的。要注意,这个ordinal值原来是不能改变的,即编译器是从0升序赋值的,在字节码里面固定写着iconst_0(n=0,1,2...)的。
static{
        JAN = new EnumMonth$Month("JAN", 0);
        SEP = new EnumMonth$Month("SEP", 1);
        ...
}
于是Enum里面的String name()和int ordinal()方法就是返回这两个值的。
Enum.toString()也是返回name值,这就是为什么System.out.println(month);会输出那些值。
看看下面的例子:
public class EnumMonth {
    public static void main(String[] args) {
        for (Month month : Month.values()) {
            System.out.println(month.ordinal());
        }
    }

    private enum Month {
        JAN(1), SEP(9), MAR(3), APR(4), MAY(5);

        final int id;

        Month(int id){
            this.id = id;
        }
    }
}
一开始没有加上id成员变量和构造函数的,编译时提示找不到构造函数 Month(int),本以为JAN(1)的1会赋值给形参ordinal呢!修正后,运行结果是:
0
1
2
3
4
说明ordinal值是固定不变的。
查看汇编代码:
0:     new         #4; //class EnumMonth$Month
3:     dup
4:     ldc         #8; //String JAN
6:     iconst_0
7:     iconst_1
8:     invokespecial     #9; //Method "<init>":(Ljava/lang/String;II)V
11:    putstatic             #10; //Field JAN:LEnumMonth$Month;
这时发现构造函数似乎增加一个形参int类型id。
我再尝试把int id改为char,运行结果是一样的:
private enum Month { 
        JAN('1'), SEP('9'), MAR('3'), APR('4'), MAY('5'); 

        final char id; 

        Month(char id){ 
                this.id = id; 
        } 
}
但编译代码有所不同:
0:     new         #4; //class EnumMonth$Month
3:     dup
4:     ldc         #8; //String JAN
6:     iconst_0
7:     bipush    49
9:     invokespecial     #9; //Method "<init>":(Ljava/lang/String;IC)V
12:    putstatic             #10; //Field JAN:LEnumMonth$Month;
所以个人认为,如果我们添加了自定义的构造函数,编译器会自动构建新的构造函数:
Enum(String name, int ordinal, char id) {
        super(name, ordinal);
        
        //copy 自定义构造函数的内容
        this.id = id;
}
注意:构造器只能私有private,绝对不允许有public构造器。否则提示modifier public not allowed here。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值