java 枚举的真面目

缘起

在开发中,经常用到枚举,但一直没有认真研究过它。为了更好的理解枚举,决定好好挖掘挖掘。

举个最简单的例子,性别是定数的,可以枚举,所以很容易写出枚举类

public enum SexEnum {


    MAN(11,"男"),
    WOMAN(12,"女");

    private Integer code;
    private String myname;

    SexEnum(Integer code, String name) {
        this.code = code;
        this.myname = name;
    }

   //getter and setter
    
}

问题

写完后,不禁想到几个问题

  • SexEnum.values()方法从何而来,Enum抽象类中并不存在values()方法

  • MAN和WOMAN是SexEnum对象,有get/set方法,我们只是写了一个文本MAN、WOMAN,它怎么就变成了对象?是怎么实现的?

  • 还有一点你也许没有注意,构造方法的修饰符只能是默认或private,不能是protected、public的,这是为什么?

为了一探究竟,将类反编译

public final class SexEnum extends Enum
{

    public static SexEnum[] values()
    {
        return (SexEnum[])$VALUES.clone();
    }

    public static SexEnum valueOf(String name)
    {  
      //这里的name是Enum类中的name,也就是MAN、WOMAN字符串,而不是男、女
        return (SexEnum)Enum.valueOf(com/jun/javase/SexEnum, name);
    }

    private SexEnum(String s, int i, Integer code, String name)
    {
       //这里调用的Enum(String name, int ordinal)
        super(s, i);
        this.code = code;
        myname = name;
    }

   

    public static final SexEnum MAN;
    public static final SexEnum WOMAN;
    private Integer code;
    private String myname;
    private static final SexEnum $VALUES[];

    static 
    {
        MAN = new SexEnum("MAN", 0, Integer.valueOf(11), "\u7537");
        WOMAN = new SexEnum("WOMAN", 1, Integer.valueOf(12), "\u5973");
        $VALUES = (new SexEnum[] {
            MAN, WOMAN
        });
    }
//setter and getter
}

看到反编译后代码,恍然大悟,说到底,枚举又是一个语法糖就跟泛型一样,枚举类继承了java.lang.Enum

protected	Enum(String name, int ordinal)
唯一的构造函数。
String	name()
返回此枚举常量的名称,与其枚举声明中声明的完全相同。
int	ordinal()
返回此枚举常数的序数(其枚举声明中的位置,其中初始常数的序数为零)。

values()方法从何而来

values()方法的返回值来源于变量$VALUES,而$VALUES 是包含MAN和WOMAN的数组,java编译器创建了values方法

枚举变量如何变成了对象

MAN和WOMAN有getCode/setCode/getMyname/setMyname方法,由此可以推断,它是SexEnum 对象,我们只是写了一个文本,它怎么就变成了对象?是怎么实现的?

从代码中可以看出,MAN和WOMAN被定义成了SexEnum 类型的变量,并且在静态代码块中赋了值,名字是MAN,次序是0,code是11,myname是"\u7537"。这也解答了Enum api中的ordinal() 方法和name() 方法。既然MAN和WOMAN是final static的,所以比较时可以使用== 这也让我弄明白了java核心卷中说的枚举比较使用== 的原因了。从中可以还出还是java编译器捣的鼓_

public static final SexEnum MAN;
public static final SexEnum WOMAN;
MAN = new SexEnum("MAN", 0, Integer.valueOf(11), "\u7537");
WOMAN = new SexEnum("WOMAN", 1, Integer.valueOf(12), "\u5973");

构造方法范围为什么只能是private或默认

构造方法的修饰符只能是默认或private,不能是protected、public的,这是为什么?

这是因为枚举相当于util类,没有必要多例,最好单例,实现单例的第一要素肯定是private构造方法,所以从反编译后的构造方法是private的,所以,我们将枚举类的构造方法设置为public会编译报错。

private SexEnum(String s, int i, Integer code, String name)
    {
        super(s, i);
        this.code = code;
        myname = name;
    }

总结

枚举归根到底是一个语法糖,它能让我们更容易、更快速写出常量定义,并且更加直观安全。
java编译器将定义的枚举,都转换成对象,并且在static代码块中创建了对象并将所有枚举值放入了一个临时变量中,创建了values()方法。java编译器创建了一个参数是名字、次序、自定义参数的构造方法,并且范围是private,所以才有了枚举的特性。

关于枚举相比常量更加直观、更加安全,这一点这里举个例子,比如设置性别方法,如果使用常量方式,我们就得这样写

public void setSex(int value){
  //需要写代码判断value是否合法
}

value值的取值范围就不好限制了,当然你会说,在方法中判断,这样当然可以达到目的,但有点繁琐。
我们看看枚举的方式

public void setSex(SexEnum sex){

}

这样sex只能是性别,其他值都会编译错误,一眼就能懂是性别,而且更加安全。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值