Java基础:枚举(二)

一、枚举类

1. 枚举类介绍

1.1 什么是枚举类

java.lang有一个Enum表示枚举类,枚举类是java5中新增特性的一部分,它是一种特殊的类,一般表示一组常量。

我们在使用枚举类的时候不用去实现Enum,只需要在自定义枚举类的时候使用enum关键字即可。这也是枚举类和其它类不同的原因,自定义的枚举类没有class修饰而是enum

1.2 为什么使用枚举类

用枚举类实现的,我们也可以用常量来实现,那么使用枚举类的优点有哪些:意义明确,常量只是个变量,而枚举类可以加一些附属的描述信息;可以更好的归类,常量一般写在一个文件里面,而枚举类可以按照用途分类。

1.3 定义一个枚举类

定义枚举类有几个点需要注意

1.枚举类的修饰符是enum不是class

2.枚举类的变量不需要new来创建

3.枚举类必须要所有参数的构造方法,并且构造方法是private

public enum CoffeeEnum {

    ZB(1, "中杯"),

    DB(2, "大杯"),

    CDB(3, "超大杯");

    private int value;

    private String desc;

    CoffeeEnum(int value, String desc) {
        this.value = value;
        this.desc = desc;
    }

    public int getValue () {
        return this.value;
    }

    public String getDesc () {
        return this.desc;
    }
}

2. 枚举类实现原理

上面的枚举类,我们先试用命令javac CoffeeEnum.java编译生成CoffeeEnum.class

然后使用反编译工具JAD去反编译CoffeeEnum.class生成如下的代码,下载地址:JAD下载

当我们创建一个枚举类的时候,编译器会为我们生成一个相关的类,这个类继承了Enum类,通过上面反编译的代码可以看到CoffeeEnum这个类继承了Enum类,同时我们在枚举类中定义的各个类型也变成了CoffeeEnum对象,在静态方法中被初始化。

package com.lee.study.basic.enums;

public final class CoffeeEnum extends Enum{

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

    public static CoffeeEnum valueOf(String s) {
        return (CoffeeEnum)Enum.valueOf(com/lee/study/basic/enums/CoffeeEnum, s);
    }

    private CoffeeEnum(String s, int i, int j, String s1){
        super(s, i);
        value = j;
        desc = s1;
    }

    public int getValue(){
        return value;
    }

    public String getDesc(){
        return desc;
    }

    public static final CoffeeEnum ZB;
    public static final CoffeeEnum DB;
    public static final CoffeeEnum CDB;
    private int value;
    private String desc;
    private static final CoffeeEnum $VALUES[];

    static {
        ZB = new CoffeeEnum("ZB", 0, 1, "\u4E2D\u676F");
        DB = new CoffeeEnum("DB", 1, 2, "\u5927\u676F");
        CDB = new CoffeeEnum("CDB", 2, 3, "\u8D85\u5927\u676F");
        $VALUES = (new CoffeeEnum[] {
            ZB, DB, CDB
        });
    }
}

二、枚举类Enum详解

1. Enum介绍

EnumJava.lang包下面的一个抽象类,我们自定义的枚举类都会自动的继承这个类。

2.Enum方法介绍

下面一一介绍枚举类的用法,先自定义一个枚举类。

public enum CoffeeEnum {

    ZB(1, "中杯"),

    DB(2, "大杯"),

    CDB(3, "超大杯");

    private int value;

    private String desc;

    CoffeeEnum(int value, String desc) {
        this.value = value;
        this.desc = desc;
    }

    public int getValue () {
        return this.value;
    }

    public String getDesc () {
        return this.desc;
    }
}

2.1 valueOf(Class enumType, String name)

valueOf方法是一个静态方法,有两个参数,一个是枚举类类型,一个是名称,返回的是一个枚举对象。

CoffeeEnum coffeeEnum = Enum.valueOf(CoffeeEnum.class, "CDB");
System.out.println(coffeeEnum.getDesc());

2.2 valuesOf(String s)

这个方法虽然也叫valuesOf,但是参数只有一个,而且这个是编译器生成的,只需要传入枚举对象的名称,就可以返回对应的枚举对象。

CoffeeEnum coffeeEnum = CoffeeEnum.valueOf( "CDB");
System.out.println(coffeeEnum.getDesc());

2.3 values()

values方法是以数组的形式返回所有的枚举对象。

CoffeeEnum[] coffeeEnums = CoffeeEnum.values( );
for (CoffeeEnum coffeeEnum : coffeeEnums) {
  System.out.println(coffeeEnum.getDesc());
}

2.4 ordinal()

ordinal方法是返回枚举对象的索引位置,从0开始。那么这个方法是什么时候初始化的时候,我们看一下反编译之后的代码,这里我们可以看到我们定义了两个变量,但是构造方法有四个变量,而且sj直接传递给了父类,这里也就是初始化ordinal的地方。

private CoffeeEnum(String s, int i, int j, String s1){
  super(s, i);
  value = j;
  desc = s1;
}

2.5 name()

nameordinal方法一样,都是在编译器加的参数里面初始化的,name的值就是对象名称的字符串,索引就是从0开始,按照位置从上到下递增。

第一个参数就是name,第二个就是ordinal,第三个和第四个就是我们自己定义的valuedesc

ZB = new CoffeeEnum("ZB", 0, 1, "\u4E2D\u676F");
DB = new CoffeeEnum("DB", 1, 2, "\u5927\u676F");
CDB = new CoffeeEnum("CDB", 2, 3, "\u8D85\u5927\u676F");

三、枚举的应用

1. EnumMap

1.1 EnumMap介绍

EnumMapkey是一个枚举对象,valueObject的一个Map,它实现了Map接口,所以Map接口的常用方法都具备。EnumMapkey是有范围的,只能是在枚举类中定义对象类型。

1.2 构造方法

EnumMap有三个构造方法,其中第一个是接收一个枚举类类型,另外两个是接收一个Map集合,EnumMap的构造方法主要是初始化三个参数:

keyType:作为Mapkey的类型

keyUniverse:是一个数组变量,存放所有key值的数组

vals:是一个Object类型的数组变量,用于存放所有的value

EnumMap(Class<K> keyType);
EnumMap(EnumMap<K, ? extends V> m);
EnumMap(Map<K, ? extends V> m);
EnumMap<CoffeeEnum, String> enumMap = new EnumMap<>(CoffeeEnum.class);
enumMap.put(CoffeeEnum.CDB, "超大");
System.out.println(enumMap.get(CoffeeEnum.CDB));

1.3 put

put方法首先要检查一下key的类型是不是初始化的枚举类型,然后获取元素的索引,这个索引的位置也就是枚举对象的ordinal,所以EnumMap中元素的顺序和枚举类中定义对象的顺序一致,在EnumMap中允许值是空的,但是不允许key是空的,因为key的范围已经固定了,就是在枚举类中的对象。

public V put(K key, V value) {
    typeCheck(key);

    int index = key.ordinal();
    Object oldValue = vals[index];
    vals[index] = maskNull(value);
    if (oldValue == null)
        size++;
    return unmaskNull(oldValue);
}

1.4 remove

remove方法直接获取keyordinal,然后把对应位置的值置为NULL

public V remove(Object key) {
  if (!isValidKey(key))
    return null;
  int index = ((Enum<?>)key).ordinal();
  Object oldValue = vals[index];
  vals[index] = null;
  if (oldValue != null)
    size--;
  return unmaskNull(oldValue);
}

1.5 get

get方法先判断key是否是有效的,如果key是无效的直接返回空,然后获取对应索引位置的值,这里使用了unmaskNull方法作用是如果值不为空就返回值,否则就返回NULL

public V get(Object key) {
  return (isValidKey(key) ?
          unmaskNull(vals[((Enum<?>)key).ordinal()]) : null);
}

2. EnumSet

2.1 EnumSet介绍

EnumSet不是线程安全的

EnumSet是一个用于存放枚举对象集合的抽象类,实现了Set接口,不是一个线程安全的类。EnumSet由于是一个抽象类,所以不能直接的使用new来创建一个集合。

EnumSet有两个实现类:JumboEnumSetRegularEnumSet

2.2 创建EnumSet

虽然没有构造方法,但是我们还是可以创建一个EnumSet对象,在EnumSet里面提供了一些方法用于创建EnumSet,大概有以下几种

noneOf:创建一个空的集合
allOf:创建一个枚举类所有对象的集合
of:支持传多个对象的集合,of有多个参数的重载,而不是直接用可变参数,因为可变参数的效率低一些,预留的几个重载方法已经够用了
range:创建从枚举类某个位置到某个位置的对象的集合

这几个方法最后都是调用noneOf方法先创建一个空的集合,然后再使用add方法把元素添加进去,看一下noneOf的源码

从这个源码我们可以看出,如果枚举类的对象个数小于等于64位的话,就用RegularEnumSet,如果对象个数大于64个的话就用JumboEnumSet

public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) {
  Enum<?>[] universe = getUniverse(elementType);
  if (universe == null)
    throw new ClassCastException(elementType + " not an enum");

  if (universe.length <= 64)
    return new RegularEnumSet<>(elementType, universe);
  else
    return new JumboEnumSet<>(elementType, universe);
}

EnumSet的对元素的操作都是通过位向量来实现的,关于位向量可以参考

位向量的理解

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值