用的不多,但用的时候仅仅简单的使用,不太明白原理,今天就系统的学一下枚举。参考: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. iPhone6/6 Plus兩款大屏智能機

    蘋果終於順應時代潮流,於今年推出了iPhone6/6 Plus兩款大屏智能機.但很快就有人開始懷念老款iPhone的“一手掌控”,畢竟不是所有人都有一雙大手.不過近期就有傳言稱,蘋果將於明年重新推出一 ...

  2. OC整理1

    关于面向对象 看起来简单,好好体会其实有点深奥,初学的时候更感觉抽象,可能由于开发经验不足的关系吧,很难体会到面向对象的精髓. 放轻松,我们来用最直白得方式来探讨一下面向对象. 首先,编程是为了用计算 ...

  3. jvm的内存分配总结

    最近看了周志明版本的<深入理解Java虚拟机>第一版和第二版,写的很好,收获很多,此处总结一下.   jvm中内存划分:   如上图,一共分为五块,其中: 线程共享区域为: 1.java堆 ...

  4. wampserver解决“不能切换在线”及运行“404问题”

    初次安装使用wampserver2.2,由于各个电脑安装的应用或是电脑型号不一样会出现以下问题: 1.安装后,不能出切换“服务器在线”或是“服务器离线” 2.设置站点后,运行编写好的代码出现404错误 ...

  5. 登陆数据库,界面一直保持正在登陆的状态,oracle使用界面无法登陆

    原因:关机时没有关闭oracle窗口. 导致在登陆数据库的时候,使用oracle的这个界面登陆时,界面一直保持''正在登陆''的状态,一旦点击就会卡住,使界面变得无法响应. 然后使用sqlplus仍然 ...

  6. HTML5- Canvas入门(七)

    这是本系列的最后一篇入门文章,主要是对剩余的未说明的canvas方法来逐个介绍. 首先,如果你是一名擅长矢量设计的设计师,对Illustrator或者Fireworks很熟悉的话,那你肯定知道它们有一 ...

  7. NodeJS入门(四)—— path对象

    很快Node就会迎来4.0的时代,届时将并入现有的iojs,所以先前写过的iojs入门系列直接更名为NodeJS入门. 本篇开始将逐个介绍Node的各主要模块,依循API文档走一遍,但会给出比API文 ...

  8. Unity3D游戏开发初探—4.开发一个“疯狂击箱子”游戏

    一.预备知识—对象的”生“与”死“ (1)如何在游戏脚本程序中创建对象而不是一开始就创建好对象?->使用GameObject的静态方法:CreatePrimitive() 以上一篇的博文中的“指 ...

  9. 推荐:图片轮播插件Nivo Slider

          因为项目需要一款切换样式多一些的轮播插件,不经意找到了NivoSlider,非常好用,比bootstrap要好用,而且样式丰富.值得注意的是,这款插件是在MIT协议下免费的.        ...

  10. 学习RBAC 用户·角色·权限·表