一、什么情况下使用枚举类?
有的时候一个类的对象是有限且固定的,这种情况下我们使用枚举类就比较方便?
二、为什么不用静态常量来替代枚举类呢?
public static final int SEASON_SPRING = 1;
public static final int SEASON_SUMMER = 2;
public static final int SEASON_FALL = 3;
public static final int SEASON_WINTER = 4;
枚举类更加直观,类型安全。使用常量会有以下几个缺陷:
1. 类型不安全。若一个方法中要求传入季节这个参数,用常量的话,形参就是int类型,开发者传入任意类型的int类型值就行,但是如果是枚举类型的话,就只能传入枚举类中包含的对象。
2. 没有命名空间。开发者要在命名的时候以SEASON_开头,这样另外一个开发者再看这段代码的时候,才知道这四个常量分别代表季节。
三、枚举类入门
先看一个简单的枚举类。
package enumcase;
public enum SeasonEnum {
SPRING,SUMMER,FALL,WINTER;
}
- enum和class、interface的地位一样
- 使用enum定义的枚举类默认继承了java.lang.Enum,而不是继承Object类。枚举类可以实现一个或多个接口。
- 枚举类的所有实例都必须放在第一行展示,不需使用new 关键字,不需显式调用构造器。自动添加public static final修饰。
- 使用enum定义、非抽象的枚举类默认使用final修饰,不可以被继承。
- 枚举类的构造器只能是私有的。
四、枚举类介绍
枚举类内也可以定义属性和方法,可是是静态的和非静态的。
package enumcase;
public enum SeasonEnum {
SPRING("春天"),SUMMER("夏天"),FALL("秋天"),WINTER("冬天");
private final String name;
private SeasonEnum(String name)
{
this.name = name;
}
public String getName() {
return name;
}
}
实际上在第一行写枚举类实例的时候,默认是调用了构造器的,所以此处需要传入参数,因为没有显式申明无参构造器,只能调用有参数的构造器。
构造器需定义成私有的,这样就不能在别处申明此类的对象了。枚举类通常应该设计成不可变类,它的Field不应该被改变,这样会更安全,而且代码更加简洁。所以我们将Field用private final修饰。
五、枚举类实现接口
枚举类可以实现一个或多个接口。与普通类一样,实现接口的时候需要实现接口中定义的所有方法,若没有完全实现,那这个枚举类就是抽象的,只是不需显式加上abstract修饰,系统化会默认加上。
package enumcase;
public enum Operation {
PLUS{
@Override
public double eval(double x, double y) {
return x + y;
}
},
MINUS{
@Override
public double eval(double x, double y) {
return x - y;
}
},
TIMES{
@Override
public double eval(double x, double y) {
return x * y;
}
},
DIVIDE{
@Override
public double eval(double x, double y) {
return x / y;
}
};
/**
* 抽象方法,由不同的枚举值提供不同的实现。
* @param x
* @param y
* @return
*/
public abstract double eval(double x, double y);
public static void main(String[] args) {
System.out.println(Operation.PLUS.eval(10, 2));
System.out.println(Operation.MINUS.eval(10, 2));
System.out.println(Operation.TIMES.eval(10, 2));
System.out.println(Operation.DIVIDE.eval(10, 2));
}
}
Operatio类实际上是抽象的,不可以创建枚举值,所以此处在申明枚举值的时候,都实现了抽象方法,这其实是匿名内部类的实现,花括号部分是一个类体。我们可以看下编译以后的文件。
共生成了五个class文件,这样就证明了PLUS,MINUS,TIMES,DIVIDE是Operation的匿名内部类的实例。
六、switch语句里的表达式可以是枚举值
Java5新增了enum关键字,同时扩展了switch。
package enumcase;
public class SeasonTest {
public void judge(SeasonEnum s)
{
switch(s)
{
case SPRING:
System.out.println("春天适合踏青。");
break;
case SUMMER:
System.out.println("夏天要去游泳啦。");
break;
case FALL:
System.out.println("秋天一定要去旅游哦。");
break;
case WINTER:
System.out.println("冬天要是下雪就好啦。");
break;
}
}
public static void main(String[] args) {
SeasonEnum s = SeasonEnum.SPRING;
SeasonTest test = new SeasonTest();
test.judge(s);
}
}
case表达式中直接写入枚举值,不需加入枚举类作为限定。
以上转自:https://www.cnblogs.com/sister/p/4700702.html
java enum(枚举)使用详解 + 总结
enum 的全称为 enumeration, 是 JDK 1.5 中引入的新特性,存放在 java.lang 包中。
下面是我在使用 enum 过程中的一些经验和总结,主要包括如下内容:
1. 原始的接口定义常量
2. 语法(定义)
7. enum 的原理分析
8. 总结
原始的接口定义常量
|
语法(定义)
创建枚举类型要使用 enum 关键字,隐含了所创建的类型都是 java.lang.Enum 类的子类(java.lang.Enum 是一个抽象类)。枚举类型符合通用模式 Class Enum<E extends Enum<E>>
,而 E
表示枚举类型的名称。枚举类型的每一个值都将映射到 protected Enum(String name, int ordinal)
构造函数中,在这里,每个值的名称都被转换成一个字符串,并且序数设置表示了此设置被创建的顺序。
|
这段代码实际上调用了7次 Enum(String name, int ordinal):
|
遍历、switch 等常用操作
对enum进行遍历和switch的操作示例代码:
|
输出结果:
|
enum 对象的常用方法介绍
int
compareTo(E o)
比较此枚举与指定对象的顺序。
Class<E>
getDeclaringClass()
返回与此枚举常量的枚举类型相对应的 Class 对象。
String
name()
返回此枚举常量的名称,在其枚举声明中对其进行声明。
int
ordinal()
返回枚举常量的序数(它在枚举声明中的位置,其中初始常量序数为零)。
String
toString()
返回枚举常量的名称,它包含在声明中。
static
<T extends Enum<T>> T
valueOf(Class<T> enumType, String name)
返回带指定名称的指定枚举类型的枚举常量。
|
输出结果:
|
给 enum 自定义属性和方法
给 enum 对象加一下 value 的属性和 getValue() 的方法:
|
|
输出结果:
|
EnumSet,EnumMap 的应用
|
原理分析
enum 的语法结构尽管和 class 的语法不一样,但是经过编译器编译之后产生的是一个class文件。该class文件经过反编译可以看到实际上是生成了一个类,该类继承了java.lang.Enum<E>。EnumTest 经过反编译(javap com.hmw.test.EnumTest 命令)之后得到的内容如下:
|
所以,实际上 enum 就是一个 class,只不过 java 编译器帮我们做了语法的解析和编译而已。
总结
可以把 enum 看成是一个普通的 class,它们都可以定义一些属性和方法,不同之处是:enum 不能使用 extends 关键字继承其他类,因为 enum 已经继承了 java.lang.Enum(java是单一继承)。
以上转自:https://www.cnblogs.com/hyl8218/p/5088287.html
EnumSet——专门存放枚举类型元素的Set:
1) EnumSet只能存放一种枚举类型的元素,具体存放什么枚举类型的元素可以通过两种方法指定,一种是显式,一种是隐式;
2) 一旦元素的枚举类型确定那么集合就确定了(即只能存放该种枚举类型的元素,不能同时存放多种枚举类型的元素!!否则就会抛出异常);
3) 枚举集合EnumSet底层并不是直接存放枚举对象的,而是用二进制位向量来存放的,因此存储非常紧凑,并且高效(比如枚举类型A里有4个枚举值a、b、c、d,并且这4个值都是对象,现在一个枚举集合存放a、c、d三个值,由于枚举类型的所有值的个数是有限的,因此可以用二进制序列来唯一表示,这里就用4位二进制数表示,现在只有a、c、d这三个值,因此可以表示为1011,1表示该枚举值在集合中,0表示不在集合种;
!!枚举值在枚举类型中是有序号的,就是按照其定义顺序排列的;
!!而从枚举集合中取出一个枚举值是是根据二进制位的位置还原原本的枚举值的,比如取出第二个二进制位置的枚举值,那么根据枚举类型中枚举值定义的顺序,第二个位置是b,那么取出的就是b,即位置和值是一一对应的;
!!由于是二进制位操作,因此向containsAll、retainAll等方法会非常高效;
4) EnumSet不允许包含null元素,强行添加会抛出异常!但是判空和删除null元素的方法可以正常调用,只不过永远返回null而已(因为不存在null元素,因此也没办法删除);
5) 由于使用二进制位来保存的,重复就更加不用担心的,每个二进位的位置就代表一个枚举值,因此一定不会重复;
2. 构造EnumSet:
1) EnumSet并没有提供公开的构造器来构造对象,而是提供了很多静态工具方法来构造EnumSet对象;
2) 下面介绍的都是EnumSet的静态工具方法,用来构造EnumSet对象:共有显式和隐式两种
****显式:显式(手工)指出存放元素所属的枚举类型
i. static EnumSet allOf(Class elementType); // 将elementType所代表的枚举类型的所有枚举值加入到集合中,例如EnumSet es = EnumSet.allOf(Season.class);
!!将Season枚举类的所有枚举值SPRING、SUMMER、FALL、WINNTER加入到集合中
ii. static EnumSet noneOf(Class elementType); // 创建一个空的、只用来存放elementType枚举类型值的集合
****隐式:不直接在参数中指定枚举类型,而是通过参数的类型自动推断枚举类型
i. 用其它枚举集合来构造:
a. static EnumSet complementOf(EnumSet s); // 用s之外的其它枚举值来构造一个集合(比如枚举类型有a、b、c、d 4个值,现在s有a和c,那么构造出来集合只包含b和d,即补集);
b. static EnumSet copyOf(EnumSet s); // s的深复制(不是引用复制)
ii. 直接用枚举值来构造:
a. static EnumSet<E> of(E first, E... rest); // 直接用若干枚举值构建一个枚举集合,例如EnumSet es = EnumSet.of(Season.SPRING, Season.WINNTER);
b. static EnumSet<E> range(E from, E to); // 直接用枚举值区间构建一个集合,包括[from, to]的所有枚举值
注意!
*1. 直接用枚举值构造时枚举值必须都属于同一个类型,否则会报错!
*2. [from, to]是闭区间!而不是左闭右开了,因为枚举类型无法表示最后一个值的后一个值!因此只有这里比较特殊,采用了右边闭合的区间;
*3. 枚举值的顺序就是枚举类型中枚举值定义的顺序;
3) 构造完之后就可以正常调用add等集合操作方法对枚举集合进行操作了;
3. 特殊的构造方式——用另一个集合来构造EnumSet:
1) 原型:static EnumSet copyOf(Collection c);
2) 如果c就是一个EnumSet那该方法就跟static EnumSet copyOf(EnumSet s);没有区别;
3) 如果是c是普通的集合(Set、List等),那就要求c里面存放的必须是同一类型的枚举类型对象,因为它会将c中的全部元素加入到新集合中,如果类型不一致肯定是不行的;
!!注意两个关键点:
i. c中元素的类型必须是枚举类型,否则和EnumSet的本质相违背肯定是会异常的!
ii. c中的元素必须属于同一枚举类型,否则也会违反EnumSet类型一致的规定而异常的!
4) 如果c是一个List,而里面的元素有重复会怎么样呢?没关系,重复元素就相当于add了多次,add的时候会自动判断是否重复的,如果重复则拒绝添加,因此最终产生的EnumSet是绝对不会重复(添加的时候自动去重了);
5) 示例:
public class Test {
enum Season {
SPRING, SUMMER, FALL, WINNTER
}
public static void main(String[] args) {
List li = new ArrayList<>();
li.add(Season.SPRING);
li.add(Season.SPRING);
li.add(Season.SUMMER);
EnumSet es = EnumSet.copyOf(li);
System.out.println(es); // [SPRING, SUMMER]
}
}
4. Set各实现类的性能以及该如何选择使用哪种实现类:
1) 就效率而言EnumSet毋庸置疑是最高的,毕竟是用二进制向量保存的,其次HashSet性能好于LinkedHashSet(仅仅多了一个维护插入顺序的链表),而TreeSet性能排最后,因为需要时刻维护红黑树的结构已达到有序状态(大小顺序);
2) 至于碰到一个问题该选择何种Set的实现类就很简单了,关键看你是什么需求,枚举就用EnumSet,仅仅是一个无需集合就用HashSet,需要维护插入顺序就用LinkedHashSet,需要维护大小顺序的就只能用TreeSet了!
以上转自:https://blog.csdn.net/Lirx_Tech/article/details/51516752
EnumSet类的常规应用
1 代码示例
import java.util.*;
enum Season
{
SPRING,SUMMER,FALL,WINTER
}
public class EnumSetTest
{
public static void main(String[] args)
{
// 创建一个EnumSet集合,集合元素就是Season枚举类的全部枚举值
EnumSet es1 = EnumSet.allOf(Season.class);
System.out.println(es1); // 输出[SPRING,SUMMER,FALL,WINTER]
// 创建一个EnumSet空集合,指定其集合元素是Season类的枚举值。
EnumSet es2 = EnumSet.noneOf(Season.class);
System.out.println(es2); // 输出[]
// 手动添加两个元素
es2.add(Season.WINTER);
es2.add(Season.SPRING);
System.out.println(es2); // 输出[SPRING,WINTER]
// 以指定枚举值创建EnumSet集合
EnumSet es3 = EnumSet.of(Season.SUMMER , Season.WINTER);
System.out.println(es3); // 输出[SUMMER,WINTER]
EnumSet es4 = EnumSet.range(Season.SUMMER , Season.WINTER);
System.out.println(es4); // 输出[SUMMER,FALL,WINTER]
// 新创建的EnumSet集合的元素和es4集合的元素有相同类型,
// es5的集合元素 + es4集合元素 = Season枚举类的全部枚举值
EnumSet es5 = EnumSet.complementOf(es4);
System.out.println(es5); // 输出[SPRING]
}
}
2 运行结果
[SPRING, SUMMER, FALL, WINTER]
[]
[SPRING, WINTER]
[SUMMER, WINTER]
[SUMMER, FALL, WINTER]
[SPRING]
3 代码说明
此代码示范了如何使用EnumSet来保存枚举类的多个枚举值。
三 EnumSet类的错误使用
1 代码示例
import java.util.*;
enum Season
{
SPRING,SUMMER,FALL,WINTER
}
public class EnumSetTest2
{
public static void main(String[] args)
{
Collection c = new HashSet();
c.clear();
c.add(Season.FALL);
c.add(Season.SPRING);
// 复制Collection集合中所有元素来创建EnumSet集合
EnumSet enumSet = EnumSet.copyOf(c); // ①
System.out.println(enumSet); // 输出[SPRING,FALL]
c.add("Java");
c.add("Linux");
// 下面代码出现异常:因为c集合里的元素不是全部都为枚举值
enumSet = EnumSet.copyOf(c); // ②
System.out.println(enumSet);
}
}
2 运行结果
[SPRING, FALL]
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Enum
at java.util.EnumSet.copyOf(Unknown Source)
at EnumSetTest2.main(EnumSetTest2.java:21)
3 代码说明
当试图复制一个Collection集合里的元素来创建EnumSet集合时,必须保证Collection集合里的所有元素都是同一个枚举类的枚举值
原文:https://blog.csdn.net/chengqiuming/article/details/70139107
自写demo地址:https://gitee.com/xuefengo/enum.git
如有侵权请联系我,谢谢、