[转载] Java中枚举类型的使用 - enum

时间:2022-05-02 00:47:37

本文转载自博客 - Java枚举类型, 博主对原文内容及结构作了一定的修改.

修改时参考到的文章:深入理解Java枚举类型(enum).

1 枚举类的编译特性

从JDK 5开始, Java中多了一个关键字 —— enum: 可以将一组具有名称的值(包括String、Integer等)的有限集合创建为一种新的类型, 而这些具名的值可以作为常规的程序组件使用.

这些具名的值称为枚举值, 这种新的类型称为枚举类型.

1.1 枚举类演示

下面是一个简单的表示星期几的枚举类:

enum Day {
// 结尾处可以不用“;”, 但若有其他方法, 必须通过“;”结束枚举实例的声明
SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY
}

创建enum时, 编译器会自动为enum类添加一些特性, 比如:

a) 创建toString()方法: 以便显示某个枚举实例的名字;

b) 创建name()方法: 获取枚举类型中某个实例的名称;

c) 创建ordinal()方法: 表示某个特定枚举常量的申明顺序, 从0开始;

d) 创建values()方法: 按照枚举常量的申明顺序产生这些常量构成的数组…...

关于这些特性, 编写测试方法如下:

public class EnumTest {
public static void main(String[] args) {
// 获取枚举类型的父类型
System.out.println(Day.class.getSuperclass());
// 遍历枚举类型中的值
for (Day day : Day.values()) {
System.out.println(day.name() + ", ordinal: " + day.ordinal());
}
}
}

程序的输出结果是:

class java.lang.Enum   // 所有enum的父类都是它
SUNDAY, ordinal: 0 // ordinal()方法从0开始计数
MONDAY, ordinal: 1
TUESDAY, ordinal: 2
WEDNESDAY, ordinal: 3
THURSDAY, ordinal: 4
FRIDAY, ordinal: 5
SATURDAY, ordinal: 6

从输出结果可以看到: 枚举类型Day的父类是java.lang.Enum——我们的代码中并没有指明extends动作, 所以这是由编译器完成的.

1.2 反编译查看编译器的操作

(1) 利用javac编译EnumTest.java文件后, 会生成Day.classEnumTest.class文件, 这里的Day.class就是枚举类型 —— 使用关键字enum定义枚举类型并编译后, 编译器会自动生成一个与枚举相关的类;

(2) 反编译查看Day.class文件:

// final修饰的类, 不允许再被继承
final class Day extends Enum {
// 编译器添加的静态values()方法
public static Day[] values() {
return (Day[])$VALUES.clone();
}
// 编译器添加的静态valueOf()方法 —— 间接调用了Enum类的valueOf()方法
public static Day valueOf(String s) {
return (Day)Enum.valueOf(com/healchow/java/Day, s);
}
// 私有构造函数, 不允许被外部调用
private Day(String s, int i) {
super(s, i);
}
// 前面定义的7种枚举实例, 都是静态常量
public static final Day MONDAY;
public static final Day TUESDAY;
public static final Day WEDNESDAY;
public static final Day THURSDAY;
public static final Day FRIDAY;
public static final Day SATURDAY;
public static final Day SUNDAY;
private static final Day $VALUES[]; static {
// 实例化枚举实例, 并指定声明的次序
MONDAY = new Day("MONDAY", 0);
TUESDAY = new Day("TUESDAY", 1);
WEDNESDAY = new Day("WEDNESDAY", 2);
THURSDAY = new Day("THURSDAY", 3);
FRIDAY = new Day("FRIDAY", 4);
SATURDAY = new Day("SATURDAY", 5);
SUNDAY = new Day("SUNDAY", 6);
$VALUES = (new Day[] {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
});
}
}

(3) 编译器生成的静态方法:

values(): 获取枚举类中的所有变量, 并作为数组返回;

valueOf(String name): 与Enum类中的valueOf()的作用类似 —— 根据名称获取枚举变量, 只不过编译器生成的valueOf()更简洁: 只需传递一个参数.

==> 这两个方法是编译器自动加入的, Enum类中并没有, 所以如果我们将枚举实例向上转型为Enum, 那这两个方法就无法被调用了.

(4) 枚举类不能继承其他任何类:

由于枚举类已经继承了java.lang.Enum(是一个抽象类), 而Java又不支持多继承, 所以enum不能再继承其他的类, 但是能实现接口. —— 这在反编译后的信息中可以看到, 编译器为enum生成的class被final修饰, 也就是不允许被继承.

==> 所以, enum只是看起来像一种新的数据类型, 除了上面讲到的这些特殊的编译行为, 并没有什么特殊的地方.

2 向枚举类中添加方法

除了不能继承一个enum外, 基本上可以把enum当作一个普通的类来处理, 也就是说可以向enum中添加方法, 比如返回其自身描述的方法, 还可以添加main方法.

下面是一个演示enum添加自定义方法和实现接口的示例:

(1) 定义一个对象描述信息的接口:

interface ObjectDescription {
String todo();
}

(2) 创建枚举类:

 public enum Signal implements ObjectDescription {
// 结尾处可以不用“;”, 但若有其他方法, 必须通过“;”结束枚举实例的声明
Red("红灯", "敢过去就是6分, 还要罚款哦"),
Yellow("黄灯", "黄灯你也不敢过"),
Green("绿灯", "绿灯也得小心过啊"); // 其他属性、方法都必须定义在枚举实例的声明之后, 否则编译器将报错
private String name;
private String description; /**
* 构造方法, 对内部变量进行初始化
*/
Signal(String name, String description) {
this.name = name;
this.description = description;
} public String getName() {
return name;
} public String getDescription() {
return description;
} @Override
public String todo() {
return "Signal类用于表示交通信号灯, [" + this + "] 表示 [" + this.getName() + "]";
} public static void main(String[] args) {
// 调用实现的接口中的方法
for (Signal signal : Signal.values()) {
System.out.println(signal.todo());
}
// 调用自定义的方法
for (Signal signal : Signal.values()) {
System.out.println(signal.getName() + ": " + signal.getDescription());
}
}
}

(3) 运行结果如下:

Signal类用于表示交通信号灯, [Red] 表示 [红灯]
Signal类用于表示交通信号灯, [Yellow] 表示 [黄灯]
Signal类用于表示交通信号灯, [Green] 表示 [绿灯]
红灯: 敢过去就是6分, 还要罚款哦
黄灯: 黄灯你也不敢过
绿灯: 绿灯也得小心过啊

使用注意事项:

a) 如果要自定义方法, 就必须在enum实例序列的最后添加一个分号, 同时Java要求必须先定义enum实例, 否则编译时会报错.

b) enum的构造器只能是private, 它只能在enum内部被用来创建enum实例, 一旦enum定义结束, 编译器就不允许再使用其构造器来创建任何实例了.

3 枚举类中使用抽象方法

与常规抽象类一样, 枚举类允许我们为其定义抽象方法, 然后灵每个枚举实例都实现该方法, 以便产生不同的行为方式.

注意: abstract关键字对于枚举类来说并不是必须的.

public enum EnumTest {
// 枚举实例的声明必须在最前
FIRST {
// 实现抽象方法
@Override
public String getInfo() {
return "FIRST TIME";
}
},
SECOND {
// 实现抽象方法
@Override
public String getInfo() {
return "SECOND TIME";
}
} ; // 如果之后还有其他成员, 就必须用“;”结束 /**
* 定义抽象方法
* @return
*/
public abstract String getInfo(); // 测试
public static void main(String[] args) { System.out.println("First: " + EnumTest.FIRST.getInfo()); // First: FIRST TIME
System.out.println("Second: " + EnumTest.SECOND.getInfo()); // Second: SECOND TIME
}
}

上述方式为每个枚举实例定义了不同的行为.

我们可能注意到, 枚举类的实例似乎表现出了多态的特性, 可惜的是枚举类型的实例终究不能作为类型传递使用, 就像下面的使用方式, 是不能通过编译器的检查的:

// 无法通过编译, 毕竟EnumTest.FIRST是个实例对象
public void text(EnumTest.FIRST instance){ }

4 接口内部创建枚举

无法使用继承限制了枚举的使用, 比如需要用enum表示食物, 但同时需要区分水果、点心等类型, 这个时候就没不够灵活了.

我们通过在一个接口内部创建实现该接口的枚举, 从而达到对元素进行分类组织的目的:

public interface Food {
/**
* 开胃菜
*/
enum Appetizer implements Food {
// 结尾处可以不用“;”, 但若有其他方法, 必须通过“;”结束枚举实例的声明
SALAD, SOUP, SPRING_ROLLS
} /**
* 主菜
*/
enum MainCourse implements Food {
RICE, NOODLES, VINDALOO, BEEF
} /**
* 甜品
*/
enum Dessert implements Food {
TIRAMISU, ICE_CREAM, BISCUIT, FRUIT
} /**
* 咖啡
*/
enum Coffee implements Food {
BLACK_COFFEE, DECAF_COFFEE, LATTE
}
}

enum而言, 实现接口是使其子类化的唯一方法.

通过上面的形式, 成功地完成了对不同食物的分组, 并且它们都是Food.

5 枚举类中使用枚举

下面是一个枚举的随机选择器, 是一个工具类:

public class Enums {
private static Random rand = new Random(47); public static <T extends Enum<T>> T random(Class<T> enumClazz) {
return random(enumClazz.getEnumConstants());
} public static <T> T random(T[] values) {
return values[rand.nextInt(values.length)];
}
}

结合工具类及上面Food接口的内容, 下面通过枚举的枚举实现一个产生随机菜单的例子:

public enum Course {
// 结尾处可以不用“;”, 但若有其他方法, 必须通过“;”结束枚举实例的声明
APPETIZER(Food.Appetizer.class),
MAINCOURSE(Food.MainCourse.class),
DESSERT(Food.Dessert.class),
COFFEE(Food.Coffee.class); // 其他属性、方法都必须定义在枚举实例的声明之后, 否则编译器将报错
private Food[] values; Course(Class<? extends Food> kind) {
// 返回枚举中所有的元素, 及所有实例构成的数组, 如果kind不是枚举返回null
values = kind.getEnumConstants();
} public Food randomSelection() {
return Enums.random(values);
} public static void main(String[] args) {
// 产生5份随机菜单
for (int i = 0; i < 5; i++) {
for (Course c : Course.values()) {
Food food = c.randomSelection();
System.out.println(food + " ");
}
System.out.println("---------------");
}
}
}

6 扩展: 验证values()不是通过父类继承的

下面的代码用来验证values()方法是enum自身的, 而不是继承自父类java.lang.Enum的:

public enum Signal implements ObjectDescription {
// 结尾处可以不用“;”, 但若有其他方法, 必须通过“;”结束枚举实例的声明
Red("红灯", "敢过去就是6分, 还要罚款哦"),
Yellow("黄灯", "黄灯你也不敢过"),
Green("绿灯", "绿灯也得小心过啊"); // 其他属性、方法都必须定义在枚举实例的声明之后, 否则编译器将报错
private String name;
private String description; Signal(String name, String description) {
this.name = name;
this.description = description;
} public String getName() {
return name;
} public String getDescription() {
return description;
} @Override
public String todo() {
return "Signal类用于表示交通信号灯, [" + this + "] 表示 [" + this.getName() + "]";
} public static void main(String[] args) {
Set<Method> methodSet = new HashSet<Method>();
// 获取本类的所有方法
Method[] signalMethods = Signal.class.getMethods();
for (Method m : signalMethods) {
methodSet.add(m);
}
// 获取父类中的方法
Method[] superClassMethods = Signal.class.getSuperclass().getMethods();
// 去除本类中继承的父类方法
for (Method m : superClassMethods) {
methodSet.remove(m);
}
// 遍历输出本类中独有的方法
for(Method m : methodSet) {
System.out.println(m);
}
}
}

结果如下, 其中各个字段的含义依次为访问权限 [是否静态] 返回值类型的全限定名称 方法的全限定名称:

public static com.healchow.Signal com.healchow.Signal.valueOf(java.lang.String)
public static com.healchow.Signal[] com.healchow.Signal.values()
public static void com.healchow.Signal.main(java.lang.String[])
public java.lang.String com.healchow.Signal.todo()

版权声明

本文版权归原作者所有, 如有侵权, 请联系博主, 定当立即删除.

若要转载, 请在文章页面明显位置标明原始链接, 否则一切责任自负.

[转载] Java中枚举类型的使用 - enum的更多相关文章

  1. java中枚举类型的使用

    Java 枚举(enum) 详解7种常见的用法 JDK1.5引入了新的类型——枚举.在 Java 中它虽然算个“小”功能,却给我的开发带来了“大”方便. web项目里实体类使用枚举类型: 一般在该实体 ...

  2. Java中枚举类型与for、switch语句

    1.枚举类型的声明 格式为: enum 枚举类型名{ 常量1,常量2,常量3 } 如: enum Number{ one,two,three,four,five    //常量} 注意:enum内装的 ...

  3. Java中枚举类型Enum的一种使用方式

    枚举类定义如下: public enum Status { SCUUESS("1", "成功"), FAILED("2", "失败 ...

  4. Java中枚举类型简单学习

    /* * enum类型不允许继承 * 除了这一点,我们基本上可以将enum看作一个常规的类 * 我们可以添加自己的方法与属性,我们也可以覆盖其中的方法. * 不过一定要在enum实例序列的最后添加分号 ...

  5. 深度分析 Java 的枚举类型:枚举的线程安全性及序列化问题(转)

    写在前面: Java SE5 提供了一种新的类型 Java的枚举类型,关键字 enum 可以将一组具名的值的有限集合创建为一种新的类型,而这些具名的值可以作为常规的程序组件使用,这是一种非常有用的功能 ...

  6. 《挑战30天C&plus;&plus;入门极限》新手入门:C&sol;C&plus;&plus;中枚举类型&lpar;enum&rpar;

        新手入门:C/C++中枚举类型(enum) 如果一个变量你需要几种可能存在的值,那么就可以被定义成为枚举类型.之所以叫枚举就是说将变量或者叫对象可能存在的情况也可以说是可能的值一一例举出来. ...

  7. 深度分析Java的枚举类型—-枚举的线程安全性及序列化问题

    原文:深度分析Java的枚举类型--枚举的线程安全性及序列化问题 枚举是如何保证线程安全的 要想看源码,首先得有一个类吧,那么枚举类型到底是什么类呢?是enum吗?答案很明显不是,enum就和clas ...

  8. c&num;中枚举类型的定义与使用

    介绍枚举是一个指定的常数,其基础类型可以是除 Char 外的任何整型.如果没有显式声明基础类型,则使用 Int32.编程语言通常提供语法来声明由一组已命名的常数和它们的值组成的枚举.定义默认基数从O开 ...

  9. Java的枚举类型使用方法详解

    1.背景在java语言中还没有引入枚举类型之前,表示枚举类型的常用模式是声明一组具有int常量.之前我们通常利用public final static 方法定义的代码如下,分别用1 表示春天,2表示夏 ...

随机推荐

  1. MVC学习系列——Filter扩展

    在MVC中,Filter也是可以扩展的.在此,本人对Filter的理解就是AOP,不知道各位大侠,有什么高的见解,呵呵... 首先MVC四大过滤神器IAuthorizationFilter,IActi ...

  2. Apache和mysql的安装设置

    Apache和mysql的安装较简单,主要是安装前请保证80端口未被占用 比如 iis 以前安装过的apache mysql 先停止运行phpmyadmin,主要是配置文件的问题,把phpMyAdmi ...

  3. Python中if &lowbar;&lowbar;name&lowbar;&lowbar; &equals;&equals; &quot&semi;&lowbar;&lowbar;main&lowbar;&lowbar;&quot&semi;&colon; 的作用

    在很多python脚本中在最后的部分会执行一个判断语句if __name__ == "__main__:",之后还可能会有一些执行语句.那添加这个判断的目的何在? 在python编 ...

  4. highcharts 柱状图动态设置数据应用实例

    <div id="container" style="min-width:700px;height:400px"></div> #jav ...

  5. 利用线程把文本文件填充到richTextBox&semi;防止导入大文本文件窗口假死现象

    private void btnDr_Click(object sender, EventArgs e) { richTextBox1.Text = ""; //richTextB ...

  6. 从HTML5规范弄清i、em、b、strong元素的区别

    为了语义化,HTML5增加了不少新标签.其中i.em和b.strong这两组标签是最容易弄混的,不好好去探究一下,还真说不清.这个也是前端面试中经常会问的问题.今天从源头上,也就是从HTML5的文档( ...

  7. docker删除已经停止的容器

    前言:docker容器已经停止运行的容器,怎么清理 1.如图:   docker   ps  -a  :显示所有运行过的docker容器 status    :  docker容器的状态 docker ...

  8. mysql error&lpar;2003&rpar; 10060的再解决

    前段时间在window虚拟机上处理过这样的问题 现在在linux上也遇到了这样的问题一项一项的排查 1.网络问题,ping的通 但是telnet (ip)  (端口号)失败,telnet(ip)都失败 ...

  9. 使用pymysql&lpar;使用一&rpar;

    创建数据表 import pymysql db = pymysql.connect("localhost","root",""," ...

  10. Python3基础 &lowbar;&lowbar;getattr&lowbar;&lowbar; 访问不存在的属性时,新增提示功能

             Python : 3.7.0          OS : Ubuntu 18.04.1 LTS         IDE : PyCharm 2018.2.4       Conda ...