枚举
1、定义枚举类
Java语言从设计之初并没有提供枚举的概念,所以开发者不得不使用多例模式来代替枚举的解决方案,直到JDK 1.5开始,Java支持了枚举结构的定义,提供了一个新的关键字:enum,利用此关键字可以实现枚举类型的定义,并且通过枚举可以简化多例模式设计的实现。
public enum UserRole {
// 系统管理员
ROLE_ROOT_ADMIN,
// 订单管理员
ROLE_ORDER_ADMIN,
// 普通用户
ROLE_NORMAL
}
定义了一个UserRole的枚举类,并在类的内部提供有UserRole类的三个实例化对象,所有枚举类的对象名称全部采用了大写字母定义,这一点是符合多例设计模式要求的,同时枚举类对象的名称也可以采用中文的形式进行定义,在外部调用时可以直接通过枚举名称进行对象的调用。
1.1、枚举常用的方法
范例:枚举的一些常用的方法
public enum UserRole {
// 系统管理员
ROLE_ROOT_ADMIN,
// 订单管理员
ROLE_ORDER_ADMIN,
// 普通用户
ROLE_NORMAL
}
public class EnumTestDemo {
public static void main(String[] args) {
UserRole role1 = UserRole.ROLE_ROOT_ADMIN;
UserRole role2 = UserRole.ROLE_ORDER_ADMIN;
UserRole role3 = UserRole.ROLE_NORMAL;
// values()方法:返回所有枚举常量的数组集合
for (UserRole role : UserRole.values()) {
System.out.println(role);
}
System.out.println();
// 打印:
// ROLE_ROOT_ADMIN
// ROLE_ORDER_ADMIN
// ROLE_NORMAL
// ordinal()方法:返回枚举常量的序数,注意从0开始
System.out.println(role1.ordinal()); // 打印0
System.out.println(role2.ordinal()); // 打印1
System.out.println(role3.ordinal()); // 打印2
System.out.println();
// compareTo()方法:枚举常量间的比较
System.out.println(role1.compareTo(role2)); //打印-1
System.out.println(role2.compareTo(role3)); //打印-1
System.out.println(role1.compareTo(role3)); //打印-2
System.out.println();
// name()方法:获得枚举常量的名称
System.out.println(role1.name()); // 打印ROLE_ROOT_ADMIN
System.out.println(role2.name()); // 打印ROLE_ORDER_ADMIN
System.out.println(role3.name()); // 打印ROLE_NORMAL
System.out.println();
// valueOf()方法:返回指定名称的枚举常量
System.out.println(UserRole.valueOf("ROLE_ROOT_ADMIN"));
System.out.println(UserRole.valueOf("ROLE_ORDER_ADMIN"));
System.out.println(UserRole.valueOf("ROLE_NORMAL"));
System.out.println();
}
}
/*
输出结果:
ROLE_ROOT_ADMIN
ROLE_ORDER_ADMIN
ROLE_NORMAL
0
1
2
-1
-1
-2
ROLE_ROOT_ADMIN
ROLE_ORDER_ADMIN
ROLE_NORMAL
ROLE_ROOT_ADMIN
ROLE_ORDER_ADMIN
ROLE_NORMAL
*/
1.3、枚举与switch语句
采用枚举代替多例设计模式的还有一个很重要的原因,就是枚举可以直接在switch语句中进行枚举对象类型判断,而且意义更加明确。
范例:在swtich中判断枚举类型
public enum UserRole {
// 系统管理员
ROLE_ROOT_ADMIN,
// 订单管理员
ROLE_ORDER_ADMIN,
// 普通用户
ROLE_NORMAL
}
public class EnumTestDemo {
public static void main(String[] args) {
UserRole userRole = UserRole.ROLE_ORDER_ADMIN;
switch (userRole) {
case ROLE_ROOT_ADMIN:
System.out.println("这是系统管理员");
break;
case ROLE_ORDER_ADMIN:
System.out.println("这是订单管理员");
break;
case ROLE_NORMAL:
System.out.println("这是订单管理员");
break;
}
}
}
/*
输出结果:
这是订单管理员
*/
上述程序直接在switch语句中实现枚举对象的匹配,而若使用多例设计模式,则只能通过大量的if/else语句的判断来进行内容的匹配与结果的输出。
tips:关于switch允许操作的数据类型
- switch语句中支持判断的数据类型,随着JDK版本的升级也越来越完善。
- JDK 1.5之前:只支持byte、short、char、int四种数据类型
- JDK 1.5:增加了枚举类
- JDK 1.7:增加了String类型
- JDK 1.8:支持10种类型
- 基本类型:byte、char、short、int
- 包装类:Byte、Short、Character、Integer、String、enum
- 但是,值得一提的是,实际上Java的switch语句只支持int类型,那么其他类型是如何支持的。
- 基本类型:byte、char、short三种数据类型可以在不失精度的情况下向上转型为int类型,所以实际上还是int类型
- 基本类型包装类:Java的自动拆箱机制,java编译器会在底层将Byte、Character、Short、Integer这些包装类进行拆箱,将包装器类型转换为基本数据类型
- enum类型:枚举类有一个ordinal方法,比较的是enum的ordinal值(表示枚举值的顺序),实际上是int类型的数值
- String类型:String类有一个hashCode算法,比较的string.hashCode值,结果也是int类型
2、Enum类
枚举并不是一个新的类型,它是提供了一种更为方便的结构。严格来说,每一个使用enum定义的类实际上都属于一个类继承了Enum父类而已。Enum类实现了枚举公共操作方法的定义,同样基于构造方法私有化的形式完成,java.lang.Enum类定义如下:
public abstract class Enum<E extends Enum<E>> extends Object implements Comparable<E>, Serializable{}
在Enum类中定义了其可以支持的泛型上限,同时Enum类提供了一些常用方法,如下表2-1所示:
方法名称 | 类型 | 描述 |
---|---|---|
protected Enum(String name, int ordinal) | 构造 | 传入名字和序号 |
public final String name() | 普通 | 获取对象名字 |
public final int ordina() | 普通 | 获取对象序号 |
通过表2-1,可以发现Enum类中的构造方法使用了protected访问权限,实际上这也是属于构造方法的封装表现,同时在实例化每一个枚举类对象时都可以自动传递以一个对象名称以及序号。
范例:enum关键与Enum类之间的联系
// 枚举类
public enum Color {
// 实例化对象
RED, GREEN, BLUE;
}
public class EnumTestDemo {
public static void main(String[] args) {
for(Color color : Color.values()) {
// 获取枚举信息
System.out.println(color.ordinal + " - " + color.name());
}
}
}
/*
输出结果:
0 - RED
1 - GREEN
2 - BLUE
*/
本程序中每输出一个枚举对象时都调用了Enum类中定义的ordina()与name()方法来获取相应的信息,便可证明,enum定义的枚举类是默默继承了Enum父类。
3、定义枚举结构
Java中的枚举结构与其他语言相比得到进一步的结构提升,在枚举类中除了可以定义若干个实例化对象之外,也可以像普通类一样定义成员属性、构造方法(注意:枚举的本质上是属于多例设计模式,所以构造方法不允许使用public进行定义)、普通方法。若类中没有提供无参构造方法,则必须在定义每一个枚举对象时明确传入参数内容。
3.1、自定义扩张枚举类
扩展上述范例中的UserRole枚举,在里面加入“角色名—角色编码”的对应关系,这也是项目中常用的用法。
范例:在枚举类中定义成员属性成员方法
public enum UserRole {
ROLE_ROOT_ADMIN("系统管理员", "1001"),
ROLE_ORDER_ADMIN("订单管理员", "1002"),
ROLE_NORMAL("普通用户", "1003");
//自定义属性
private final String roleName;
private final String roleCode;
//自定义构造
UserRole(String roleName, String roleCode) {
this.roleName = roleName;
this.roleCode = roleCode;
}
//自定义方法
public String getRoleName() {
return this.roleName;
}
public String getRoleCode() {
return this.roleCode;
}
}
public class EnumTestDemo {
public static void main(String[] args) {
for (UserRole userRole : UserRole.values()) {
System.out.println(userRole.getRoleName() + "---" + userRole.getRoleCode());
}
}
}
/*
输出结果:
系统管理员---1001
订单管理员---1002
普通用户---1003
*/
3.2、枚举+接口
范例:通过枚举类实现接口
//IMessage接口
public interface IMessage {
//获取信息
public String getMessage();
}
//枚举类实现接口
public enum Color implements IMessage {
//枚举对象要写在首行
RED("红色", 1),
GREEN("绿色", 2),
BLUE("蓝色", 3);
//自定义成员属性
private final String title;
private final Integer code;
//自定义构造方法,初始化属性
Color(String title, Integer code) {
this.title = title;
this.code = code;
}
//方法覆盖
@Override
public String getMessage() {
return this.title + "---" + this.code;
}
}
//测试
public class EnumTestDemo {
public static void main(String[] args) {
//接口引用指向接口实现类对象
//IMessage color = Color.DED;
for (IMessage color : Color.values()) {
System.out.println(color.getMessage());
}
}
}
/*
输出结果:
红色---1
绿色---2
蓝色---3
*/
此范例程序,枚举类Color实现了IMessage接口,在Color类中覆盖接口中的抽象方法,并且由于Color类是IMessage的实现子类,所以每一个枚举类对象都可以通过对象的向上转型实现IMessage接口对象实例化(接口引用指向接口实现类对象)。
枚举还有一个特别的功能就是可以直接进行抽象方法的定义,此时可以在每一个枚举类对象中分别实现此抽象方法。
范例:在枚举类中定义抽象方法
/*
什么角色干干什么事,很明显有个对应关系,所以我们先定义一个公用的接口
RoleOperation,表示不同角色所能做的操作
*/
public interface RoleOperation {
//表示某个角色能做的哪些操作
public String getOperation();
}
/*
不同角色的情况交由枚举类来做,定义一个枚举类RoleEnum,并让其实现RoleOperation接口
*/
public enum RoleEnum implements RoleOperation {
// 系统管理员(有A操作权限)
ROLE_ROOT_ADMIN("系统管理员") {
//覆盖抽象方法
@Override
public String getOperation() {
return ROLE_ROOT_ADMIN.getRoleName() + "有A操作权限";
}
},
// 订单管理员(有B操作权限)
ROLE_ORDER_ADMIN("订单管理员") {
@Override
public String getOperation() {
return ROLE_ORDER_ADMIN.getRoleName() + "有B操作权限";
}
},
// 普通用户(有C操作权限)
ROLE_NORMAL("普通用户") {
@Override
public String getOperation() {
return ROLE_NORMAL.getRoleName() + "有C操作权限";
}
};
private String roleName;
RoleEnum(String roleName) {
this.roleName = roleName;
}
public String getRoleName() {
return this.roleName;
}
/*
//直接定义抽象方法(也可以不用定于接口和实现接口,直接定义抽象方法,然后重写抽象方法即可)
@Override
public abstract String getOperation() {}
*/
}
public class EnumTestDemo {
public static void main(String[] args) {
RoleEnum[] roleEnums = RoleEnum.values();
for (RoleEnum roleEnum : roleEnums) {
System.out.println(roleEnum.getOperation());
}
}
}
/*
输出结果:
系统管理员有A操作权限
订单管理员有B操作权限
普通用户有C操作权限
*/
4、枚举应用小案例
枚举作为一种提供有限实例化对象个数的类结构,主要是定义了实例化对象的使用范围,同时枚举类型也可以作为成员属性类型。例如,现在定义一个Persion类,里面需要提供有性别属性,而性别肯定不希望用户随意输入,所以使用枚举类型更加合适。
范例:枚举结构小案例
//性别
public enum Sex {
//枚举对象
MALE("男"),FEMALE("女");
//成员属性
private final String sex;
//构造方法
private Sex(String sex) {
this.sex = sex;
}
//重写Object类中的toString()方法
@Override
public String toString() {
return this.sex;
}
}
//普通类Person
public class Person {
//姓名
private String name;
//年龄
private int age;
//性别
private Sex sex;
//无参构造
public Person() {
}
//有参构造
public Person(String name, int age, Sex sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Sex getSex() {
return sex;
}
public void setSex(Sex sex) {
this.sex = sex;
}
//重写Object类中的toString()方法
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", sex=" + sex +
'}';
}
}
public class EnumTestDemo {
public static void main(String[] args) {
Person person01 = new Person("zhangsan", 18, Sex.MALE);
System.out.println(person01);
Person person02 = new Person();
person02.setName("lisi");
person02.setAge(30);
person02.setSex(Sex.valueOf("FEMALE"));
System.out.println(person02);
}
}
/*
输出结果:
Person{name='zhangsan', age=18, sex=男}
Person{name='lisi', age=30, sex=女}
*/
在定义Person类时使用了枚举类型,实例化Person类对象时就可以限定Sex对象的范围。
5、专门用于枚举的集合类
在平时学习过程中,我们一般习惯于使用诸如:**“HashMap” 和“HashSet”**等集合来盛放元素,而对于枚举,有它专门的集合类:“EnumSet”和“EnumMap”。
5.1、EnumSet集合
**“EnumSet”集合是专门为盛放枚举类型所设计的”Set“**类型集合。
/*
还是以角色枚举为例
*/
public enum UserRole {
// 系统管理员
ROLE_ROOT_ADMIN,
// 订单管理员
ROLE_ORDER_ADMIN,
// 普通用户
ROLE_NORMAL
}
/*
比如现在系统里来了一批人,我们需要查看他是不是某个角色中的一个
*/
//定义一个管理员角色的专属集合
EnumSet<UserRole> userRoleEnumSet = EnumSet.of(
UserRole.ROLE_ROOT_ADMIN,
UserRole.ROLE_ORDER_ADMIN
);
//判断某个角色是不是管理员
Boolean isAdmin(User user) {
if(userRoles.contains(user.getUserRole())) {
return true;
}
return false;
}
5.2、EnumMap集合
**“EnumMap”集合是专门为盛放枚举类型为“key”所设计的”Map“**类型集合。
/*
系统里来了一批人,我们需要统计不同的角色到底有多少人
*/
Map<UserRole, Integer> userRoleMap = new EnumMap<>(UserRole.class);
for(User user : userList) {
Integer num = userRoleMap.get(user.getUserRole());
if(null != num) {
userRoleMap.put(user.getUserRole(), num + 1);
} else {
userRoleMap.put(user.getUserRole(), 1);
}
}
6、枚举与设计模式
6.1、单例模式
public class Singleton {
// 构造函数私有化,避免外部创建实例
private Singleton() {
}
//定义一个内部枚举
public enum SingletonEnum {
// 唯一一个枚举对象,我们称它为“种子选手”!
SEED;
private Singleton singleton;
SingletonEnum() {
//真正的对象创建隐蔽在此!
singleton = new Singleton();
}
public Singleton getInstnce() {
return singleton;
}
}
// 故意外露的对象获取方法,也是外面获取实例的唯一入口
public static Singleton getInstance() {
// 通过枚举的种子选手来完成
return SingletonEnum.SEED.getInstnce();
}
}
6.2、策略模式
范例:用枚举就可以写出一个基于策略模式的加减乘除计算器
public enum Calculator {
ADDITION {
public Double execute(Double x, Double y) {
// 加法
return x + y;
}
},
SUBTRACTION {
public Double execute(Double x, Double y) {
// 减法
return x - y;
}
},
MULTIPLICATION {
public Double execute(Double x, Double y) {
// 乘法
return x * y;
}
},
DIVISION {
public Double execute(Double x, Double y) {
// 除法
return x / y;
}
};
public abstract Double execute(Double x, Double y);
}
public class EnumTestDemo {
public static void main(String[] args) {
System.out.println(Calculator.ADDITION.execute(4.0, 2.0));
System.out.println(Calculator.SUBTRACTION.execute(4.0, 2.0));
System.out.println(Calculator.MULTIPLICATION.execute(4.0, 2.0));
System.out.println(Calculator.DIVISION.execute(4.0, 2.0));
}
}
/*
输出结果:
6.0
2.0
8.0
2.0
*/
注:此文章只是学习过程中做的个人笔记,如有错误,敬请谅解。