枚举

枚举

  • 枚举是JDK5 引入的一个特性,他是一种特殊的数据类型,它特殊在即是一种类型,又有一些特殊之处,这些特殊造就了枚举的特性,比如简单,安全等。

一、枚举

  • 在没有枚举之前,或者现在开发中我们经常用常量来定义一些固定的变量,比如性别,星期等,
public class WeekConstant  {

    public static final int MONDAY =1;
    public static final int TUESDAY=2;
    public static final int WEDNESDAY=3;
    public static final int THURSDAY=4;
    public static final int FRIDAY=5;
    public static final int SATURDAY=6;
    public static final int SUNDAY=7;
} 
  • 引入枚举后,是这样的:
public enum WeekEnum {
    MON, TUE, WEN,THR,FRI, SAT,SYN
}
  • 使用枚举定义更加简洁,发生错误的时候编译器还会给出错误提示,可以按照如下方式引用,非常简洁:
WeekEnum.MON

二、本质

2.1 反编译

  • 下面我们通过javac WeekEnum.java 编译前面的 WeekEnum 文件看枚举类的特殊之处,中文是添加的注释:
//反编译 WeekEnum.MON
//final类,不能被继承,是 Enum 抽象类的子类
public final class WeekEnum extends Enum {

    //定义的枚举实例
	public static final WeekEnum MON;
	public static final WeekEnum TUE;
	public static final WeekEnum WEN;
	public static final WeekEnum THR;
	public static final WeekEnum FRI;
	public static final WeekEnum SAT;
	public static final WeekEnum SYN;
	
	//$VALUES保存了全部的枚举值
	private static final WeekEnum $VALUES[];

    //编译器添加的静态 values() 方法
	public static WeekEnum[] values()
	{
		return (WeekEnum[])$VALUES.clone();
	}

    //编译器添加的静态的 valueOf() 方法,间接调用了 Enum 类的 valueOf 方法
	public static WeekEnum valueOf(String name)	{
		return (WeekEnum)Enum.valueOf(com/intellif/vo/WeekEnum, name);
	}
    
    //构造私有,不能实例化
	private WeekEnum(String s, int i)
	{
		super(s, i);
	}

    //静态代码块实例化自定义的枚举实例和一个额外的$VALUES,$VALUES是包括全部枚举实例的数组
	static {
		MON = new WeekEnum("MON", 0);
		TUE = new WeekEnum("TUE", 1);
		WEN = new WeekEnum("WEN", 2);
		THR = new WeekEnum("THR", 3);
		FRI = new WeekEnum("FRI", 4);
		SAT = new WeekEnum("SAT", 5);
		SYN = new WeekEnum("SYN", 6);
		$VALUES = (new WeekEnum[] {
			MON, TUE, WEN, THR, FRI, SAT, SYN
		});
	}
}

2.2 特点

我们来看看反编译之后的特点:

  • 编译后的枚举类是 Enum 的子类,且无法被继承, 无法实例化
  • 自定义的枚举实例是 final 修饰的,因此自定义的枚举实例是定义的枚举类对象,且是常量,通过静态代码块初始化
  • 会新增两个重要的方法 values() 和 valueOf(String name),(前者返回全部的枚举对象,后者根据值返回指定的枚举对象)

2.3 values()方法

  • valuse() 方法是静态方法,因此通过类调用和实例调用效果一致,他都会返回定义好的全部枚举实例,如下面代码所示,
    public static void main(String[] args) {
        WeekEnum[] values = WeekEnum.MON.values();
        for (WeekEnum w : values) {
            System.out.print(w + " ");
        }
        System.out.println();
        WeekEnum[] values1 = WeekEnum.values();
        for (WeekEnum w : values1) {
            System.out.print(w+ " ");
        }
    }
    
输出:
MON TUE WEN THR FRI SAT SYN 
MON TUE WEN THR FRI SAT SYN 

三、Enum

  • 由前面可知,Enum 是所有枚举类的抽象父类,因此我们看看 Enum 的特点,也帮助我们理解方法 values() 和 valueOf(String name),下面是主要的源码:

3.1 属性

public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializable {
     
    //枚举类名称,比如前面的 MON
    private final String name;
     
    //枚举的顺序,默认第一个是0,后面一次递增,比如前面的MON到SUN就是0-6
    private final int ordinal;

}

3.2 构造方法

  • 构造方法,查看前面反编译,其实就是调用这个方法,初始化name和oridinal,这个方法外部是无法调用的,子类也是在静态代码块的时候调用,创建预先定义好的枚举常量;
    protected Enum(String name, int ordinal) {
        this.name = name;
        this.ordinal = ordinal;
    }

3.3 其他方法

  • valueOf : valueOf方法就是通过name去寻找一个枚举常量,找到就返回,找不到就抛出 IllegalArgumentException
//反编译的枚举实例调用的valueOf方法,在反编译后的代码在valueOf方法只有一个String
    //类型的参数,另一个参数就是枚举的类型,然后参数会传给Enum的这个方法
    //
    public static <T extends Enum<T>> T valueOf(Class<T> enumType, String name) {
        //enumConstantDirectory 会返回一个Map,里面包含全部的枚举常量,然后通过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);
    }
    
下面是使用示例:
WeekEnum m = WeekEnum.valueOf("MON1"); //返回WeekEnum.MON
WeekEnum m = WeekEnum.valueOf("MON1"); //抛出异常
  • compareTo : 在类型一致的情况下,枚举之间可以比较 ordinal 的值,自身大则返回正数
    public final int compareTo(E o) {
        Enum<?> other = (Enum<?>)o;
        Enum<E> self = this;
        if (self.getClass() != other.getClass() && // optimization
            self.getDeclaringClass() != other.getDeclaringClass())
            throw new ClassCastException();
        return self.ordinal - other.ordinal;
    }
  • 其他方法
public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializable {

    //Enum将clone方法保护起来,不能在外部调用,只能子类调用,因此不能调用枚举类实例的clone方法
    protected final Object clone() throws CloneNotSupportedException {
        throw new CloneNotSupportedException();
    }

    //返回类型
    public final Class<E> getDeclaringClass() {
        Class<?> clazz = getClass();
        Class<?> zuper = clazz.getSuperclass();
        return (zuper == Enum.class) ? (Class<E>)clazz : (Class<E>)zuper;
    }
 
    //阻止默认的序列化
    private void readObject(ObjectInputStream in) throws IOException,
        ClassNotFoundException {
        throw new InvalidObjectException("can't deserialize enum");
    }

    private void readObjectNoData() throws ObjectStreamException {
        throw new InvalidObjectException("can't deserialize enum");
    }
}

四、应用

4.1 自定义构造方法

  • 枚举类也是一个类,因此也具备类的特性,可以定义方法和属性,下面我们给枚举类定义一个属性和构造方法,代码如下:
public enum WeekEnum {
    MON("星期一"),
    TUE("星期二"),
    WEN("星期三"),
    THR("星期四"),
    FRI("星期五"),
    SAT("星期六"),
    SYN("星期日");

    String desc;

    WeekEnum(String desc) {
        this.desc = desc;
    }
}
  • 测试:
        WeekEnum[] weekEnum =   WeekEnum.values();
        for (WeekEnum w: weekEnum) {
            System.out.print(w + w.desc+ " " );
        }
  • 输出:
MON星期一 TUE星期二 WEN星期三 THR星期四 FRI星期五 SAT星期六 SYN星期日 
  • 注意:即使我们定义了构造方法,但是却无法调用它,我们看编译后的就知道,就像最前面提到的,构造方法只能在编译器静态代码块中调用用于初始化属性,不能有用户显式调用;

4.2 抽象方法

  • 可以给枚举类内部定义一个抽象方法,这样每个枚举实现实现该方法,可以有不同的行为,不过需要记住的是每个枚举实例都是一个实例,都是一个枚举类型的常量实例,不是一个类型,这种用法貌似不多见。
public enum GenderEnum {
    MAN("男") {
        @Override
        protected String echo() {
            return "男人好难!";
        }
    },

    WOMAN("女") {
        @Override
        protected String echo() {
            return "女人好难!";
        }
    };

    String desc;

    GenderEnum(String desc) {
        this.desc = desc;
    }

    protected abstract String echo();
}

测试:
    GenderEnum[] values = GenderEnum.values();
        for (GenderEnum ge: values) {
            System.out.println(ge.echo());
        }

输出:
男人好难!
女人好难!

4.3 实现接口

  • 枚举类本身继承了Enum,因此不能再继承其他类,但是可以实现接口,从代码上看,和前面的抽象类很相似;
public enum GenderEnum implements Hello {
    MAN("男") {
        @Override
        public void say() {
            System.out.println("男人好难!");
        }

    },

    WOMAN("女") {
        @Override
        public void say() {
            System.out.println("女人好难!");
        }

    };

    String desc;

    GenderEnum(String desc) {
        this.desc = desc;
    }
}

接口:
public interface Hello {

    void say();
}

测试:
GenderEnum[] values = GenderEnum.values();
        for (GenderEnum ge: values) {
             ge.say();
        }

输出:
男人好难!
女人好难!

4.4 枚举和单例

  • 枚举类型的单例模式,枚举类型的单例模式在反射和序列化时也能保证安全
public enum  SingletonEnum {
    INSTANCE;
    private String name;
    public String getName(){
        return name;
    }
    public void setName(String name){
        this.name = name;
    }
}

参考

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值