JAVA中枚举Enum详解

1.关键字:enum。枚举可以定义成单独的文件,也可以定义在其他类内部。

枚举在类内部的示例:

public class EnumInner {
    public static void main(String[] args) {
        Day day=Day.MONDAY;
    }
    
    enum Day{
        MONDAY,TUESDAY,THIRSDAY,FORTHDAY,FRIDAY
    }

}

Enum里,需要我们定义实例,定义的实例,返回类型还是Enum这个类本身。实例之间,用逗号隔开。我们也可以给实例添加参数,如MONDAY("参数1","参数2","参数3",,,)。添加参数后,我们在后面,定义成员变量,来接收这些参数。因为MONDAY,TUESDAY这些,都是Enum实例,其定义的参数,就是成员变量,只不过,在Eunm里,实例和成员变量,混在一起来写了。然后,我们提供方法,来获得这些实例的参数。如下代码:

public enum SimpleEnum {
    
    MONDAY("aaa","AAA"),//一个实例,相当于是一个对象。aaa就是成员变量的值
    TUESDAY("bbb");//另一个实例
    
    //实例中写了几个参数,就写几个成员变量
    private String param1;//第一个成员变量
    
    private String param2;//第二个成员变量
    
    /**
     * 枚举可以定义构造方法,但是构造方法都是私有的,或者是默认修饰符,不能用public修饰符,编译都过不去
     * 因为构造方法我们不能手动调用,是编译器自己调用的
     * 在实例中,我们有几个参数,定义构造器时,就要写几个参数的构造方法,这是为了给成员变量赋值。
     * @param param1
     * @param param2
     */
    private SimpleEnum(String param1,String param2) {
        this.param1=param1;
        this.param2=param2;
    }
    private SimpleEnum(String param){
        this.param1=param;
    }
    
    /**
     * 通过提供方法,供外部获得实例的参数值。在调用这些方法时,都是通过枚举.实例.方法调用。直接使用枚举,不.实例,是不能用这些方法的
     * @return
     */
    public String getParam(){
        return param1;
    }
    
    public String getParam2(){
        return param2;
    }
    
    public static void main(String[] args) {
        SimpleEnum.MONDAY.getParam();//通过枚举.实例.方法,获得参数
    }
}

 

2.枚举原理解析

我们定义的所有的枚举,在jdk内部,都会自动继承java.lang.Enum类。这是一个抽象类,我们看源码的构造方法:

//由编译器调用
protected Enum(String name, int ordinal) {
        this.name = name;
        this.ordinal = ordinal;
    }

枚举的构造方法都是由编译器自己调用的,我们在定义枚举时,无需定义构造方法。

我们说到,枚举在编译器中会自动继承Enum类,因为java是单继承的,所以,我们定义的枚举,是不能手动继承任何类的。但是,在枚举中,可以定义成员变量和方法,甚至可以定义main方法。也可以定义抽象方法,然后在枚举实例中,实现这些抽象方法,如下代码:

public enum SimpleEnum {
    
    MONDAY("aaa","AAA") {
        //实例中实现抽象方法
        @Override
        public void enumAbatract() {
            // TODO Auto-generated method stub
            System.out.println("星期一");
        }
    },//一个实例,相当于是一个对象。aaa就是成员变量的值
    TUESDAY("bbb") {
        @Override
        public void enumAbatract() {
            // TODO Auto-generated method stub
            System.out.println("星期二");
        }
    };//另一个实例
    
    //实例中写了几个参数,就写几个成员变量
    private String param1;//第一个成员变量
    
    private String param2;//第二个成员变量
    
    /**
     * 枚举可以定义构造方法,但是构造方法都是私有的,或者是默认修饰符,不能用public修饰符,编译都过不去
     * 因为构造方法我们不能手动调用,是编译器自己调用的
     * 在实例中,我们有几个参数,定义构造器时,就要写几个参数的构造方法,这是为了给成员变量赋值。
     * @param param1
     * @param param2
     */
    private SimpleEnum(String param1,String param2) {
        this.param1=param1;
        this.param2=param2;
    }
    private SimpleEnum(String param){
        this.param1=param;
    }
    
    /**
     * 通过提供方法,供外部获得实例的参数值。在调用这些方法时,都是通过枚举.实例.方法调用。直接使用枚举,不.实例,是不能用这些方法的
     * @return
     */
    public String getParam(){
        return param1;
    }
    
    public String getParam2(){
        return param2;
    }
    //定义抽象方法,上面每个实例,需要实现这个方法。
    public abstract void enumAbatract();
    
    public static void main(String[] args) {
        SimpleEnum.MONDAY.getParam();
        SimpleEnum.MONDAY.enumAbatract();
    }
}

由此可知,enum就是一个java类,类里面的东西,在枚举里都可以有。定义了抽象方法,每个实例实现不同的抽象方法,我们获得每个实例时,也就有了不同的实现方法。

同样的,枚举也能实现接口,然后实现接口里的方法,代码如下:

/**
 * 枚举接口,随便定义两个方法
 * @author Administrator
 *
 */
public interface IEnum {
    
    public void method();
    
    public void way();

}
/**
 * 枚举实现接口
 * @author Administrator
 *
 */
public enum EnumImpl implements IEnum{
    

    INSTANCE{
        //在实例里,也可以重写实现的接口方法
        @Override
        public void method() {
            // TODO Auto-generated method stub
            super.method();
        }
    };
    /**
     * 枚举中实现接口方法,那么所有的实例都是走的这个方法
     */
    @Override
    public void method() {
        System.out.println("实现了method方法");
        
    }

    @Override
    public void way() {
        System.out.println("实现了way方法");
        
    }

}

3.枚举与单例

在《设计模式之单例模式(Singleton Pattern)》文章中提到,枚举实现单例是最安全的,解决了反射和序列化破坏单例的可能。为什么呢?首先,我们看反射处理枚举时是怎么处理的:

 

 这是jdk中new Instance方法的源码,我们可以看出,当通过反射实例化一个枚举时,会抛出异常。所有,我们无法通过反射,来破坏单例模式。

在序列化问题上,jvm对枚举的序列化做了特殊的规定,其序列化时,只是序列化了枚举的name()方法,在反序列化时,也保证了枚举的唯一性。相当于把枚举的唯一性,交给了jvm做保证。具体的原理,可以看这篇文章:https://blog.csdn.net/wang__qin/article/details/79802628

在这里,我们不对其进行过深的研究。

4.总结:

枚举最常用的就是取代我们之前的public static final常量定义,而是通过枚举定义一些常量参数。在枚举中,实例定义在枚举类中,成员属性和成员方法定义在枚举类中,这是一个比较特殊的地方。它不像普通的类,实例是通过new出来的,而是直接就在枚举类中定义实例,然后初始化实例的成员变量,方法等。

 

posted on 2021-03-05 16:37  敲代码的小小酥  阅读(7895)  评论(0编辑  收藏  举报