用的不多,但用的时候仅仅简单的使用,不太明白原理,今天就系统的学一下枚举。参考:java编程思想。

Update:

  • 枚举可以当做数据字典来存储,通常只要一个字段即instance本身,toString()或者name()打印的string。
  • 枚举的数据都是一个实例对象,比如 enum Test{A}中A就是一个对象,A的toString和name()的结果是“A”。而如果一个字符串为"A",可以转为对应的枚举实例:Test.valueOf("A")

1.简单创建

枚举就是一个固定的集合,内容是声明的类。

package com.test.java.tenum;

/**
* 一个简单的enum实例
* Created by Administrator on 2016/3/30.
*/
public enum SimpleEnumUse {
NOT,MILD,MEDIUM,HOT,FLAMING
} class TestSE{
public static void main(String[] args) {
SimpleEnumUse medium = SimpleEnumUse.MEDIUM;
System.out.println(medium);
System.out.println(medium.ordinal());
}
}

创建enum的时候,里面的元素就是类,默认创建了toString(),odinal()方法以及value字段。其中odinal是声明的顺序(从0开始),value就是声明的名字。简单的使用的话,这样就可以了,配合switch即可。

2.深入了解

2.1基本特性

创建enum时,编译器会为你生成一个相关的类,这个类继承自java.lang.Enum。下面实例:

package com.test.java.tenum;

/**
* 了解enum特性
* Created by Administrator on 2016/3/30.
*/
public class EnumClass {
public static void main(String[] args) {
for (Shrubbery s : Shrubbery.values()) {
//s继承Enum
//ordinal表示声明的顺序
System.out.print(s+" ordinal:"+s.ordinal());
//enum自动实现了Comparable接口
System.out.println(" compareTo:"+s.compareTo(Shrubbery.CRAWING));
//编译器提供了equals和hashCode
System.out.println(s.equals(Shrubbery.CRAWING));
System.out.println(s==Shrubbery.CRAWING);
System.out.println(s.getDeclaringClass());
System.out.println(s.name());
System.out.println("======================");
}
for (String s :
"HANGING CRAWING GROUND".split(" ")) {
Shrubbery shrubbery = Enum.valueOf(Shrubbery.class, s);
System.out.println(shrubbery); }
}
} enum Shrubbery{
GROUND,CRAWING,HANGING
}

ordinal()方法返回一个int值,这个每个enum实例在声明时的次序,从0开始。可以使用==比较enum实例,编译器自动为你提供了equals()和hashCode()方法。Enum类实现了Comparable接口和Serializable接口。

2.1.1静态导入

在使用enum的实例的时候发现人家不是EnumClass.INSTANCE,即不是通过类名调用的,而是直接调用,看了下。原来是在包下静态导入了。即:

import static package.EnumClass.*。

究竟显示的修饰enum实例还是静态导入要看代码的复杂度,看看静态导入是不是会让代码难以理解。

2.2可以在enum中添加自己的方法

enum可以添加方法和构造器。

package com.test.java.tenum;

/**
* 可以在enum中添加方法和构造器
* Created by Administrator on 2016/3/30.
*/
public enum OzWitch {
WEST("Miss Gulch, aka the Wiched Witch of the West"),
NORTH("Glinda,the Good Witch of the North"),
EAST("Wicked Witch of the East,wearer of the Ruby Slippers,crushed by Dorothy's house"),
SOUTH("Good by inference,but missing")
; private String description;
private OzWitch(String description){
this.description = description;
}
public String getDescription(){
return description;
} public static void main(String[] args) {
for (OzWitch with :
OzWitch.values()) {
System.out.println(with+":"+with.getDescription());
}
}
}

通常,将enum的构造方法声明private,而实际上对于它的可访问性来说没有什么变化,因为即使不是private也只能在enum内部使用创建enum实例。一旦enum定义结束,编译器就不允许我们再使用其构造器来创建任何实例了。另外,必须优先声明实例,然后声明方法或属性,否则编译报错

2.3探究values()

经过一下检测,values是编译器添加到你创建的enum类的,而Enum类本身中并没有values方法。我们可以通过Class对象获取所有的enum实例。

package com.test.java.tenum;

import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Set;
import java.util.TreeSet; /**
* 通过反射查看enum的values
* 结果:values是编译器添加到enum类的,而Enum类本身没有values方法
* Created by Administrator on 2016/3/30.
*/
public class Reflection {
public static Set<String> analyze(Class<?> enumClass){
System.out.println("-----------Analyzing "+enumClass+"-------");
System.out.println("Interfaces:");
for (Type t :
enumClass.getGenericInterfaces()) {
System.out.println(t);
}
System.out.println("Base: "+enumClass.getSuperclass());
System.out.println("Method:");
Set<String> methods = new TreeSet<>();
for (Method m :
enumClass.getMethods()) {
methods.add(m.getName());
}
System.out.println(methods);
return methods;
}
public static void main(String[] args){
Set<String> exploreMethods = analyze(Explore.class);
Set<String> enumMethods = analyze(Enum.class);
System.out.println("Explore.containsAll(Enum)?"+enumMethods.containsAll(enumMethods));
System.out.println("Explore.removeAll(Enum):");
exploreMethods.removeAll(enumMethods);
System.out.println(exploreMethods); }
}
enum Explore{
HERE,THERE
}
/************************通过class获取实例********************************/
enum Search{
HITHER,YON
}
class UpcastEnum{
public static void main(String[] args) {
Search[] values = Search.values();
Enum e = Search.HITHER;
// e.values();//编译报错,说明No values() in Enum
for (Enum anEnum : e.getClass().getEnumConstants()) {
System.out.println(anEnum);
}
}
} class NonEnum{
public static void main(String[] args) {
Class<Integer> integerClass = Integer.class;
try{
for (Object en : integerClass.getEnumConstants()) {
System.out.println(en);
}
}catch (Exception e){
System.out.println(e);
}
}
}

2.4通过实现接口而不是继承他类来扩展

应为所有enum类都继承java.lang.Enum类。由于java不支持多继承,所以你的enum不能再继承其他类。但是可以实现多个接口。

2.5随机选取

package com.test.java.tenum;

import java.util.Random;

/**
* 随机抽取的工具类
* Created by Administrator on 2016/3/30.
*/
public class Enums {
private static Random random = new Random(47);
public static <T extends Enum<T>> T random(Class<T> ec){
return random(ec.getEnumConstants());
}
public static <T> T random(T[] values){
return values[random.nextInt(values.length)];
}
}

2.6使用接口组织枚举

无法从enum继承子类有时很令人沮丧。这种需求有时源自我们希望扩展原enum中的元素,有时我们希望使用子类将一个enum中的元素进行分组。

在一个接口内部创建实现该接口的枚举,以此将元素进行分组,可以达到将枚举元素分类的目的。举例来说,假设想用enum表示不同的食物,同时还希望每个enum元素仍然保持Food类型。可以这样:

package com.test.java.tenum;

/**
* 使用接口组织枚举
* Created by Administrator on 2016/3/30.
*/
public interface Food {
//开胃食物
enum Appetizer implements Food{
SALAD,SOUP,SPRING_ROLLS;
}
//主菜
enum MainCourse implements Food{
LASNGE,BURRITO,PAD_THAI,LENTILS,HUMMOUS,VINDALOO;
}
//甜点
enum Dessert implements Food{
TIRAMISU,GELATO,BLACK_FOREST_CAKE,FRUIT,CREME_CARMEL;
}
//coffe
enum Coffee implements Food{
BLACK_COFFEE,DECAF_COFFEE,ESPRESSO,LATTE,CAPPUCCINO,TEA,HERB_TEA;
}
//....
}
class TypeOfFood{
public static void main(String[] args) {
Food food = Food.Appetizer.SALAD;
food = Food.MainCourse.PAD_THAI;
} }

如果enum类型实现了Food接口,那么我们就可以将其实例向上转型为Food,所以上例中的所有东西都是Food。

然而当你需要与一大堆类型打交道时,接口就不如enum好用。例如,你想创建一个枚举的枚举。那么可以创建一个新的enum,然后用其实例包装Food中的每一个enum类。

package com.test.java.tenum;

/**
* 枚举的枚举
* Created by Administrator on 2016/3/30.
*/
public enum Course {
APPETIZER(Food.Appetizer.class),
MAINCOURSE(Food.MainCourse.class); private Food[] values;
private Course(Class<? extends Food> kind){
values = kind.getEnumConstants();
}
public Food randomSelection(){
return Enums.random(values);
}
}

在上面的程序中,每个Course实例都将其对应的Class对象作为构造器的参数。通过getEnumConstants方法,可以从该Class对象中取得某个Food子类的所有enum实例。因此,我们通过随机可以生成一份菜单:

class Meal{
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
for (Course course : Course.values()) {
Food food = course.randomSelection();
System.out.println(food);
}
System.out.println("-------");
}
}
}

在这个例子中,我们通过遍历每个Course实例来获得枚举的枚举的值。此外,还可以更简洁:将一个enum嵌套在另一个enum内。

package com.test.java.tenum;

/**
* 枚举嵌套
* Created by Administrator on 2016/3/30.
*/
public enum SecurityCategory {
STOCK(Security.Stock.class),
BOND(Security.Bond.class); Security[] values;
SecurityCategory(Class<? extends Security> kind){
values = kind.getEnumConstants();
}
interface Security{
enum Stock implements Security{SHORT,LONG,MARGIN}
enum Bond implements Security{MUNICIPAL,JUNK}
}
public Security randomSelection(){
return Enums.random(values);
} public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
SecurityCategory random = Enums.random(SecurityCategory.class);
System.out.println(random+":"+random.randomSelection());
}
}
}

Security接口的作用是将其所包含的enum组成一个公共类型,这一点是必要的。然后SecurityCategory才能将Security中的enum作为其构造器的参数作用,以起到组织的效果。

如果我们将这种方式应用于Food实例,结果是这样:

/**
* 同理,food
*/
enum Meal2{
APPETIZER(Food.Appetizer.class),
MAINCOURSE(Food.MainCourse.class),
DESSERT(Food.Dessert.class); private Food[] values;
private Meal2(Class<? extends Food> kind){
values = kind.getEnumConstants();
}
public interface Food{
//开胃食物
enum Appetizer implements Food{
SALAD,SOUP,SPRING_ROLLS;
}
//主菜
enum MainCourse implements Food{
LASNGE,BURRITO,PAD_THAI,LENTILS,HUMMOUS,VINDALOO;
}
//甜点
enum Dessert implements Food{
TIRAMISU,GELATO,BLACK_FOREST_CAKE,FRUIT,CREME_CARMEL;
}
//coffe
enum Coffee implements Food{
BLACK_COFFEE,DECAF_COFFEE,ESPRESSO,LATTE,CAPPUCCINO,TEA,HERB_TEA;
}
//.... } public Food randomSelection(){
return Enums.random(values);
} public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
for (Meal2 meal2 : Meal2.values()) {
Food food = meal2.randomSelection();
System.out.println(food);
}
System.out.println("----------------------------");
}
} }

这仅仅是重新组织了下代码,不过多数情况下,这种方式使你的代码具有更清晰的结构。

2.7使用EnumSet代替标识

java se5引入EnumSet,是为了通过enum创建一种替代品,以替代传统的基于int的“标志位”。这种标识可以用来表示某种“开/关”信息。

下面enum表示在一座大楼中,警报传感器的安防位置:

package com.test.java.tenum;

import java.util.EnumSet;

/**
* 酱爆传感器位置
* Created by Administrator on 2016/3/31.
*/
public enum AlarmPoints {
STAR1,STAR2,LOBBY,OFFICE1,OFFICE2,OFFICE3,OFFICE4,BATHROOM,UTILITY,KITCHEN
} /**
* 我们使用enumSet来跟踪警报器的状态
*/
class EnumSets{
public static void main(String[] args) {
EnumSet<AlarmPoints> points = EnumSet.noneOf(AlarmPoints.class);//Empty set
points.add(AlarmPoints.BATHROOM);
System.out.println(points);
//添加多个
points.addAll(EnumSet.of(AlarmPoints.STAR1,AlarmPoints.STAR2,AlarmPoints.KITCHEN,AlarmPoints.OFFICE3));
System.out.println(points);
//去除office1到office4范围内的
points.removeAll(EnumSet.range(AlarmPoints.OFFICE1,AlarmPoints.OFFICE4));
System.out.println(points);
//其他的
points = EnumSet.complementOf(points);
System.out.println(points);
}
}

2.8 使用EnumMap

EnumMap是一种特殊的Map,它要求其中的key必须来自一个enum。由于enum本身的限制,所以EnumMap在内部可由数组实现。因此EnumMap速度很快。

下面演示了命令设计模式的用法。一般来说,命令模式首先需要一个只有单一方法的接口,然后从该接口实现具有各自不同的行为的多个子类。接下来,程序员就可以构造命令对象,并在需要的时候使用它们。

package com.test.java.tenum;

import java.util.EnumMap;
import java.util.Map; /** * Created by Administrator on 2016/3/31.
*/
public class EnumMaps {
public static void main(String[] args){
EnumMap<AlarmPoints,Command> em = new EnumMap<AlarmPoints, Command>(AlarmPoints.class);
em.put(AlarmPoints.KITCHEN, new Command() {
@Override
public void action() {
System.out.println("kitchen fire!");
}
});
em.put(AlarmPoints.BATHROOM, new Command() {
@Override
public void action() {
System.out.println("bathroom alert!");
}
});
for (Map.Entry<AlarmPoints, Command> entry : em.entrySet()) {
System.out.println(entry.getKey()+":");
entry.getValue().action();
}
try {
em.get(AlarmPoints.UTILITY).action();
}catch (Exception e){
System.out.println(e);
}
}
}
interface Command{
void action();
}

与EnumSet一样,enum实例定义时的次序决定了其在EnumMap中的顺序。enum的每个实例作为一个键总是存在的,如果你没有为这个键调用put来存入相应的值,则对应的值为null。

与常量相关的方法相比,EnumMap有一个有点,允许程序员改变值对象,而常量相关的方法在编译器就被固定了。

2.9常量相关方法

java的enum有个特性:允许程序员为enum实例编写方法,从而为每个enum实例赋予各自不同的行为。要实现常量相关的方法,你需要为enum定义一个或者多个abstract方法,然后为每个enum实例实现该抽象方法。如下:

package com.test.java.tenum;

import java.text.DateFormat;
import java.util.Date; /**
* Created by Administrator on 2016/3/31.
*/
public enum ConstantspecificMethod {
DATE_TIME{
String getInfo(){
return DateFormat.getDateInstance().format(new Date());
}
},
CLASSPATH{
String getInfo(){
return System.getenv("CLASSPATH");
}
},
VERSION{
String getInfo(){
return System.getProperty("java.version");
}
};
abstract String getInfo(); public static void main(String[] args) {
for (ConstantspecificMethod csm : values()) {
System.out.println(csm.getInfo());
}
}
}

通过相应的enum实例,我们可以调用其上的方法。这通常也成为表驱动的代码(table-driven code),请注意它与前面提到的命令模式的相似之处。

在面向对象的程序设计中,不同的行为与不同的类关联。而通过常量相关的方法,每个enum实例可以具备自己独特的行为,这似乎说明每个enum实例就是一个特殊的类。我们并不能真的将enum实例当做一个类来使用。

与使用匿名内部类相比较,定义常量相关方法的语法更高效和简洁,下面是一个有趣的洗车的例子:

java枚举类型学习的更多相关文章

  1. 【转载】Java枚举类型的使用

    枚举类型概念 package com.lxq.enumm; public class EnumDemoOne { private enum InnerEnum { RED, GREEN, YELLOW ...

  2. Java枚举类型使用示例

    Java枚举类型使用示例 学习了:https://www.cnblogs.com/zhaoyanjun/p/5659811.html http://blog.csdn.net/qq_27093465/ ...

  3. java 枚举类型分析

    最近做android开发,需要用到枚举值,这样可以连续赋值,我按之前c++那样书写,如下所示: public enum ColorSelect { RED_BAGE = 0, GREEN_BAGE, ...

  4. 【转】java枚举类型enum的使用

    原文网址:http://blog.csdn.net/wgw335363240/article/details/6359614 java 枚举类型enum 的使用 最近跟同事讨论问题的时候,突然同事提到 ...

  5. 【转】掌握java枚举类型(enum type)

    原文网址:http://iaiai.iteye.com/blog/1843553 1   背景 在java语言中还没有引入枚举类型之前,表示枚举类型的常用模式是声明一组具有int常量.之前我们通常利用 ...

  6. 转载 java枚举类型enum的使用 (原文地址:http://blog.csdn.net/wgw335363240/article/details/6359614)

    java枚举类型enum的使用 最近跟同事讨论问题的时候,突然同事提到我们为什么java中定义的常量值不采用enmu枚举类型,而采用public final static 类型来定义呢?以前我们都是采 ...

  7. Java 枚举类型简介

    目录 Java 枚举示例 Java 枚举构造函数 枚举类型是用于定义常量集合的特殊类型,更确切的说,JAVA枚举类型是一种特殊的 java 类.枚举类型可以包含常量.方法等.在 java5 中添加了 ...

  8. 深入理解Java枚举类型(enum)

    https://blog.csdn.net/javazejian/article/details/71333103 深入理解Java类型信息(Class对象)与反射机制 深入理解Java枚举类型(en ...

  9. Java枚举类型的使用,数值的二进制表示

    一.Java枚举类型的使用 首先请看这段代码: package java上课; public class EnumTest { public static void main(String[] arg ...

随机推荐

  1. 简单正则匹配QQ邮箱

    <!DOCTYPE html><html> <head> <meta charset="UTF-8"> <script src ...

  2. 实践Html5的上传文件

    技术点: 1.通过input的change事件获取文件信息: onchange = function() { this.files } 这个files属性是htmlInputElement接口的属性, ...

  3. PHP的FastCGI

    CGI全称是“通用网关接口”(Common Gateway Interface), 它可以让一个客户端,从网页浏览器向执行在Web服务器上的程序请求数据. CGI描述了客户端和这个程序之间传输数据的一 ...

  4. Kotlin笔记

    官网: http://kotlinlang.org/ http://kotlinlang.org/docs/reference/ 中文教程: http://kotlindoc.com/ Gradle: ...

  5. 玩转JavaScript OOP[3]——彻底理解继承和原型链

    概述 上一篇我们介绍了通过构造函数和原型可以实现JavaScript中的“类”,由于构造函数和函数的原型都是对象,所以JavaScript的“类”本质上也是对象.这一篇我们将介绍JavaScript中 ...

  6. Mina入门实例(一)

    mina现在用的很多了,之前也有用到,但是毕竟不熟悉,于是查了一些资料,做了一些总结.看代码是最直观的,比什么长篇大论都要好.不过其中重要的理论,也要理解下. 首先是环境,程序运行需要几个包,这里用m ...

  7. [.net 面向对象程序设计进阶] (21) 反射(Reflection)(下)设计模式中利用反射解耦

    [.net 面向对象程序设计进阶] (21) 反射(Reflection)(下)设计模式中利用反射解耦 本节导读:上篇文章简单介绍了.NET面向对象中一个重要的技术反射的基本应用,它可以让我们动态的调 ...

  8. 企业IT管理员IE11升级指南【2】—— Internet Explorer 11 对Adobe Flash的支持

    企业IT管理员IE11升级指南 系列: [1]—— Internet Explorer 11增强保护模式 (EPM) 介绍 [2]—— Internet Explorer 11 对Adobe Flas ...

  9. MySQL 变量和条件

    概述 变量在存储过程中会经常被使用,变量的使用方法是一个重要的知识点,特别是在定义条件这块比较重要. mysql版本:5.6 变量定义和赋值 #创建数据库 DROP DATABASE IF EXIST ...

  10. C#设计模式之装饰者

    IronMan之装饰者 前言 上一篇的文章我们讲到要给"IronMan"配备"武器",并且还使用了"武器",效果还是不错的,对于多种环境.多 ...