0

0

Java枚举的使用详解

黄舟

黄舟

发布时间:2017-09-09 11:23:18

|

2100人浏览过

|

来源于php中文网

原创

这篇文章主要介绍了 java枚举的使用方法详解的相关资料,希望通过本文大家能掌握枚举的使用方法,需要的朋友可以参考下

 Java枚举的使用方法详解

前言  你代码中的flag和status,都应该用枚举来替代

很多人都说,枚举在实际开发中很少用到,甚至就没用到。因为,他们的代码往往是这样子的:


public class Constant { 
  /* 
   * 以下几个变量表示英雄的状态 
   */ 
  public final static int STATUS_WALKING = 0;//走  
  public final static int STATUS_RUNNINGING = 1;//跑 
  public final static int STATUS_ATTACKING = 2;//攻击  
  public final static int STATUS_DEFENDING = 3;//防御  
  public final static int STATUS_DEAD = 4;//挂了  
   
  /* 
   * 以下几个变量表示英雄的等级 
   */ 
  //此处略去N行代码 
}

然后,他们是这样使用这个类的:

立即学习Java免费学习笔记(深入)”;


hero.setStatus(Contant.STATUS_ATTACKING);

嗯,然后他们就说,“我在实际开发中很少用到枚举”

当然,他们的意思是说很少用到枚举Enum这个类。

但是,我想说的是,上面这些代码,通通应该用Enum去实现。

为什么?

因为他们的代码完全建立在对队友的信任,假设来了个奇葩队友,做了这件事:


hero.setStatus(666);

你说,屏幕上的英雄会怎么样呢?

总之,假如你在实际编程中经常使用这样的代码,那是时候好好学习一下Enum了。

枚举初探   为什么要使用枚举类型

生活中处处都有枚举,包括“天然的枚举”,比如行星、一周的天数,也包括我们设计出来的枚举,比如csdn的tab标签,菜单等。

Java代码中表示枚举的方式,大体上有两种,一是int枚举,而是Enum枚举,当然,我们都知道,Enum枚举才是Java提供的真正枚举。

那么,为什么我们要使用Enum枚举类型呢?先来看看在Java 1.5之前,没有枚举类型时,我们是怎样表示枚举的。

以八大行星为例,每个行星对应一个int值,我们大概会这样写


public class PlanetWithoutEnum { 
  public static final int PLANET_MERCURY = 0; 
  public static final int PLANET_VENUS = 1; 
  public static final int PLANET_EARTH = 2; 
  public static final int PLANET_MARS = 3; 
  public static final int PLANET_JUPITER = 4; 
  public static final int PLANET_SATURN = 5; 
  public static final int PLANET_URANUS = 6; 
  public static final int PLANET_NEPTUNE = 7; 
}

这种叫int枚举模式,当然你也可以使用String枚举模式,无论采用何种方式,这样的做法,在类型安全和使用方便性上都很差。
如果变量planet表示一个行星,使用者可以给这个值赋与一个不在我们枚举值里面的值,比如 planet = 9,这是哪个行星估计也只有天知道了; 再者,我们很难计算出到底有多少个行星,我们也很难对行星进行遍历操作等等。

现在我们用枚举来创建我们的行星。


public enum Planet { 
  MERCURY, VENUS, EARTH, MARS, JUPITER, SATURN, URANUS, NEPTUNE; 
}

上面这个是最简单的枚举,我们姑且叫做Planet 1.0,这个版本的行星枚举,我们实现了一个功能,就是任何一个Planet类型的变量,都可以由编译器来保证,传到给参数的任何非null对象一定属于这八个行星之一。

然后,我们对Planet进行升级,Java允许我们给枚举类型添加任意的方法,这里引言书中的代码,大家自行体会一下枚举的构造器、公共方法、枚举遍历等知识点。


public enum Planet { 
  MERCURY(3.302e+23, 2.439e6),  
  VENUS(4.869e+24, 6.052e6),  
  EARTH(5.975e+24,6.378e6),  
  MARS(6.419e+23, 3.393e6),  
  JUPITER(1.899e+27, 7.149e7),  
  SATURN(5.685e+26, 6.027e7),  
  URANUS(8.683e+25, 2.556e7),  
  NEPTUNE(1.024e+26,2.477e7); 
  private final double mass; // In kilograms 
  private final double radius; // In meters 
  private final double surfaceGravity; // In m / s^2 
 
  // Universal gravitational constant in m^3 / kg s^2 
  private static final double G = 6.67300E-11; 
 
  // Constructor 
  Planet(double mass, double radius) { 
    this.mass = mass; 
    this.radius = radius; 
    surfaceGravity = G * mass / (radius * radius); 
  } 
 
  public double mass() { 
    return mass; 
  } 
 
  public double radius() { 
    return radius; 
  } 
 
  public double surfaceGravity() { 
    return surfaceGravity; 
  } 
 
  public double surfaceWeight(double mass) { 
    return mass * surfaceGravity; // F = ma 
  } 
}


//注:这里对书中的代码做了微调 
public class WeightTable { 
  public static void main(String[] args) { 
    printfWeightOnAllPlanets(8d); 
  } 
 
  public static void printfWeightOnAllPlanets(double earthWeight) { 
    double mass = earthWeight / Planet.EARTH.surfaceGravity(); 
    for (Planet p : Planet.values()) 
      System.out.printf("Weight on %s is %f%n", p, p.surfaceWeight(mass)); 
  } 
}

运行WeightTable,打印结果如下:


Weight on MERCURY is 3.023254
Weight on VENUS is 7.240408
Weight on EARTH is 8.000000
Weight on MARS is 3.036832
Weight on JUPITER is 20.237436
Weight on SATURN is 8.524113
Weight on URANUS is 7.238844
Weight on NEPTUNE is 9.090108

在这个小程序里,我们用到了枚举的values()方法,这个方法返回了枚举类型里的枚举变量的集合,非常实用。

枚举进阶   计算器运算符枚举类

上一小节的例子里,我们用到了枚举类的公共方法,这一节,我们以计算器运算符 Operation 枚举类为例,看看怎么实现对于

每一个枚举对象,执行不同的操作。

首先,我们很容易想到的一个方法,在公共方法里,使用switch去判断枚举类型,然后执行不同的操作,代码如下:

jspgou网店系统
jspgou网店系统

JSPGOU一直以来都和jeecms、jeebbs一样,是收费软件,但是从2014年7月12日开始,开始jspgou发布第一个免费版,金磊科技承诺:永远不向使用jspgou免费版用户索取任何费用,为免费用户提供更好的技术支持服务,根据用户提出的完善建议快速完善jspgou系统。 jspgou系统使命:做中国最优秀的免费网店系统,让更多的用户了解和使用java产品。 jspgou免费版适用对象:不限

下载


public enum OperationUseSwitch { 
  PLUS, MINUS, TIMES, pIDE; 
 
  double apply(double x, double y) { 
    switch (this) { 
      case PLUS: 
        return x + y; 
      case MINUS: 
        return x + y; 
      case TIMES: 
        return x + y; 
      case pIDE: 
        return x + y; 
    } 
    // 如果this不属于上面四种操作符,抛出异常 
    throw new AssertionError("Unknown operation: " + this); 
  } 
}

这段代码确实实现了我们的需求,但是有两个弊端。

首先是我们不得不在最后抛出异常或者在switch里加上default,不然无法编译通过,但是很明显,程序的分支是不会进入异常或者default的。

其次,这段代码非常脆弱,如果我们添加了新的操作类型,却忘了在switch里添加相应的处理逻辑,执行新的运算操作时,就会出现问题。

还好,Java枚举提供了一种功能,叫做 特定于常量的方法实现。

我们只需要在枚举类型中声明一个抽象方法,然后在各个枚举常量中去覆盖这个方法,实现如下:


public enum Operation { 
  PLUS { 
    double apply(double x, double y) { 
      return x + y; 
    } 
  }, 
  MINUS { 
    double apply(double x, double y) { 
      return x - y; 
    } 
  }, 
  TIMES { 
    double apply(double x, double y) { 
      return x * y; 
    } 
  }, 
  pIDE { 
    double apply(double x, double y) { 
      return x / y; 
    } 
  }; 
 
  abstract double apply(double x, double y); 
}

这样,也就再也不会出现添加新操作符后忘记添加对应的处理逻辑的情况了,因为编译器就会提示我们必须覆盖apply方法。

不过,这种 特定于常量的方法实现 有一个缺点,那就是你很难在枚举常量之间共享代码。

我们以星期X的枚举为例,周一到周五是工作日,执行一种逻辑,周六周日,休息日,执行另一种逻辑。

如果还是使用 特定于常量的方法实现,写出来的代码可能就是这样的:


public enum DayUseAbstractMethod { 
  MONDAY { 
    @Override 
    void apply() { 
      dealWithWeekDays();//伪代码 
    } 
  }, 
  TUESDAY { 
    @Override 
    void apply() { 
      dealWithWeekDays();//伪代码 
    } 
  }, 
  WEDNESDAY { 
    @Override 
    void apply() { 
      dealWithWeekDays();//伪代码 
    } 
  }, 
  THURSDAY { 
    @Override 
    void apply() { 
      dealWithWeekDays();//伪代码 
    } 
  }, 
  FRIDAY { 
    @Override 
    void apply() { 
      dealWithWeekDays();//伪代码 
    } 
  }, 
  SATURDAY { 
    @Override 
    void apply() { 
      dealWithWeekEnds();//伪代码 
    } 
  }, 
  SUNDAY { 
    @Override 
    void apply() { 
      dealWithWeekEnds();//伪代码 
    } 
  }; 
 
  abstract void apply(); 
}

很明显,我们这段代码里面有相当多的重复代码。

那么要怎么优化呢,我们不妨这样想,星期一星期二等等是一种枚举,那么工作日和休息日,难道不也是一种枚举吗,我们能不能给Day的构造函数传入一个工作日休息日的DayType枚举呢?这也就是书中给出的一种叫策略枚举 的方法,代码如下:


public enum Day { 
  MONDAY(DayType.WEEKDAY), TUESDAY(DayType.WEEKDAY), WEDNESDAY( 
      DayType.WEEKDAY), THURSDAY(DayType.WEEKDAY), FRIDAY(DayType.WEEKDAY), SATURDAY( 
      DayType.WEEKDAY), SUNDAY(DayType.WEEKDAY); 
  private final DayType dayType; 
 
  Day(DayType daytype) { 
    this.dayType = daytype; 
  } 
 
  void apply() { 
    dayType.apply(); 
  } 
 
  private enum DayType { 
    WEEKDAY { 
      @Override 
      void apply() { 
        System.out.println("hi, weekday"); 
      } 
    }, 
    WEEKEND { 
      @Override 
      void apply() { 
        System.out.println("hi, weekend"); 
      } 
    }; 
    abstract void apply(); 
  } 
}

通过策略枚举的方式,我们把Day的处理逻辑委托给了DayType,个中奥妙,读者可以细细体会。

枚举集合   EnumSet的使用

EnumSet提供了非常方便的方法来创建枚举集合,下面这段代码,感受一下


public class Text { 
  public enum Style { 
    BOLD, ITALIC, UNDERLINE, STRIKETHROUGH 
  } 
 
  // Any Set could be passed in, but EnumSet is clearly best 
  public void applyStyles(Set<Style> styles) { 
    // Body goes here 
    for(Style style : styles){ 
      System.out.println(style); 
    } 
  } 
 
  // Sample use 
  public static void main(String[] args) { 
    Text text = new Text(); 
    text.applyStyles(EnumSet.of(Style.BOLD, Style.ITALIC)); 
  } 
}

这个例子里,我们使用了EnumSet.of方法,轻松创建了枚举集合。

枚举Map   EnumMap的使用

假设对于香草(Herb),有一个枚举属性Type(一年生、多年生、两年生)

Herb:


public class Herb { 
  public enum Type { ANNUAL, PERENNIAL, BIENNIAL } 
 
  private final String name; 
  private final Type type; 
   
  Herb(String name, Type type) { 
    this.name = name; 
    this.type = type; 
  } 
 
  @Override public String toString() { 
    return name; 
  } 
}

现在,假设我们有一个Herb数组,我们需要对这个Herb数组按照Type进行分类存放。

所以接下来,我们需要创建一个Map,value肯定是Herb的集合了,那么用什么作为key呢?

有的人会使用枚举类型的ordinal()方法,这个函数返回int类型,表示枚举遍历在枚举类里的位置,这样做,缺点很明显,由于你的key的类型是int,不能保证传入的int一定能和枚举类里的变量对应上。

所以,在key的选择上,毫无疑问,只能使用枚举类型,也即Herb.Type。

最后还有一个问题,要使用什么Map? Java为枚举类型专门提供了一种Map,叫EnumMap,相比较与其他Map,这种Map在处理枚举类型上更快,有兴趣的同学可以研究一下这个map的内部实现。

下面让我们看看怎么使用EnumMap:


public static void main(String[] args) { 
  Herb[] garden = { new Herb("Basil", Type.ANNUAL), 
      new Herb("Carroway", Type.BIENNIAL), 
      new Herb("Dill", Type.ANNUAL), 
      new Herb("Lavendar", Type.PERENNIAL), 
      new Herb("Parsley", Type.BIENNIAL), 
      new Herb("Rosemary", Type.PERENNIAL) }; 
 
  // Using an EnumMap to associate data with an enum - Page 162 
  Map<Herb.Type, Set<Herb>> herbsByType = new EnumMap<Herb.Type, Set<Herb>>( 
      Herb.Type.class); 
  for (Herb.Type t : Herb.Type.values()) 
    herbsByType.put(t, new HashSet<Herb>()); 
  for (Herb h : garden) 
    herbsByType.get(h.type).add(h); 
  System.out.println(herbsByType); 
 
}

总结

和int枚举相比,Enum枚举的在类型安全和使用便利上的优势是不言而喻的。
Enum为枚举提供了丰富的功能,如文章中提到的特定于常量的方法实现和策略枚举。
EnumSet和EnumMap是两个为枚举而设计的集合,在实际开发中,用到枚举集合时,请优先考虑这两个。

相关文章

java速学教程(入门到精通)
java速学教程(入门到精通)

java怎么学习?java怎么入门?java在哪学?java怎么学才快?不用担心,这里为大家提供了java速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
pixiv网页版官网登录与阅读指南_pixiv官网直达入口与在线访问方法
pixiv网页版官网登录与阅读指南_pixiv官网直达入口与在线访问方法

本专题系统整理pixiv网页版官网入口及登录访问方式,涵盖官网登录页面直达路径、在线阅读入口及快速进入方法说明,帮助用户高效找到pixiv官方网站,实现便捷、安全的网页端浏览与账号登录体验。

1044

2026.02.13

微博网页版主页入口与登录指南_官方网页端快速访问方法
微博网页版主页入口与登录指南_官方网页端快速访问方法

本专题系统整理微博网页版官方入口及网页端登录方式,涵盖首页直达地址、账号登录流程与常见访问问题说明,帮助用户快速找到微博官网主页,实现便捷、安全的网页端登录与内容浏览体验。

334

2026.02.13

Flutter跨平台开发与状态管理实战
Flutter跨平台开发与状态管理实战

本专题围绕Flutter框架展开,系统讲解跨平台UI构建原理与状态管理方案。内容涵盖Widget生命周期、路由管理、Provider与Bloc状态管理模式、网络请求封装及性能优化技巧。通过实战项目演示,帮助开发者构建流畅、可维护的跨平台移动应用。

213

2026.02.13

TypeScript工程化开发与Vite构建优化实践
TypeScript工程化开发与Vite构建优化实践

本专题面向前端开发者,深入讲解 TypeScript 类型系统与大型项目结构设计方法,并结合 Vite 构建工具优化前端工程化流程。内容包括模块化设计、类型声明管理、代码分割、热更新原理以及构建性能调优。通过完整项目示例,帮助开发者提升代码可维护性与开发效率。

35

2026.02.13

Redis高可用架构与分布式缓存实战
Redis高可用架构与分布式缓存实战

本专题围绕 Redis 在高并发系统中的应用展开,系统讲解主从复制、哨兵机制、Cluster 集群模式及数据分片原理。内容涵盖缓存穿透与雪崩解决方案、分布式锁实现、热点数据优化及持久化策略。通过真实业务场景演示,帮助开发者构建高可用、可扩展的分布式缓存系统。

111

2026.02.13

c语言 数据类型
c语言 数据类型

本专题整合了c语言数据类型相关内容,阅读专题下面的文章了解更多详细内容。

77

2026.02.12

雨课堂网页版登录入口与使用指南_官方在线教学平台访问方法
雨课堂网页版登录入口与使用指南_官方在线教学平台访问方法

本专题系统整理雨课堂网页版官方入口及在线登录方式,涵盖账号登录流程、官方直连入口及平台访问方法说明,帮助师生用户快速进入雨课堂在线教学平台,实现便捷、高效的课程学习与教学管理体验。

17

2026.02.12

豆包AI网页版入口与智能创作指南_官方在线写作与图片生成使用方法
豆包AI网页版入口与智能创作指南_官方在线写作与图片生成使用方法

本专题汇总豆包AI官方网页版入口及在线使用方式,涵盖智能写作工具、图片生成体验入口和官网登录方法,帮助用户快速直达豆包AI平台,高效完成文本创作与AI生图任务,实现便捷智能创作体验。

813

2026.02.12

PostgreSQL性能优化与索引调优实战
PostgreSQL性能优化与索引调优实战

本专题面向后端开发与数据库工程师,深入讲解 PostgreSQL 查询优化原理与索引机制。内容包括执行计划分析、常见索引类型对比、慢查询优化策略、事务隔离级别以及高并发场景下的性能调优技巧。通过实战案例解析,帮助开发者提升数据库响应速度与系统稳定性。

97

2026.02.12

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 3.8万人学习

C# 教程
C# 教程

共94课时 | 10万人学习

Java 教程
Java 教程

共578课时 | 70.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号