- 浏览: 58121 次
- 性别:
- 来自: 武汉
文章分类
最新评论
很多时候我们定义了一组值来表示用于特定的数值,往往都是习惯性地使用常量:
后来才知道原来这样会使得类型不安全,你必须确保是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 436eclispe的tomcat设置 -Xms128m -X ... -
对象修改日志
2012-05-11 11:15 2159转自:http://hi.baidu.com/41 ... -
spring bean 获取 ServletContext
2012-04-12 10:23 1249我的需求是获取spring开发的web项目在服务器上的绝对路径 ... -
jdk1.6生成webservice的 p命令
2012-03-12 09:26 672wsimport -keep -p service.clien ... -
axis2 axis2-1.6.1 客户端 adb方式调用下最少jar包
2011-12-23 16:55 1064太痛恨自己了,maven没有学好就是这样,不然的话,很快搞定. ... -
银行货币正则表达式
2011-11-24 16:49 1702/** * * @Title: isBankF ... -
去掉.svn文件
2011-10-11 16:41 829双击这个reg文件。成功后,在每一个文件夹上点击右键都会 ... -
JAVA发送HTTP请求,返回HTTP响应内容,接口调用用
2011-07-08 15:06 2220本文转自:http://yymmiinngg.iteye.co ... -
Visual Swing 安装到myeclipse中
2011-05-27 19:57 1889在eclipse 目录下建立两个文件夹,一个MyPlugins ...
相关推荐
JAVA 1.5的新特性是Java发展史上的一个重要里程碑,这一版本引入了一系列创新特性,旨在提升编程效率、增强代码安全性并简化开发流程。以下是对JAVA 1.5新特性的详细解析: ### 1. 泛型 (Generics) #### 概述 泛型...
java1.5之后的新特性: 1,枚举(enum) 作用:一般用于代表一组相同类型的常用常量。 原理:语法结构与java类的语法不一样,但是经过编译器编译之后产生的是一个class文件。该class文件经过反编译之后实际上是...
在 Java 1.5 中,枚举的实现优化了内存使用和访问速度,确保了在运行时的高效性能。 Java 之前的“准枚举”类型,如使用 `public static final` 常量,虽然可以达到类似的效果,但存在一些不足,例如无法实现枚举的...
### JDK 1.5 新特性详解 ...综上所述,Java 1.5 的新特性极大地提升了编程效率和代码质量,为开发者带来了更多的便利。通过使用这些新特性,不仅可以减少代码量,还能提高代码的安全性和可维护性。
Java 1.5 引入的新特性之一就是枚举(Enum)类型,这是对之前版本的一个重大改进,尤其是在从 C++ 等语言过渡到 Java 的开发者中受到了广泛关注。在 Java 1.5 之前,Java 缺乏对枚举类型的原生支持,导致了一些不便...
JDK 1.5的发布标志着Java平台的一次重大进步,通过引入一系列新特性,不仅增强了语言本身的表达能力,还提高了开发者的生产力。泛型、枚举、注解等特性至今仍是Java开发中不可或缺的重要组成部分。此外,JDK 1.5还对...
2. **泛型**:泛型是1.5最重要的新特性之一,它允许在类、接口和方法中使用类型参数,增强了类型安全性和代码重用性。泛型可以限制集合元素的类型,避免了类型转换错误。 3. **枚举类(enum)**:之前的Java中,...
Java 1.5,也被称为Java SE 5.0,是Java发展历程中的一个重要版本,它引入了许多新特性和改进,极大地提升了编程效率和代码的可读性。以下是对这些新特性的详细解释: 1. **静态导入(Static Import)** 静态导入...
Java 1.5 是一个重要的版本更新,引入了许多新特性,极大地提升了开发效率和代码质量。以下是对这些新特性的详细解释: 1. **自动装箱与拆箱**: 自动装箱是指将基本数据类型(如 int、char 等)自动转换为对应的...
Java API 1.5,也被称为Java SE 5.0,引入了许多重要的新特性和改进。首先,泛型的引入是Java编程的一个重大革新,它允许在编译时检查类型安全,减少了运行时的类型转换异常。泛型在集合框架中得到了广泛的应用,如...
这些新特性和改进极大地增强了Java编程语言的功能和灵活性,使得Java 1.5成为了一个广泛采用的版本。`JAVA_API1.5.chm`文件包含了关于这个版本的完整API文档,是学习和查阅Java 1.5 API的宝贵资源。通过深入理解这些...
总的来说,JDK 1.5(Java 5.0)的新特性极大地提高了Java的生产力和代码质量。从泛型到增强的for循环,再到注解和枚举,这些改进都让Java开发者能够编写出更安全、更易于维护的代码。在实际开发中,理解并充分利用...
【JDK 1.5新特性详解】 JDK 1.5是Java发展历程中的一个重要里程碑,引入了许多创新特性,极大地提升了开发效率和代码质量...JDK 1.5的这些更新奠定了后续版本许多新特性的基础,对于理解和使用现代Java编程至关重要。
通过这些新特性,JDK 1.5 提升了 Java 语言的现代性和实用性,使开发者能够编写更安全、更易读、更高效的代码。从泛型到增强的 for 循环,每一个改变都显著提高了开发效率和代码质量。在实际项目中,理解和熟练运用...
Java API 1.5,也称为Java SE 5.0,是Java开发的关键版本,引入了许多新特性,改进了语言的效率和可读性,并扩展了标准库。这个中文文档对于学习和理解Java编程语言至关重要,它包含了详细的类库、接口、方法和其他...
JDK1.5,开发代号“猛虎”,是Java发展历程中的...这些新特性显著提升了Java编程的效率和质量,降低了出错的可能性,是Java开发者不可或缺的工具。了解并熟练运用这些特性,能帮助我们编写出更加健壮、易于维护的代码。
Java 1.5 API,全称为Java Application Programming Interface,是Java编程语言的核心组成部分,它包含了...下载的中文版API文档对于学习和查阅这些特性非常有帮助,能够辅助开发者更好地理解和使用Java 1.5的特性。