- 浏览: 56716 次
- 性别:
- 来自: 武汉
文章分类
最新评论
很多时候我们定义了一组值来表示用于特定的数值,往往都是习惯性地使用常量:
后来才知道原来这样会使得类型不安全,你必须确保是int,而且还要确保它的范围必须正确。
像以上的代码,一旦color定义的常量以后增加了,这里就需要相应地修改。而enum可以很方便地解决。
首先看看最简单的enum用法:
运行结果:
使用javap EnumMonth$Month看到反编译的结果,Month会被编译成一个Java类:
1. 这个Month枚举类是final class,即不能被继承,而它本身是继承于Enum;
2. 这些枚举值是Month对象,而且是static final修饰的;
3. 注意values()方法,它能得到所有的枚举值,但这方法在Enum里面没有找到,我是在看到反编译结果后才确认的。
而valueOf(String)方法,返回带指定名称的指定枚举类型的枚举常量。在使用javap -c EnumMonth$Month进一步查看汇编代码后,知道原来是调用Enum.valueOf(Class<T> enumType, String name):
public static EnumMonth$Month valueOf(java.lang.String);
Code:
0: ldc_w #4; //class EnumMonth$Month
3: aload_0
4: invokestatic #5; //Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
7: checkcast #4; //class EnumMonth$Month
10: areturn
static{}里面主要是new这些枚举值对象。
0: new #4; //class EnumMonth$Month
3: dup
4: ldc #7; //String JAN
6: iconst_0
7: invokespecial #8; //Method "<init>":(Ljava/lang/String;I)V
10: putstatic #9; //Field JAN:LEnumMonth$Month;
这里要说说Enum的构造函数Enum(String name, int ordinal),形参name就是枚举值的string形式("JAN"),ordinal值则是编译器按照枚举值的顺序从0排着赋值的。要注意,这个ordinal值原来是不能改变的,即编译器是从0升序赋值的,在字节码里面固定写着iconst_0(n=0,1,2...)的。
static{
JAN = new EnumMonth$Month("JAN", 0);
SEP = new EnumMonth$Month("SEP", 1);
...
}
于是Enum里面的String name()和int ordinal()方法就是返回这两个值的。Enum.toString()也是返回name值,这就是为什么System.out.println(month);会输出那些值。
看看下面的例子:
一开始没有加上id成员变量和构造函数的,编译时提示找不到构造函数 Month(int),我当初还以为JAN(1)的1会赋值给形参ordinal呢!修正后,运行结果是
0
1
2
3
4
说明ordinal值是固定不变的。
查看汇编代码:
0: new #4; //class EnumMonth$Month
3: dup
4: ldc #8; //String JAN
6: iconst_0
7: iconst_1
8: invokespecial #9; //Method "<init>":(Ljava/lang/String;II)V
11: putstatic #10; //Field JAN:LEnumMonth$Month;
这时发现构造函数似乎增加一个形参int类型id。
我再尝试把int id改为char,运行结果是一样的:
private enum Month {
JAN('1'), SEP('9'), MAR('3'), APR('4'), MAY('5');
final char id;
Month(char id){
this.id = id;
}
}
但编译代码有所不同:
0: new #4; //class EnumMonth$Month
3: dup
4: ldc #8; //String JAN
6: iconst_0
7: bipush 49
9: invokespecial #9; //Method "<init>":(Ljava/lang/String;IC)V
12: putstatic #10; //Field JAN:LEnumMonth$Month;
所以个人认为,如果我们添加了自定义的构造函数,编译器会自动构建新的构造函数:
Enum(String name, int ordinal, char id) {
super(name, ordinal);
//copy 自定义构造函数的内容
this.id = id;
}
注意:构造器只能私有private,绝对不允许有public构造器。否则提示modifier public not allowed here。
现在了解了枚举值就是该枚举类的对象,可以传常量数值作为枚举对象的属性以及自定义构造函数,接下来看看我们平时常用的switch例子:
实际上用以下代码可以解决,每个枚举对象都包含desc属性值和getDesc()方法:
运行结果:
RED is : 红色
GREEN is : 绿色
BLUE is : 蓝色
YELLOW is : 黄色
BLACK is : 黑色
WHITE is : 白色
以上的例子提醒我们,不需要在外面的代码添加switch逻辑来判断以赋予不同的值,直接在enum里面处理就完成了。
注意:在case标签中,枚举前缀不能出现,即case Color.RED是不合法的,只能直接用枚举值RED。而在其他地方出现时则必须用Color.RED。
为什么switch可以支持enum呢?switch其实是支持int基本类型,而因为byte,short,char可以向上转换为int,所以switch也支持它们,但long因为转换int会截断便不能支持。
而enum在switch中也是int类型,看了《switch之enum》,应该这样解释:
我使用jd反编译上面那段switch代码:
??? = EnumColor.Color.RED;
switch (EnumColor.1.$SwitchMap$EnumColor$Color[???.ordinal()]) {
case 1:
System.out.println("红色");
break;
case 2:
System.out.println("绿色");
break;
case 3:
System.out.println("黑色");
break;
}
可以看到case后面的值变成int值,但这个数值并不是枚举值的ordinal。jd没有把EnumColor$1里面的代码反编译出来,但看了文章里面那段static int[] $SWITCH_TABLE$meiju$EnumTest(),原来这里面有一个int数组,按照枚举值的ordinal值作为索引值,依次给数组元素赋值从1开始:
ai[Color.RED.ordinal()] = 1;
ai[Color.GREEN.ordinal()] = 2;
...
再看看switch括号里面的就是数组[???.ordinal()],明了!
下面说一下方法枚举(多态),我一开始写的是以下代码:
结果编译不通过:
EnumGrade.java:10: 无法从静态上下文中引用非静态 变量 base
return base;
^
EnumGrade.java:16: 无法从静态上下文中引用非静态 变量 base
return base*2;
^
EnumGrade.java:22: 无法从静态上下文中引用非静态 变量 base
return base*3;
^
于是修改为以下代码:
运行结果:
另外Java1.5还有EnumMap和EnumSet(参考Java枚举类型enum使用详解):
运行结果:
ON
OFF
ON:is On
OFF:is off
private static final int COLOR_WHITE = Color.WHITE; private static final int COLOR_BLACK = Color.BLACK;
后来才知道原来这样会使得类型不安全,你必须确保是int,而且还要确保它的范围必须正确。
private void updateBackgourndColor(int color){ if (color == COLOR_WHITE || color == COLOR_BLACK) frameView.setBackgroundColor(color); } }
像以上的代码,一旦color定义的常量以后增加了,这里就需要相应地修改。而enum可以很方便地解决。
首先看看最简单的enum用法:
public class EnumMonth{ public static void main(String[] args) { for (Month month : Month.values()) { System.out.println(month); } } private enum Month { JAN, SEP, MAR, APR, MAY; } }
运行结果:
JAN SEP MAR APR MAY
使用javap EnumMonth$Month看到反编译的结果,Month会被编译成一个Java类:
Compiled from "EnumMonth.java" final class EnumMonth$Month extends java.lang.Enum{ public static final EnumMonth$Month JAN; public static final EnumMonth$Month SEP; public static final EnumMonth$Month MAR; public static final EnumMonth$Month APR; public static final EnumMonth$Month MAY; public static EnumMonth$Month[] values(); public static EnumMonth$Month valueOf(java.lang.String); static {}; }
1. 这个Month枚举类是final class,即不能被继承,而它本身是继承于Enum;
2. 这些枚举值是Month对象,而且是static final修饰的;
3. 注意values()方法,它能得到所有的枚举值,但这方法在Enum里面没有找到,我是在看到反编译结果后才确认的。
而valueOf(String)方法,返回带指定名称的指定枚举类型的枚举常量。在使用javap -c EnumMonth$Month进一步查看汇编代码后,知道原来是调用Enum.valueOf(Class<T> enumType, String name):
public static EnumMonth$Month valueOf(java.lang.String);
Code:
0: ldc_w #4; //class EnumMonth$Month
3: aload_0
4: invokestatic #5; //Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
7: checkcast #4; //class EnumMonth$Month
10: areturn
static{}里面主要是new这些枚举值对象。
0: new #4; //class EnumMonth$Month
3: dup
4: ldc #7; //String JAN
6: iconst_0
7: invokespecial #8; //Method "<init>":(Ljava/lang/String;I)V
10: putstatic #9; //Field JAN:LEnumMonth$Month;
这里要说说Enum的构造函数Enum(String name, int ordinal),形参name就是枚举值的string形式("JAN"),ordinal值则是编译器按照枚举值的顺序从0排着赋值的。要注意,这个ordinal值原来是不能改变的,即编译器是从0升序赋值的,在字节码里面固定写着iconst_0(n=0,1,2...)的。
static{
JAN = new EnumMonth$Month("JAN", 0);
SEP = new EnumMonth$Month("SEP", 1);
...
}
于是Enum里面的String name()和int ordinal()方法就是返回这两个值的。Enum.toString()也是返回name值,这就是为什么System.out.println(month);会输出那些值。
看看下面的例子:
public class EnumMonth{ public static void main(String[] args) { for (Month month : Month.values()) { System.out.println(month.ordinal()); } } private enum Month { JAN(1), SEP(9), MAR(3), APR(4), MAY(5); final int id; Month(int id){ this.id = id; } } }
一开始没有加上id成员变量和构造函数的,编译时提示找不到构造函数 Month(int),我当初还以为JAN(1)的1会赋值给形参ordinal呢!修正后,运行结果是
0
1
2
3
4
说明ordinal值是固定不变的。
查看汇编代码:
0: new #4; //class EnumMonth$Month
3: dup
4: ldc #8; //String JAN
6: iconst_0
7: iconst_1
8: invokespecial #9; //Method "<init>":(Ljava/lang/String;II)V
11: putstatic #10; //Field JAN:LEnumMonth$Month;
这时发现构造函数似乎增加一个形参int类型id。
我再尝试把int id改为char,运行结果是一样的:
private enum Month {
JAN('1'), SEP('9'), MAR('3'), APR('4'), MAY('5');
final char id;
Month(char id){
this.id = id;
}
}
但编译代码有所不同:
0: new #4; //class EnumMonth$Month
3: dup
4: ldc #8; //String JAN
6: iconst_0
7: bipush 49
9: invokespecial #9; //Method "<init>":(Ljava/lang/String;IC)V
12: putstatic #10; //Field JAN:LEnumMonth$Month;
所以个人认为,如果我们添加了自定义的构造函数,编译器会自动构建新的构造函数:
Enum(String name, int ordinal, char id) {
super(name, ordinal);
//copy 自定义构造函数的内容
this.id = id;
}
注意:构造器只能私有private,绝对不允许有public构造器。否则提示modifier public not allowed here。
现在了解了枚举值就是该枚举类的对象,可以传常量数值作为枚举对象的属性以及自定义构造函数,接下来看看我们平时常用的switch例子:
switch(color) { case RED: System.out.println("红色"); break; case GREEN: System.out.println("绿色"); break; case BLACK: System.out.println("黑色"); break; ... default: break; }
实际上用以下代码可以解决,每个枚举对象都包含desc属性值和getDesc()方法:
public class EnumColor { public static void main(String[] args) { for (Color color : Color.values()) System.out.println(color + " is : " + color.getDesc()); } private enum Color { RED("红色"), GREEN("绿色"), BLUE("蓝色"), YELLOW("黄色"), BLACK("黑色"), WHITE("白色"); private final String desc; private Color(String desc) { this.desc = desc; } public String getDesc() { return desc; } } }
运行结果:
RED is : 红色
GREEN is : 绿色
BLUE is : 蓝色
YELLOW is : 黄色
BLACK is : 黑色
WHITE is : 白色
以上的例子提醒我们,不需要在外面的代码添加switch逻辑来判断以赋予不同的值,直接在enum里面处理就完成了。
注意:在case标签中,枚举前缀不能出现,即case Color.RED是不合法的,只能直接用枚举值RED。而在其他地方出现时则必须用Color.RED。
为什么switch可以支持enum呢?switch其实是支持int基本类型,而因为byte,short,char可以向上转换为int,所以switch也支持它们,但long因为转换int会截断便不能支持。
而enum在switch中也是int类型,看了《switch之enum》,应该这样解释:
我使用jd反编译上面那段switch代码:
??? = EnumColor.Color.RED;
switch (EnumColor.1.$SwitchMap$EnumColor$Color[???.ordinal()]) {
case 1:
System.out.println("红色");
break;
case 2:
System.out.println("绿色");
break;
case 3:
System.out.println("黑色");
break;
}
可以看到case后面的值变成int值,但这个数值并不是枚举值的ordinal。jd没有把EnumColor$1里面的代码反编译出来,但看了文章里面那段static int[] $SWITCH_TABLE$meiju$EnumTest(),原来这里面有一个int数组,按照枚举值的ordinal值作为索引值,依次给数组元素赋值从1开始:
ai[Color.RED.ordinal()] = 1;
ai[Color.GREEN.ordinal()] = 2;
...
再看看switch括号里面的就是数组[???.ordinal()],明了!
下面说一下方法枚举(多态),我一开始写的是以下代码:
public class EnumGrade { public static void main(String[] args) { for (Grade grade : Grade.values()) System.out.println(grade + "'s result is : " + grade.getResult()); } private enum Grade { A(1) { public int getResult() { return base; } }, B(3) { public int getResult() { return base*2; } }, C(5) { public int getResult() { return base*3; } }; private final int base; private Grade(int base) { this.base = base; } public abstract int getResult(); } }
结果编译不通过:
EnumGrade.java:10: 无法从静态上下文中引用非静态 变量 base
return base;
^
EnumGrade.java:16: 无法从静态上下文中引用非静态 变量 base
return base*2;
^
EnumGrade.java:22: 无法从静态上下文中引用非静态 变量 base
return base*3;
^
于是修改为以下代码:
public class EnumGrade { public static void main(String[] args) { for (Grade grade : Grade.values()) System.out.println(grade + "'s result is : " + grade.getResult(grade.ordinal())); } private enum Grade { A { public int getResult(int base) { return base; } }, B { public int getResult(int base) { return base*2; } }, C { public int getResult(int base) { return base*3; } }; public abstract int getResult(int base); } }
运行结果:
A's result is : 0 B's result is : 2 C's result is : 6
另外Java1.5还有EnumMap和EnumSet(参考Java枚举类型enum使用详解):
import java.util.EnumMap; import java.util.EnumSet; public class EnumState{ public static void main(String[] args) { // EnumSet的使用 EnumSet<State> stateSet = EnumSet.allOf(State.class); for (State s : stateSet) { System.out.println(s); } // EnumMap的使用 EnumMap<State,String> stateMap = new EnumMap<State,String>(State.class); stateMap.put(State.ON, "is On"); stateMap.put(State.OFF, "is off"); for (State s : State.values()) { System.out.println(s.name() + ":" + stateMap.get(s)); } } private enum State { ON, OFF }; }
运行结果:
ON
OFF
ON:is On
OFF:is off
发表评论
-
日常积累
2013-09-18 16:41 408eclispe的tomcat设置 -Xms128m -X ... -
对象修改日志
2012-05-11 11:15 2127转自:http://hi.baidu.com/41 ... -
spring bean 获取 ServletContext
2012-04-12 10:23 1218我的需求是获取spring开发的web项目在服务器上的绝对路径 ... -
jdk1.6生成webservice的 p命令
2012-03-12 09:26 641wsimport -keep -p service.clien ... -
axis2 axis2-1.6.1 客户端 adb方式调用下最少jar包
2011-12-23 16:55 1036太痛恨自己了,maven没有学好就是这样,不然的话,很快搞定. ... -
银行货币正则表达式
2011-11-24 16:49 1679/** * * @Title: isBankF ... -
去掉.svn文件
2011-10-11 16:41 795双击这个reg文件。成功后,在每一个文件夹上点击右键都会 ... -
JAVA发送HTTP请求,返回HTTP响应内容,接口调用用
2011-07-08 15:06 2190本文转自:http://yymmiinngg.iteye.co ... -
Visual Swing 安装到myeclipse中
2011-05-27 19:57 1865在eclipse 目录下建立两个文件夹,一个MyPlugins ...
相关推荐
java1.5之后的新特性: 1,枚举(enum) 作用:一般用于代表一组相同类型的常用常量。 原理:语法结构与java类的语法不一样,但是经过编译器编译之后产生的是一个class文件。该class文件经过反编译之后实际上是...
Enum是Sun全新引进的一个关键字,看起来很象是特殊的class,它也可以有自己的变量,可以定义自己的方法,可以实现一个或者多个接口。在声明一个enum类型时,应该注意到enum类型有如下的一些特征:
enum的全称为enumeration, 是 JDK 1.5中引入的新特性,存放在 java.lang包中。下面这篇文章是我在使用enum过程中的一些经验和总结,分享出来方便大家快速的掌握Java中枚举(enum),有需要的朋友们下面跟着小编来一起...
enum 的全称为 enumeration, 是 JDK 1.5 中引入的新特性,存放在 java.lang 包中,在上面的这种情况下,enum能派上用场了。枚举类型的用途不仅如此,具体可用场景可看下面的介绍 1、常量 以前我们定义一...
31.8 静态导入(1.5新特性) 46 32 单例模式 47 32.1 饿汉模式 47 32.2 懒汉模式 47 33 接口(interface) 48 33.1 如何创建一个接口。 48 33.2 如何使用接口 48 33.3 如何使用类实现一个接口 49 33.4 接口的细节 49...
11.2.8枚举(Enum)使用示例355 11.2.9枚举集(EnumSet)使用示例358 11.3常用算法361 11.3.1Collections中的简单算法361 11.3.2排序362 11.3.3二分查找364 11.4遗留的类和接口366 11.4.1Enumeration接口简介...
本书作者根据自己学习C++的亲身体会及多年教学经验,用简单的例子和简练的叙述讲解C++编程,别具特色。 全书共分十八章,内容涉及对象的演化、数据抽象、隐藏实现、初始化与清除、函数重载与缺省参数、输入输出流...
本书作者根据自己学习C++的亲身体会及多年教学经验,用简单的例子和简练的叙述讲解C++编程,别具特色。 全书共分十八章,内容涉及对象的演化、数据抽象、隐藏实现、初始化与清除、函数重载与缺省参数、输入输出流...
1.5.5. MaxDB和MySQL之间的特性差异 1.5.6. MaxDB和MySQL之间的协同性 1.5.7. 与MaxDB有关的链接 1.6. MySQL发展大事记 1.6.1. MySQL 5.1的新特性 1.7. MySQL信息源 1.7.1. MySQL邮件列表 1.7.2. IRC(在线聊天系统...
1.5.5. MaxDB和MySQL之间的特性差异 1.5.6. MaxDB和MySQL之间的协同性 1.5.7. 与MaxDB有关的链接 1.6. MySQL发展大事记 1.6.1. MySQL 5.1的新特性 1.7. MySQL信息源 1.7.1. MySQL邮件列表 1.7.2. IRC(在线聊天系统...
1.5.5. MaxDB和MySQL之间的特性差异 1.5.6. MaxDB和MySQL之间的协同性 1.5.7. 与MaxDB有关的链接 1.6. MySQL发展大事记 1.6.1. MySQL 5.1的新特性 1.7. MySQL信息源 1.7.1. MySQL邮件列表 1.7.2. IRC(在线聊天系统...
1.5.5. MaxDB和MySQL之间的特性差异 1.5.6. MaxDB和MySQL之间的协同性 1.5.7. 与MaxDB有关的链接 1.6. MySQL发展大事记 1.6.1. MySQL 5.1的新特性 1.7. MySQL信息源 1.7.1. MySQL邮件列表 1.7.2. IRC(在线...
1.5.5. MaxDB和MySQL之间的特性差异 1.5.6. MaxDB和MySQL之间的协同性 1.5.7. 与MaxDB有关的链接 1.6. MySQL发展大事记 1.6.1. MySQL 5.1的新特性 1.7. MySQL信息源 1.7.1. MySQL邮件列表 1.7.2. IRC(在线聊天...
1.5.5. MaxDB和MySQL之间的特性差异 1.5.6. MaxDB和MySQL之间的协同性 1.5.7. 与MaxDB有关的链接 1.6. MySQL发展大事记 1.6.1. MySQL 5.1的新特性 1.7. MySQL信息源 1.7.1. MySQL邮件列表 1.7.2. IRC(在线聊天系统...
本书作者根据自己学习C++的亲身体会及多年教学经验,用简单的例子和简练的叙述讲解C++编程,别具特色。 全书共分十八章,内容涉及对象的演化、数据抽象、隐藏实现、初始化与清除、函数重载与缺省参数、输入输出流...
MaxDB和MySQL之间的特性差异 1.5.6. MaxDB和MySQL之间的协同性 1.5.7. 与MaxDB有关的链接 1.6. MySQL发展大事记 1.6.1. MySQL 5.1的新特性 1.7. MySQL信息源 1.7.1. MySQL邮件列表 1.7.2. IRC...
1.5.5. MaxDB和MySQL之间的特性差异 1.5.6. MaxDB和MySQL之间的协同性 1.5.7. 与MaxDB有关的链接 1.6. MySQL发展大事记 1.6.1. MySQL 5.1的新特性 1.7. MySQL信息源 1.7.1. MySQL邮件列表 1.7.2. IRC(在线聊天系统...
目录 前言 1. 一般信息 1.1. 关于本手册 ...1.5.5. MaxDB和MySQL之间的特性差异 1.5.6. MaxDB和MySQL之间的协同性 1.5.7. 与MaxDB有关的链接 1.6. MySQL发展大事记 1.6.1. MySQL 5.1的新特性 1.7. MySQL信息源 ...