java枚举类型学习
用的不多,但用的时候仅仅简单的使用,不太明白原理,今天就系统的学一下枚举。参考: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枚举类型学习的更多相关文章
- 【转载】Java枚举类型的使用
枚举类型概念 package com.lxq.enumm; public class EnumDemoOne { private enum InnerEnum { RED, GREEN, YELLOW ...
- Java枚举类型使用示例
Java枚举类型使用示例 学习了:https://www.cnblogs.com/zhaoyanjun/p/5659811.html http://blog.csdn.net/qq_27093465/ ...
- java 枚举类型分析
最近做android开发,需要用到枚举值,这样可以连续赋值,我按之前c++那样书写,如下所示: public enum ColorSelect { RED_BAGE = 0, GREEN_BAGE, ...
- 【转】java枚举类型enum的使用
原文网址:http://blog.csdn.net/wgw335363240/article/details/6359614 java 枚举类型enum 的使用 最近跟同事讨论问题的时候,突然同事提到 ...
- 【转】掌握java枚举类型(enum type)
原文网址:http://iaiai.iteye.com/blog/1843553 1 背景 在java语言中还没有引入枚举类型之前,表示枚举类型的常用模式是声明一组具有int常量.之前我们通常利用 ...
- 转载 java枚举类型enum的使用 (原文地址:http://blog.csdn.net/wgw335363240/article/details/6359614)
java枚举类型enum的使用 最近跟同事讨论问题的时候,突然同事提到我们为什么java中定义的常量值不采用enmu枚举类型,而采用public final static 类型来定义呢?以前我们都是采 ...
- Java 枚举类型简介
目录 Java 枚举示例 Java 枚举构造函数 枚举类型是用于定义常量集合的特殊类型,更确切的说,JAVA枚举类型是一种特殊的 java 类.枚举类型可以包含常量.方法等.在 java5 中添加了 ...
- 深入理解Java枚举类型(enum)
https://blog.csdn.net/javazejian/article/details/71333103 深入理解Java类型信息(Class对象)与反射机制 深入理解Java枚举类型(en ...
- Java枚举类型的使用,数值的二进制表示
一.Java枚举类型的使用 首先请看这段代码: package java上课; public class EnumTest { public static void main(String[] arg ...
随机推荐
- [PDO绑定参数]使用PHP的PDO扩展进行批量更新操作
最近有一个批量更新数据库表中某几个字段的需求,在做这个需求的时候,使用了PDO做参数绑定,其中遇到了一个坑. 方案选择 笔者已知的做批量更新有以下几种方案: 1.逐条更新 这种是最简单的方案,但无疑也 ...
- ENode框架Conference案例分析系列之 - 架构设计
Conference架构概述 先贴一下Conference案例的在线地址,UI因为完全拿了微软的实现,所以都是英文的,以后我有空再改为中文的. Conference后台会议管理:http://www. ...
- Linux 服务器监控
200 ? "200px" : this.width)!important;} --> 标签:iostat/free/top/dstat 概述 文字主要讲述使用linux自带 ...
- golang reflect
golang reflect go语言中reflect反射机制.详细原文:地址 接口值到反射对象 package main import ( "fmt" "reflect ...
- GitHub的使用记录
前言: 该贴为笔者在使用GItHub中的一些使用注意,及Git的基本命令,会一直记录笔者在使用GitHub中可能产生的错误及解决方法(会一直更新中),待一些Git初使用者参考,如果有说明不详细或不对的 ...
- SQL 2014 AlwaysOn 搭建
AlwaysOn底层依然采用Windows 故障转移群集的机制进行监测和转移,因此也需要先建立Windows Cluster,只不过可用性组中的数据库不一定非要再存放在共享存储上了.可以是存储在本地磁 ...
- Drupal8重命名上传的中文名文件
完整的模块代码文件在Coding.net上,想直接使用的请前往下载:https://coding.net/u/yamus/p/chinese_rename/git/tree/master 最近吧Dru ...
- WPF入门教程系列二十——ListView示例(二)
第四步.WPF后台逻辑代码编写 在后台用Entity Framework 6.1的Code First方式获取数据库中的数据.同时,在“刷新”按钮的方法中进行数据绑定.操作步骤如下: 1) 在“刷新 ...
- 前端MVC框架Backbone 1.1.0源码分析系列
Backbone.js 是一个在JavaScript环境下的 模型-视图-控制器 (MVC) 框架.任何接触较大规模项目的开发人员一定会苦恼于各种琐碎的事件回调逻辑.以及金字塔般的代码.而且,在传统的 ...
- 详解 ML2 Core Plugin(II) - 每天5分钟玩转 OpenStack(72)
上一节我们讨论了 ML2 Plugin 解决的问题,本节将继续研究 ML2 的架构. ML2 对二层网络进行抽象和建模,引入了 type driver 和 mechansim driver. 这两类 ...